From 8b56ad0b2a875103d0f3ec9ab741fe8b09b7d9c2 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sat, 23 Feb 2019 20:24:25 -0500 Subject: [PATCH 0001/2437] Separate buy request creation into extensible builder This allows product type graphql modules a method for managing how inputs are mapped when buy requests are created. --- .../Model/Cart/AddSimpleProductToCart.php | 71 +++---------------- .../Cart/BuyRequest/BuyRequestBuilder.php | 42 +++++++++++ .../BuyRequestDataProviderInterface.php | 13 ++++ .../CustomizableOptionsDataProvider.php | 42 +++++++++++ .../Cart/BuyRequest/DefaultDataProvider.php | 41 +++++++++++ .../Magento/QuoteGraphQl/etc/graphql/di.xml | 8 +++ 6 files changed, 156 insertions(+), 61 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 1b32866ed883c..4f0d5b3637c01 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -15,6 +15,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Stdlib\ArrayManager; use Magento\Quote\Model\Quote; +use Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestBuilder; /** * Add simple product to cart @@ -29,28 +30,28 @@ class AddSimpleProductToCart private $arrayManager; /** - * @var DataObjectFactory + * @var ProductRepositoryInterface */ - private $dataObjectFactory; + private $productRepository; /** - * @var ProductRepositoryInterface + * @var BuyRequestBuilder */ - private $productRepository; + private $buyRequestBuilder; /** * @param ArrayManager $arrayManager - * @param DataObjectFactory $dataObjectFactory * @param ProductRepositoryInterface $productRepository + * @param BuyRequestBuilder $buyRequestBuilder */ public function __construct( ArrayManager $arrayManager, - DataObjectFactory $dataObjectFactory, - ProductRepositoryInterface $productRepository + ProductRepositoryInterface $productRepository, + BuyRequestBuilder $buyRequestBuilder ) { $this->arrayManager = $arrayManager; - $this->dataObjectFactory = $dataObjectFactory; $this->productRepository = $productRepository; + $this->buyRequestBuilder = $buyRequestBuilder; } /** @@ -66,8 +67,6 @@ public function __construct( public function execute(Quote $cart, array $cartItemData): void { $sku = $this->extractSku($cartItemData); - $qty = $this->extractQty($cartItemData); - $customizableOptions = $this->extractCustomizableOptions($cartItemData); try { $product = $this->productRepository->get($sku); @@ -76,7 +75,7 @@ public function execute(Quote $cart, array $cartItemData): void } try { - $result = $cart->addProduct($product, $this->createBuyRequest($qty, $customizableOptions)); + $result = $cart->addProduct($product, $this->buyRequestBuilder->build($cartItemData)); } catch (\Exception $e) { throw new GraphQlInputException( __( @@ -106,54 +105,4 @@ private function extractSku(array $cartItemData): string } return (string)$sku; } - - /** - * Extract Qty from cart item data - * - * @param array $cartItemData - * @return float - * @throws GraphQlInputException - */ - private function extractQty(array $cartItemData): float - { - $qty = $this->arrayManager->get('data/qty', $cartItemData); - if (!isset($qty)) { - throw new GraphQlInputException(__('Missing key "qty" in cart item data')); - } - return (float)$qty; - } - - /** - * Extract Customizable Options from cart item data - * - * @param array $cartItemData - * @return array - */ - private function extractCustomizableOptions(array $cartItemData): array - { - $customizableOptions = $this->arrayManager->get('customizable_options', $cartItemData, []); - - $customizableOptionsData = []; - foreach ($customizableOptions as $customizableOption) { - $customizableOptionsData[$customizableOption['id']] = $customizableOption['value']; - } - return $customizableOptionsData; - } - - /** - * Format GraphQl input data to a shape that buy request has - * - * @param float $qty - * @param array $customOptions - * @return DataObject - */ - private function createBuyRequest(float $qty, array $customOptions): DataObject - { - return $this->dataObjectFactory->create([ - 'data' => [ - 'qty' => $qty, - 'options' => $customOptions, - ], - ]); - } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php new file mode 100644 index 0000000000000..492dd18f14e03 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php @@ -0,0 +1,42 @@ +dataObjectFactory = $dataObjectFactory; + $this->providers = $providers; + } + + public function build(array $cartItemData): DataObject + { + $requestData = []; + foreach ($this->providers as $provider) { + $requestData = array_merge($requestData, $provider->execute($cartItemData)); + } + + return $this->dataObjectFactory->create(['data' => $requestData]); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php new file mode 100644 index 0000000000000..adfc5b13b762c --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php @@ -0,0 +1,13 @@ +arrayManager = $arrayManager; + } + + /** + * @inheritdoc + */ + public function execute(array $cartItemData): array + { + $customizableOptions = $this->arrayManager->get('customizable_options', $cartItemData, []); + + $customizableOptionsData = []; + foreach ($customizableOptions as $customizableOption) { + $customizableOptionsData[$customizableOption['id']] = $customizableOption['value']; + } + + return ['options' => $customizableOptionsData]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php new file mode 100644 index 0000000000000..3185510e42865 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php @@ -0,0 +1,41 @@ +arrayManager = $arrayManager; + } + + /** + * @inheritdoc + */ + public function execute(array $cartItemData): array + { + $qty = $this->arrayManager->get('data/qty', $cartItemData); + if (!isset($qty)) { + throw new GraphQlInputException(__('Missing key "qty" in cart item data')); + } + + return ['qty' => (float)$qty]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml index 86bc954ae4ac4..b51d33b6577c8 100644 --- a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -8,4 +8,12 @@ + + + + Magento\QuoteGraphQl\Model\Cart\BuyRequest\DefaultDataProvider + Magento\QuoteGraphQl\Model\Cart\BuyRequest\CustomizableOptionsDataProvider + + + From 77b636ec3c08a77be2b0bc37f29d942ecbb5b20d Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Sun, 24 Feb 2019 10:20:36 -0500 Subject: [PATCH 0002/2437] Mutation for adding bundled products to cart Partial resolution for magento/graphlql-ce#143 --- .../Model/Cart/BundleOptionDataProvider.php | 136 ++++++++++++ .../Cart/BuyRequest/BundleDataProvider.php | 43 ++++ .../Model/Resolver/BundleOption.php | 39 ++++ app/code/Magento/BundleGraphQl/composer.json | 2 + app/code/Magento/BundleGraphQl/etc/di.xml | 7 + .../Magento/BundleGraphQl/etc/graphql/di.xml | 7 + .../Magento/BundleGraphQl/etc/schema.graphqls | 44 ++++ .../Bundle/AddBundleProductToCartTest.php | 193 ++++++++++++++++++ 8 files changed, 471 insertions(+) create mode 100644 app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php create mode 100644 app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php create mode 100644 app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php new file mode 100644 index 0000000000000..8db1c64c23c42 --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php @@ -0,0 +1,136 @@ +pricingHelper = $pricingHelper; + $this->serializer = $serializer; + $this->configuration = $configuration; + } + + /** + * @param Item $item + * @return array + */ + public function getData(Item $item): array + { + $options = []; + $product = $item->getProduct(); + + /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + $typeInstance = $product->getTypeInstance(); + + $optionsQuoteItemOption = $item->getOptionByCode('bundle_option_ids'); + $bundleOptionsIds = $optionsQuoteItemOption + ? $this->serializer->unserialize($optionsQuoteItemOption->getValue()) + : []; + + if ($bundleOptionsIds) { + /** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */ + $optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $product); + + $selectionsQuoteItemOption = $item->getOptionByCode('bundle_selection_ids'); + + $bundleSelectionIds = $this->serializer->unserialize($selectionsQuoteItemOption->getValue()); + + if (!empty($bundleSelectionIds)) { + $selectionsCollection = $typeInstance->getSelectionsByIds($bundleSelectionIds, $product); + $bundleOptions = $optionsCollection->appendSelections($selectionsCollection, true); + + $options = $this->buildBundleOptions($bundleOptions, $item); + } + } + + return $options; + } + + /** + * @param \Magento\Bundle\Model\Option[] $bundleOptions + * @param Item $item + * @return array + */ + private function buildBundleOptions(array $bundleOptions, Item $item): array + { + $options = []; + foreach ($bundleOptions as $bundleOption) { + if (!$bundleOption->getSelections()) { + continue; + } + + $options[] = [ + 'id' => $bundleOption->getId(), + 'label' => $bundleOption->getTitle(), + 'type' => $bundleOption->getType(), + 'values' => $this->buildBundleOptionValues($bundleOption->getSelections(), $item), + ]; + } + + return $options; + } + + /** + * @param Product[] $selections + * @param Item $item + * @return array + */ + private function buildBundleOptionValues(array $selections, Item $item): array + { + $values = []; + + $product = $item->getProduct(); + foreach ($selections as $selection) { + $qty = (float) $this->configuration->getSelectionQty($product, $selection->getSelectionId()); + if (!$qty) { + continue; + } + + $selectionPrice = $this->configuration->getSelectionFinalPrice($item, $selection); + + $values[] = [ + 'id' => $selection->getSelectionId(), + 'label' => $selection->getName(), + 'quantity' => $qty, + 'price' => $this->pricingHelper->currency($selectionPrice, false, false), + ]; + } + + return $values; + } +} diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php new file mode 100644 index 0000000000000..72a72dd5b3bcf --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php @@ -0,0 +1,43 @@ +arrayManager = $arrayManager; + } + + /** + * @inheritdoc + */ + public function execute(array $cartItemData): array + { + $bundleOptions = []; + $bundleInputs = $this->arrayManager->get('bundle_options', $cartItemData) ?? []; + foreach ($bundleInputs as $bundleInput) { + $bundleOptions['bundle_option'][$bundleInput['id']] = $bundleInput['value']; + $bundleOptions['bundle_option_qty'][$bundleInput['id']] = $bundleInput['quantity']; + } + + return $bundleOptions; + } +} diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php new file mode 100644 index 0000000000000..9bccbf936f18d --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php @@ -0,0 +1,39 @@ +dataProvider = $bundleOptionDataProvider; + } + + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new GraphQlInputException(__('Value must contain "model" property.')); + } + return $this->dataProvider->getData($value['model']); + } +} diff --git a/app/code/Magento/BundleGraphQl/composer.json b/app/code/Magento/BundleGraphQl/composer.json index aea55cb5c644c..5e0adf0ceb169 100644 --- a/app/code/Magento/BundleGraphQl/composer.json +++ b/app/code/Magento/BundleGraphQl/composer.json @@ -7,6 +7,8 @@ "magento/module-catalog": "*", "magento/module-bundle": "*", "magento/module-catalog-graph-ql": "*", + "magento/module-quote": "*", + "magento/module-quote-graph-ql": "*", "magento/module-store": "*", "magento/framework": "*" }, diff --git a/app/code/Magento/BundleGraphQl/etc/di.xml b/app/code/Magento/BundleGraphQl/etc/di.xml index 4f41f3cb8dc80..15acad7c6bf06 100644 --- a/app/code/Magento/BundleGraphQl/etc/di.xml +++ b/app/code/Magento/BundleGraphQl/etc/di.xml @@ -16,4 +16,11 @@ + + + + BundleCartItem + + + diff --git a/app/code/Magento/BundleGraphQl/etc/graphql/di.xml b/app/code/Magento/BundleGraphQl/etc/graphql/di.xml index 50a2e32b8c9d5..5484d57b7c741 100644 --- a/app/code/Magento/BundleGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/BundleGraphQl/etc/graphql/di.xml @@ -13,6 +13,13 @@ + + + + Magento\BundleGraphQl\Model\Cart\BuyRequest\BundleDataProvider + + + diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index edde6079dfb2f..3848cabff22e5 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -1,6 +1,50 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. +type Mutation { + addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") +} + +input AddBundleProductsToCartInput { + cart_id: String! + cartItems: [BundleProductCartItemInput!]! +} + +input BundleProductCartItemInput { + data: CartItemInput! + bundle_options:[BundleOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input BundleOptionInput { + id: Int! + quantity: Float! + value: [String!]! +} + +type AddBundleProductsToCartOutput { + cart: Cart! +} + +type BundleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") + bundle_options: [SelectedBundleOption!]! @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleOption") +} + +type SelectedBundleOption { + id: Int! + label: String! + type: String! + values: [SelectedBundleOptionValue!]! +} + +type SelectedBundleOptionValue { + id: Int! + label: String! + quantity: Float! + price: Float! +} + type BundleItem @doc(description: "BundleItem defines an individual item in a bundle product") { option_id: Int @doc(description: "An ID assigned to each type of item in a bundle product") title: String @doc(description: "The display name of the item") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php new file mode 100644 index 0000000000000..7905484ec51df --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -0,0 +1,193 @@ +quoteResource = $objectManager->get(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddBundleProductToCart() + { + $sku = 'bundle-product'; + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $product = $this->productRepository->get($sku); + + /** @var $typeInstance \Magento\Bundle\Model\Product\Type */ + $typeInstance = $product->getTypeInstance(); + $typeInstance->setStoreFilter($product->getStoreId(), $product); + /** @var $option \Magento\Bundle\Model\Option */ + $option = $typeInstance->getOptionsCollection($product)->getFirstItem(); + /** @var \Magento\Catalog\Model\Product $selection */ + $selection = $typeInstance->getSelectionsCollection([$option->getId()], $product)->getFirstItem(); + $optionId = $option->getId(); + $selectionId = $selection->getSelectionId(); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<graphQlQuery($query); + + $this->assertArrayHasKey('addBundleProductsToCart', $response); + $this->assertArrayHasKey('cart', $response['addBundleProductsToCart']); + $cart = $response['addBundleProductsToCart']['cart']; + $this->assertEquals($maskedQuoteId, $cart['cart_id']); + $bundleItem = current($cart['items']); + $this->assertEquals($sku, $bundleItem['product']['sku']); + $bundleItemOption = current($bundleItem['bundle_options']); + $this->assertEquals($optionId, $bundleItemOption['id']); + $this->assertEquals($option->getTitle(), $bundleItemOption['label']); + $this->assertEquals($option->getType(), $bundleItemOption['type']); + $value = current($bundleItemOption['values']); + $this->assertEquals($selection->getSelectionId(), $value['id']); + $this->assertEquals((float) $selection->getSelectionPriceValue(), $value['price']); + $this->assertEquals(1, $value['quantity']); + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @expectedException \Exception + * @expectedExceptionMessage Please select all required options + */ + public function testAddBundleToCartWithoutOptions() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<graphQlQuery($query); + } +} From 55ea3fc961152dbdc00a8c20b37720da59f3169e Mon Sep 17 00:00:00 2001 From: Sascha Grossenbacher Date: Tue, 26 Mar 2019 17:48:10 +0100 Subject: [PATCH 0003/2437] Use new MessageQueueConfig interface, update unit tests --- .../MysqlMq/Model/Driver/Bulk/Exchange.php | 16 +++++++- .../Magento/MysqlMq/Model/Driver/Exchange.php | 16 +++++++- app/code/Magento/MysqlMq/Setup/Recurring.php | 7 ++-- .../Unit/Model/Driver/Bulk/ExchangeTest.php | 38 +++++++++++++++++-- .../MysqlMq/Test/Unit/Setup/RecurringTest.php | 31 ++++++--------- 5 files changed, 76 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php b/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php index 247a44667be06..73c6a89ef0d63 100644 --- a/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php +++ b/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php @@ -6,7 +6,7 @@ namespace Magento\MysqlMq\Model\Driver\Bulk; use Magento\Framework\MessageQueue\Bulk\ExchangeInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; use Magento\MysqlMq\Model\QueueManagement; /** @@ -41,7 +41,19 @@ public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagem */ public function enqueue($topic, array $envelopes) { - $queueNames = $this->messageQueueConfig->getQueuesByTopic($topic); + $queueNames = []; + $exchanges = $this->messageQueueConfig->getExchanges(); + foreach ($exchanges as $exchange) { + // @todo Is there a more reliable way to identify MySQL exchanges? + if ($exchange->getConnection() == 'db') { + foreach ($exchange->getBindings() as $binding) { + // This only supports exact matching of topics. + if ($binding->getTopic() == $topic) { + $queueNames[] = $binding->getDestination(); + } + } + } + } $messages = array_map( function ($envelope) { return $envelope->getBody(); diff --git a/app/code/Magento/MysqlMq/Model/Driver/Exchange.php b/app/code/Magento/MysqlMq/Model/Driver/Exchange.php index b6050c6b3d0b6..85e53c847f87b 100644 --- a/app/code/Magento/MysqlMq/Model/Driver/Exchange.php +++ b/app/code/Magento/MysqlMq/Model/Driver/Exchange.php @@ -7,7 +7,7 @@ use Magento\Framework\MessageQueue\EnvelopeInterface; use Magento\Framework\MessageQueue\ExchangeInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; use Magento\MysqlMq\Model\QueueManagement; class Exchange implements ExchangeInterface @@ -43,7 +43,19 @@ public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagem */ public function enqueue($topic, EnvelopeInterface $envelope) { - $queueNames = $this->messageQueueConfig->getQueuesByTopic($topic); + $queueNames = []; + $exchanges = $this->messageQueueConfig->getExchanges(); + foreach ($exchanges as $exchange) { + // @todo Is there a more reliable way to identify MySQL exchanges? + if ($exchange->getConnection() == 'db') { + foreach ($exchange->getBindings() as $binding) { + // This only supports exact matching of topics. + if ($binding->getTopic() == $topic) { + $queueNames[] = $binding->getDestination(); + } + } + } + } $this->queueManagement->addMessageToQueues($topic, $envelope->getBody(), $queueNames); return null; } diff --git a/app/code/Magento/MysqlMq/Setup/Recurring.php b/app/code/Magento/MysqlMq/Setup/Recurring.php index db3a39bf5fbd0..f6f21ae4da329 100644 --- a/app/code/Magento/MysqlMq/Setup/Recurring.php +++ b/app/code/Magento/MysqlMq/Setup/Recurring.php @@ -8,7 +8,7 @@ use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; /** * Class Recurring @@ -35,10 +35,9 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con { $setup->startSetup(); - $binds = $this->messageQueueConfig->getBinds(); $queues = []; - foreach ($binds as $bind) { - $queues[] = $bind[MessageQueueConfig::BIND_QUEUE]; + foreach ($this->messageQueueConfig->getQueues() as $queue) { + $queues[] = $queue->getName(); } $connection = $setup->getConnection(); $existingQueues = $connection->fetchCol($connection->select()->from($setup->getTable('queue'), 'name')); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php b/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php index 452825058c9d8..b7eba352ed253 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php @@ -12,7 +12,7 @@ class ExchangeTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Framework\MessageQueue\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\MessageQueue\Topology\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ private $messageQueueConfig; @@ -33,7 +33,7 @@ class ExchangeTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConfigInterface::class) + $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\Topology\ConfigInterface::class) ->disableOriginalConstructor()->getMock(); $this->queueManagement = $this->getMockBuilder(\Magento\MysqlMq\Model\QueueManagement::class) ->disableOriginalConstructor()->getMock(); @@ -56,10 +56,40 @@ protected function setUp() public function testEnqueue() { $topicName = 'topic.name'; - $queueNames = ['queue0', 'queue1']; + $queueNames = ['queue0']; + + $binding1 = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface::class); + $binding1->expects($this->once()) + ->method('getTopic') + ->willReturn($topicName); + $binding1->expects($this->once()) + ->method('getDestination') + ->willReturn($queueNames[0]); + + $binding2 = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface::class); + $binding2->expects($this->once()) + ->method('getTopic') + ->willReturn('different.topic'); + $binding2->expects($this->never()) + ->method('getDestination'); + + $exchange1 = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface::class); + $exchange1->expects($this->once()) + ->method('getConnection') + ->willReturn('db'); + $exchange1->expects($this->once()) + ->method('getBindings') + ->willReturn([$binding1, $binding2]); + $exchange2 = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface::class); + $exchange2->expects($this->once()) + ->method('getConnection') + ->willReturn('amqp'); + $exchange2->expects($this->never()) + ->method('getBindings'); + $envelopeBody = 'serializedMessage'; $this->messageQueueConfig->expects($this->once()) - ->method('getQueuesByTopic')->with($topicName)->willReturn($queueNames); + ->method('getExchanges')->willReturn([$exchange1, $exchange2]); $envelope = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class) ->disableOriginalConstructor()->getMock(); $envelope->expects($this->once())->method('getBody')->willReturn($envelopeBody); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php b/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php index e2e7ad3c4c92d..03ec3c82c2d14 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php @@ -24,7 +24,7 @@ class RecurringTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var \Magento\Framework\MessageQueue\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\MessageQueue\Topology\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ private $messageQueueConfig; @@ -34,7 +34,7 @@ class RecurringTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConfigInterface::class) + $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\Topology\ConfigInterface::class) ->getMockForAbstractClass(); $this->model = $this->objectManager->getObject( \Magento\MysqlMq\Setup\Recurring::class, @@ -49,23 +49,14 @@ protected function setUp() */ public function testInstall() { - $binds = [ - 'first_bind' => [ - 'queue' => 'queue_name_1', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.1' - ], - 'second_bind' => [ - 'queue' => 'queue_name_2', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.2' - ], - 'third_bind' => [ - 'queue' => 'queue_name_3', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.3' - ] - ]; + for ($i = 1; $i <=3; $i++) { + $queue = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\QueueConfigItemInterface::class); + $queue->expects($this->once()) + ->method('getName') + ->willReturn('queue_name_'. $i); + $queues[] = $queue; + } + $dbQueues = [ 'queue_name_1', 'queue_name_2', @@ -81,7 +72,7 @@ public function testInstall() ->getMockForAbstractClass(); $setup->expects($this->once())->method('startSetup')->willReturnSelf(); - $this->messageQueueConfig->expects($this->once())->method('getBinds')->willReturn($binds); + $this->messageQueueConfig->expects($this->once())->method('getQueues')->willReturn($queues); $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) ->getMockForAbstractClass(); $setup->expects($this->once())->method('getConnection')->willReturn($connection); From 8403ff643fadd7338ff9273b11c17278f7d70450 Mon Sep 17 00:00:00 2001 From: eduard13 Date: Wed, 3 Apr 2019 16:05:19 +0300 Subject: [PATCH 0004/2437] Convert ShoppingCartPagerTest to MFTF and marking the variations as migrated --- .../AssertCartPagerTextActionGroup.xml | 24 ++++ .../Manage20ProductsActionGroup.xml | 134 ++++++++++++++++++ ...ShoppingCartWithMoreThan20ProductsTest.xml | 53 +++++++ ...ingPagerShoppingCartWith20ProductsTest.xml | 37 +++++ ...PagerForOneItemPerPageAnd2ProductsTest.xml | 46 ++++++ .../Test/TestCase/ShoppingCartPagerTest.xml | 4 + 6 files changed, 298 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml new file mode 100644 index 0000000000000..0236f213435b1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml new file mode 100644 index 0000000000000..132c46e8ba612 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml new file mode 100644 index 0000000000000..89d6d654f1444 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + <description value="Test if the cart pager is visible with more than 20 cart items and the pager disappears if an item is removed from cart."/> + <severity value="MAJOR"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Set the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + + <!--Create and add to cart 20 products--> + <actionGroup ref="CreateAndAddToCart20ProductsActionGroup" stepKey="CartItems" /> + + <!-- Create and Add the 21th product to the shopping cart --> + <createData entity="SimpleTwo" stepKey="simpleProduct21CartItems"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem21"> + <argument name="product" value="$$simpleProduct21CartItems$$"/> + </actionGroup> + </before> + <!-- Go to the shopping cart and check if the pager is visible--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> + <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerText"> + <argument name="text" value="Items 1 to 20 of 21 total"/> + </actionGroup> + + <!-- Remove a product from cart and check if the pager is not visible--> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCheckoutCart"/> + <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText2" > + <argument name="text" value="Items 1 to 20"/> + </actionGroup> + <after> + <!--Set back the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + + <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> + <deleteData createDataKey="simpleProduct21CartItems" stepKey="deleteCartItem21"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml new file mode 100644 index 0000000000000..e9f83bb9cc167 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMissingPagerShoppingCartWith20ProductsTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check if the cart pager is missing with 20 cart items"/> + <title value="Test if the cart pager is missing with 20 cart items."/> + <description value="Test if the cart pager is missing with 20 cart items."/> + <severity value="MAJOR"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Set the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + <!--Create and add to cart 20 products--> + <actionGroup ref="CreateAndAddToCart20ProductsActionGroup" stepKey="CartItems" /> + </before> + <!-- Go to the shopping cart and check if the pager is missing--> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText" > + <argument name="text" value="Items 1 to 20"/> + </actionGroup> + <after> + <!--Delete the test products--> + <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> + </after> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml new file mode 100644 index 0000000000000..4e3bd22ccde51 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check if the cart pager is visible with 2 cart items and one item per page"/> + <title value="Test if the cart pager is visible with 2 cart items and one item per page."/> + <description value="Test if the cart pager is visible with 2 cart items and one item per page."/> + <severity value="MAJOR"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Changing the number of items to display in cart--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 1" /> + + <createData entity="SimpleTwo" stepKey="createSimpleProduct1"/> + <createData entity="SimpleTwo" stepKey="createSimpleProduct2"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + </before> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> + <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> + <argument name="text" value="Items 1 to 1 of 2 total"/> + </actionGroup> + <after> + <!--Set back the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + </after> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml index 8b6b69b73b894..27e9583db5fd2 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml @@ -9,12 +9,14 @@ <testCase name="Magento\Checkout\Test\TestCase\ShoppingCartPagerTest" summary="Verify pager on Shopping Cart" ticketId="MAGETWO-63337"> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd20Products" summary="Verify pager is NOT presented on Shopping Cart page if qty of products = 20, by default system configuration" ticketId="MAGETWO-63337"> <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd21Products" summary="Verify pager is presented on Shopping Cart page if items qty=21, by default system configuration" ticketId="MAGETWO-63338"> <data name="tag" xsi:type="string">severity:S2</data> <data name="config/dataset" xsi:type="string">default_number_of_items_per_page_on_shopping_cart</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart"/> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText"/> </variation> @@ -22,11 +24,13 @@ <data name="tag" xsi:type="string">severity:S2</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> <data name="itemsToRemove" xsi:type="string">1</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestForOneItemPerPageAnd20Products" summary="Verify Pager is presented on Shopping Cart page with non-default system configuration" ticketId="MAGETWO-63340"> <data name="tag" xsi:type="string">severity:S2</data> <data name="configData" xsi:type="string">one_item_per_page_on_shopping_cart</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart" /> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText" /> </variation> From 64cdcebc9ef1513e555c7d22bbb5495aca1cd41c Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 11 Apr 2019 19:24:17 +0300 Subject: [PATCH 0005/2437] Refactoring the Action Groups by best practices --- ...tIsNotVisibleCartPagerTextActionGroup.xml} | 7 ---- ...ssertIsVisibleCartPagerTextActionGroup.xml | 17 ++++++++++ ...refrontAdd20ProductsToCartActionGroup.xml} | 22 ------------- .../StorefrontDelete20ProductsActionGroup.xml | 32 +++++++++++++++++++ 4 files changed, 49 insertions(+), 29 deletions(-) rename app/code/Magento/Checkout/Test/Mftf/ActionGroup/{AssertCartPagerTextActionGroup.xml => AssertIsNotVisibleCartPagerTextActionGroup.xml} (68%) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml rename app/code/Magento/Checkout/Test/Mftf/ActionGroup/{Manage20ProductsActionGroup.xml => StorefrontAdd20ProductsToCartActionGroup.xml} (80%) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml similarity index 68% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml index 0236f213435b1..d08a6f3cf5053 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertCartPagerTextActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml @@ -7,13 +7,6 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertPagerTextIsVisibleActionGroup"> - <arguments> - <argument name="text" type="string"/> - </arguments> - <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> - <see userInput="{{text}}" stepKey="VerifyPagerText"/> - </actionGroup> <actionGroup name="AssertPagerTextIsNotVisibleActionGroup"> <arguments> <argument name="text" type="string"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml new file mode 100644 index 0000000000000..f1793ecd640b4 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertPagerTextIsVisibleActionGroup"> + <arguments> + <argument name="text" type="string"/> + </arguments> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> + <see userInput="{{text}}" stepKey="VerifyPagerText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml similarity index 80% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml index 132c46e8ba612..b4662afffec69 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/Manage20ProductsActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml @@ -109,26 +109,4 @@ <waitForPageLoad stepKey="waitForPageLoad20"/> </actionGroup> - <actionGroup name="Delete20ProductsActionGroup"> - <deleteData createDataKey="simpleProduct1CartItems" stepKey="deleteCartItem1"/> - <deleteData createDataKey="simpleProduct2CartItems" stepKey="deleteCartItem2"/> - <deleteData createDataKey="simpleProduct3CartItems" stepKey="deleteCartItem3"/> - <deleteData createDataKey="simpleProduct4CartItems" stepKey="deleteCartItem4"/> - <deleteData createDataKey="simpleProduct5CartItems" stepKey="deleteCartItem5"/> - <deleteData createDataKey="simpleProduct6CartItems" stepKey="deleteCartItem6"/> - <deleteData createDataKey="simpleProduct7CartItems" stepKey="deleteCartItem7"/> - <deleteData createDataKey="simpleProduct8CartItems" stepKey="deleteCartItem8"/> - <deleteData createDataKey="simpleProduct9CartItems" stepKey="deleteCartItem9"/> - <deleteData createDataKey="simpleProduct10CartItems" stepKey="deleteCartItem10"/> - <deleteData createDataKey="simpleProduct11CartItems" stepKey="deleteCartItem11"/> - <deleteData createDataKey="simpleProduct12CartItems" stepKey="deleteCartItem12"/> - <deleteData createDataKey="simpleProduct13CartItems" stepKey="deleteCartItem13"/> - <deleteData createDataKey="simpleProduct14CartItems" stepKey="deleteCartItem14"/> - <deleteData createDataKey="simpleProduct15CartItems" stepKey="deleteCartItem15"/> - <deleteData createDataKey="simpleProduct16CartItems" stepKey="deleteCartItem16"/> - <deleteData createDataKey="simpleProduct17CartItems" stepKey="deleteCartItem17"/> - <deleteData createDataKey="simpleProduct18CartItems" stepKey="deleteCartItem18"/> - <deleteData createDataKey="simpleProduct19CartItems" stepKey="deleteCartItem19"/> - <deleteData createDataKey="simpleProduct20CartItems" stepKey="deleteCartItem20"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml new file mode 100644 index 0000000000000..f3db7f989d31d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="Delete20ProductsActionGroup"> + <deleteData createDataKey="simpleProduct1CartItems" stepKey="deleteCartItem1"/> + <deleteData createDataKey="simpleProduct2CartItems" stepKey="deleteCartItem2"/> + <deleteData createDataKey="simpleProduct3CartItems" stepKey="deleteCartItem3"/> + <deleteData createDataKey="simpleProduct4CartItems" stepKey="deleteCartItem4"/> + <deleteData createDataKey="simpleProduct5CartItems" stepKey="deleteCartItem5"/> + <deleteData createDataKey="simpleProduct6CartItems" stepKey="deleteCartItem6"/> + <deleteData createDataKey="simpleProduct7CartItems" stepKey="deleteCartItem7"/> + <deleteData createDataKey="simpleProduct8CartItems" stepKey="deleteCartItem8"/> + <deleteData createDataKey="simpleProduct9CartItems" stepKey="deleteCartItem9"/> + <deleteData createDataKey="simpleProduct10CartItems" stepKey="deleteCartItem10"/> + <deleteData createDataKey="simpleProduct11CartItems" stepKey="deleteCartItem11"/> + <deleteData createDataKey="simpleProduct12CartItems" stepKey="deleteCartItem12"/> + <deleteData createDataKey="simpleProduct13CartItems" stepKey="deleteCartItem13"/> + <deleteData createDataKey="simpleProduct14CartItems" stepKey="deleteCartItem14"/> + <deleteData createDataKey="simpleProduct15CartItems" stepKey="deleteCartItem15"/> + <deleteData createDataKey="simpleProduct16CartItems" stepKey="deleteCartItem16"/> + <deleteData createDataKey="simpleProduct17CartItems" stepKey="deleteCartItem17"/> + <deleteData createDataKey="simpleProduct18CartItems" stepKey="deleteCartItem18"/> + <deleteData createDataKey="simpleProduct19CartItems" stepKey="deleteCartItem19"/> + <deleteData createDataKey="simpleProduct20CartItems" stepKey="deleteCartItem20"/> + </actionGroup> +</actionGroups> From 5fdc9efa6267079ceb3d84c34a2d89a9534796d1 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 16 Apr 2019 18:43:30 -0500 Subject: [PATCH 0006/2437] MC-15901: Recursion in filter widget --- .../Magento/Catalog/Ui/Component/ColumnFactory.php | 1 + .../Ui/view/base/web/js/grid/filters/filters.js | 1 + lib/web/mage/utils/template.js | 12 +++++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index f7554219c6c31..1495d6a41d91b 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -73,6 +73,7 @@ public function create($attribute, $context, array $config = []) 'filter' => ($attribute->getIsFilterableInGrid() || array_key_exists($columnName, $filterModifiers)) ? $this->getFilterType($attribute->getFrontendInput()) : null, + '__disableTmpl' => true, ], $config); if ($attribute->usesSource()) { diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 98c3eb1c6f882..e6ac0b2b80f42 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -272,6 +272,7 @@ define([ } filter = utils.extend({}, filters.base, filter); + filter.__disableTmpl = {label : true}; return utils.template(filter, { filters: this, diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index 7aa695023cb56..861c352ee2026 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -37,7 +37,7 @@ define([ * * @param {String} tmpl * @param {Object} target - * @returns {Boolean} + * @returns {Boolean|Object} */ function isTmplIgnored(tmpl, target) { var parsedTmpl; @@ -53,7 +53,7 @@ define([ if (typeof target !== 'undefined') { if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) { - return true; + return target.__disableTmpl; } } @@ -201,6 +201,8 @@ define([ * Template iterator function. */ _.each(tmpl, function iterate(value, key, list) { + var disabled; + if (key === '$data') { return; } @@ -213,7 +215,11 @@ define([ } if (isTemplate(value)) { - list[key] = render(value, tmpl, castString, list); + disabled = isTmplIgnored(value, list); + + if (!((typeof disabled) == 'object' && disabled.hasOwnProperty(key) && disabled[key])) { + list[key] = render(value, tmpl, castString, list); + } } else if ($.isPlainObject(value) || Array.isArray(value)) { _.each(value, iterate); } From 29a98deda1da6b2b2a596668028049b8a32bd857 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 18 Apr 2019 10:52:54 -0500 Subject: [PATCH 0007/2437] MC-15901: Recursion in filter widget --- .../Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php | 1 + app/code/Magento/Catalog/Ui/Component/ColumnFactory.php | 2 +- app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js | 4 +++- lib/web/mage/utils/template.js | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 774edcfeb6b64..45d911f7e94e5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -111,6 +111,7 @@ public function testCreateWithNotFilterableInGridAttribute(array $filterModifier 'visible' => null, 'filter' => $filter, 'component' => 'Magento_Ui/js/grid/columns/column', + '__disableTmpl' => ['label' => true] ], ], 'context' => $this->context, diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 1495d6a41d91b..491d945e4644f 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -73,7 +73,7 @@ public function create($attribute, $context, array $config = []) 'filter' => ($attribute->getIsFilterableInGrid() || array_key_exists($columnName, $filterModifiers)) ? $this->getFilterType($attribute->getFrontendInput()) : null, - '__disableTmpl' => true, + '__disableTmpl' => ['label' => true], ], $config); if ($attribute->usesSource()) { diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index e6ac0b2b80f42..26d21eac0c28b 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -272,7 +272,9 @@ define([ } filter = utils.extend({}, filters.base, filter); - filter.__disableTmpl = {label : true}; + filter.__disableTmpl = { + label: true + }; return utils.template(filter, { filters: this, diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index 861c352ee2026..a459d73ff360e 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -217,7 +217,7 @@ define([ if (isTemplate(value)) { disabled = isTmplIgnored(value, list); - if (!((typeof disabled) == 'object' && disabled.hasOwnProperty(key) && disabled[key])) { + if (typeof disabled !== 'object' || !disabled.hasOwnProperty(key) || !disabled[key]) { list[key] = render(value, tmpl, castString, list); } } else if ($.isPlainObject(value) || Array.isArray(value)) { From e51a4e2fa7b3930c964982f897981c72d7cc387e Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 18 Apr 2019 19:03:02 -0500 Subject: [PATCH 0008/2437] MC-15901: Recursion in filter widget --- .../view/base/web/js/grid/filters/filters.js | 11 ++++- lib/web/mage/utils/template.js | 43 +++++++++++++------ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 26d21eac0c28b..c608400a6f174 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -272,14 +272,21 @@ define([ } filter = utils.extend({}, filters.base, filter); + //Accepting labels as is. filter.__disableTmpl = { - label: true + label: 1 }; - return utils.template(filter, { + filter = utils.template(filter, { filters: this, column: column }, true, true); + + filter.__disableTmpl = { + label: true + }; + + return filter; }, /** diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index a459d73ff360e..cd3e66ef58cf1 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -33,7 +33,11 @@ define([ })(); /** - * Validates template + * Objects can specify how to use templating for their properties - getting that configuration. + * + * To disable rendering for all properties of your object add __disableTmpl: true. + * To disable for specific property add __disableTmpl: {propertyName: true}. + * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}. * * @param {String} tmpl * @param {Object} target @@ -69,14 +73,10 @@ define([ * * @param {String} tmpl - Template string. * @param {Object} $ - Data object used in a template. - * @param {Object} target * @returns {String} Compiled template. */ - template = function (tmpl, $, target) { - - if (!isTmplIgnored(tmpl, target)) { - return eval('`' + tmpl + '`'); - } + template = function (tmpl, $) { + return eval('`' + tmpl + '`'); return tmpl; }; @@ -130,19 +130,22 @@ define([ * @param {Boolean} [castString=false] - Flag that indicates whether template * should be casted after evaluation to a value of another type or * that it should be leaved as a string. + * @param {Number|undefined} maxCycles Maximum number of rendering cycles, can be 0. * @returns {*} Compiled template. */ - function render(tmpl, data, castString, target) { - var last = tmpl; + function render(tmpl, data, castString, maxCycles) { + var last = tmpl, + cycles = 0; - while (~tmpl.indexOf(opener)) { - tmpl = template(tmpl, data, target); + while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) { + tmpl = template(tmpl, data); if (tmpl === last) { break; } last = tmpl; + cycles++; } return castString ? @@ -201,7 +204,8 @@ define([ * Template iterator function. */ _.each(tmpl, function iterate(value, key, list) { - var disabled; + var disabled, + maxCycles; if (key === '$data') { return; @@ -215,11 +219,22 @@ define([ } if (isTemplate(value)) { + //Getting template disabling settings, can be true for all disabled and separate settings + //for each property. disabled = isTmplIgnored(value, list); - if (typeof disabled !== 'object' || !disabled.hasOwnProperty(key) || !disabled[key]) { - list[key] = render(value, tmpl, castString, list); + if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) { + //Checking if specific settings for a property provided. + maxCycles = disabled[key]; + if (maxCycles === true) { + maxCycles = 0; + } + } else if (disabled === true) { + //Rendering for all properties is disabled. + maxCycles = 0; } + + list[key] = render(value, tmpl, castString, maxCycles); } else if ($.isPlainObject(value) || Array.isArray(value)) { _.each(value, iterate); } From 76c9f8353ff0f9437d970a4d73a1852149396202 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 19 Apr 2019 17:34:04 -0500 Subject: [PATCH 0009/2437] MC-15901: Recursion in filter widget --- lib/web/mage/utils/template.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index cd3e66ef58cf1..192e6bddcf7ac 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -77,8 +77,6 @@ define([ */ template = function (tmpl, $) { return eval('`' + tmpl + '`'); - - return tmpl; }; /*eslint-enable no-unused-vars, no-eval*/ @@ -130,7 +128,7 @@ define([ * @param {Boolean} [castString=false] - Flag that indicates whether template * should be casted after evaluation to a value of another type or * that it should be leaved as a string. - * @param {Number|undefined} maxCycles Maximum number of rendering cycles, can be 0. + * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0. * @returns {*} Compiled template. */ function render(tmpl, data, castString, maxCycles) { @@ -226,10 +224,7 @@ define([ if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) { //Checking if specific settings for a property provided. maxCycles = disabled[key]; - if (maxCycles === true) { - maxCycles = 0; - } - } else if (disabled === true) { + } else if (disabled === true || maxCycles === true) { //Rendering for all properties is disabled. maxCycles = 0; } From ac7fe775ef062af5ebe5bbfbb5cc85df1d212d86 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 19 Apr 2019 17:40:26 -0500 Subject: [PATCH 0010/2437] MC-15901: Recursion in filter widget --- lib/web/mage/utils/template.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index 192e6bddcf7ac..4032c9387904d 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -224,7 +224,9 @@ define([ if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) { //Checking if specific settings for a property provided. maxCycles = disabled[key]; - } else if (disabled === true || maxCycles === true) { + } + + if (disabled === true || maxCycles === true) { //Rendering for all properties is disabled. maxCycles = 0; } From 9051331d7782641ae855421340c0dbb27c8daaf1 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Mon, 22 Apr 2019 15:19:27 -0500 Subject: [PATCH 0011/2437] MC-15576: CMS block cache --- app/code/Magento/Cms/Block/Block.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Cms/Block/Block.php b/app/code/Magento/Cms/Block/Block.php index d0d75ea691195..99c18c4913948 100644 --- a/app/code/Magento/Cms/Block/Block.php +++ b/app/code/Magento/Cms/Block/Block.php @@ -18,6 +18,11 @@ class Block extends AbstractBlock implements \Magento\Framework\DataObject\Ident */ protected $_filterProvider; + /** + * Prefix for cache key of CMS block + */ + const CACHE_KEY_PREFIX = 'CMS_BLOCK_'; + /** * Store manager * From 406024bc7abcc1d588211f00f01bd7606ae57c47 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 23 Apr 2019 15:03:55 -0500 Subject: [PATCH 0012/2437] MC-15132: Update XML Design --- .../Argument/Interpreter/ConfigurableObject.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index c40d26ab2f500..35a5fffd45269 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -13,6 +13,13 @@ */ class ConfigurableObject implements InterpreterInterface { + /** + * @var array + */ + private $classBlacklist = [ + \Zend\Code\Reflection\FileReflection::class + ]; + /** * @var ObjectManagerInterface */ @@ -53,6 +60,14 @@ public function evaluate(array $data) if (!isset($arguments['class'])) { throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); } + + if (in_array(ltrim($arguments['class'], '\\'), $this->classBlacklist)) { + throw new \InvalidArgumentException(sprintf( + 'Class argument is invalid: %s', + $arguments['class'] + )); + } + $className = $arguments['class']; unset($arguments['class']); } From 966764b097664efdc34e6651703edb7ea27d01ab Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Wed, 24 Apr 2019 09:58:27 -0500 Subject: [PATCH 0013/2437] MC-15576: CMS block cache --- app/code/Magento/Cms/Block/Block.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Block/Block.php b/app/code/Magento/Cms/Block/Block.php index 99c18c4913948..2ff740c9303b3 100644 --- a/app/code/Magento/Cms/Block/Block.php +++ b/app/code/Magento/Cms/Block/Block.php @@ -14,14 +14,14 @@ class Block extends AbstractBlock implements \Magento\Framework\DataObject\IdentityInterface { /** - * @var \Magento\Cms\Model\Template\FilterProvider + * Prefix for cache key of CMS block */ - protected $_filterProvider; + const CACHE_KEY_PREFIX = 'CMS_BLOCK_'; /** - * Prefix for cache key of CMS block + * @var \Magento\Cms\Model\Template\FilterProvider */ - const CACHE_KEY_PREFIX = 'CMS_BLOCK_'; + protected $_filterProvider; /** * Store manager From fe7fbce272c0c0fdd06b7eaadede0bd18cfe8212 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 24 Apr 2019 23:01:24 -0500 Subject: [PATCH 0014/2437] MC-15811: Incorrect Url Added Store Switcher Added Controller Added Unit test. --- .../Magento/Backend/etc/adminhtml/system.xml | 5 - .../Store/Controller/Store/SwitchRequest.php | 131 +++++++++++ .../Model/StoreSwitcher/HashGenerator.php | 88 ++++++++ .../Store/Setup/Patch/Data/DisableSid.php | 80 +++++++ .../Controller/Store/SwitchRequestTest.php | 128 +++++++++++ app/code/Magento/Store/etc/config.xml | 2 +- app/code/Magento/Store/etc/di.xml | 1 + .../Magento/Store/etc/frontend/sections.xml | 1 + .../Framework/Session/SidResolverTest.php | 210 ------------------ .../Magento/Framework/Session/SidResolver.php | 1 + 10 files changed, 431 insertions(+), 216 deletions(-) create mode 100644 app/code/Magento/Store/Controller/Store/SwitchRequest.php create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php create mode 100644 app/code/Magento/Store/Setup/Patch/Data/DisableSid.php create mode 100644 app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Framework/Session/SidResolverTest.php diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 98b8e702b1c53..e58335d95dc66 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -583,11 +583,6 @@ <label>Validate HTTP_USER_AGENT</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="use_frontend_sid" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> - <label>Use SID on Storefront</label> - <comment>Allows customers to stay logged in when switching between different stores.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> </group> </section> </system> diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php new file mode 100644 index 0000000000000..af2d20b5fba85 --- /dev/null +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Controller\Store; + +use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Customer\Model\Session as CustomerSession; +use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Customer\Api\CustomerRepositoryInterface; +use \Magento\Framework\Exception\LocalizedException; + +/** + * Builds correct url to target store and performs redirect. + */ +class SwitchRequest extends \Magento\Framework\App\Action\Action +{ + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var customerSession + */ + private $customerSession; + + /** + * @var \Magento\Framework\App\DeploymentConfig + */ + private $deploymentConfig; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param Context $context + * @param StoreRepositoryInterface $storeRepository + * @param CustomerSession $session + * @param DeploymentConfig $deploymentConfig + * @param CustomerRepositoryInterface $customerRepository + */ + public function __construct( + Context $context, + StoreRepositoryInterface $storeRepository, + CustomerSession $session, + DeploymentConfig $deploymentConfig, + CustomerRepositoryInterface $customerRepository + ) { + parent::__construct($context); + $this->storeRepository = $storeRepository; + $this->customerSession = $session; + $this->deploymentConfig = $deploymentConfig; + $this->customerRepository=$customerRepository; + } + + /** + * Execute action + * + * @return void + */ + public function execute() + { + $fromStoreCode = (string)$this->_request->getParam('___from_store'); + $customerId = (int)$this->_request->getParam('customer_id'); + $timeStamp = (string)$this->_request->getParam('time_stamp'); + $targetStoreCode = $this->_request->getParam('___to_store'); + $signature = (string)$this->_request->getParam('signature'); + $error = null; + + try { + /** @var \Magento\Store\Model\Store $fromStore */ + $fromStore = $this->storeRepository->get($fromStoreCode); + } catch (NoSuchEntityException $e) { + $error = __('Requested store is not found.'); + } + + if ($this->validateHash($customerId, $timeStamp, $signature, $fromStoreCode)) { + try { + $customer = $this->customerRepository->getById($customerId); + if (!$this->customerSession->isLoggedIn()) { + $this->customerSession->setCustomerDataAsLoggedIn($customer); + } + } catch (NoSuchEntityException $e) { + $error = __('The requested customer does not exist.'); + } catch (LocalizedException $e) { + $error = __('There was an error retrieving the customer record.'); + } + } else { + $error = __('Invalid request. Store switching action cannot be performed at this time.'); + } + + if ($error !== null) { + $this->messageManager->addErrorMessage($error); + $this->getResponse()->setRedirect('/'); + } else { + $this->getResponse()->setRedirect("/$targetStoreCode"); + } + } + + /** + * Validates one time token + * + * @param int $customerId + * @param string $timeStamp + * @param string $signature + * @param string $fromStoreCode + * @return bool + */ + private function validateHash(int $customerId, string $timeStamp, string $signature, string $fromStoreCode): bool + { + + if ($customerId && $timeStamp && $signature) { + $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); + $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + if (time() - $timeStamp <= 5 && hash_equals($signature, hash_hmac('sha256', $data, $key))) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php new file mode 100644 index 0000000000000..4a8549f299ed0 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcherInterface; +use Magento\Customer\Model\Session as CustomerSession; +use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\Url\Helper\Data as UrlHelper; +use Magento\Framework\Config\ConfigOptionsListConstants; + +/** + * Generate one time token and build redirect url + */ +class HashGenerator implements StoreSwitcherInterface +{ + + /** + * @var \Magento\Customer\Model\Session + */ + private $customerSession; + + /** + * @var \Magento\Framework\App\DeploymentConfig + */ + private $deploymentConfig; + + /** + * @var UrlHelper + */ + private $urlHelper; + + /** + * @param CustomerSession $customerSession + * @param DeploymentConfig $deploymentConfig + * @param UrlHelper $urlHelper + */ + public function __construct( + CustomerSession $customerSession, + DeploymentConfig $deploymentConfig, + UrlHelper $urlHelper + ) { + $this->customerSession = $customerSession; + $this->deploymentConfig = $deploymentConfig; + $this->urlHelper=$urlHelper; + } + + /** + * Builds redirect url with token + * + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $targetUrl = $redirectUrl; + $customerId = $this->customerSession->getId(); + + if ($customerId) { + $urlParts = parse_url($targetUrl); + $host = $urlParts['host']; + $scheme = $urlParts['scheme']; + $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + $timeStamp = time(); + $fromStoreCode = $fromStore->getCode(); + $targetStoreCode=$targetStore->getCode(); + $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); + $signature = hash_hmac('sha256', $data, $key); + $targetUrl = $scheme . "://" . $host.'/stores/store/switchrequest'; + $targetUrl = $this->urlHelper->addRequestParam( + $targetUrl, + ['customer_id' => $this->customerSession->getId()] + ); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => time()]); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___from_store' => $fromStoreCode]); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___to_store' => $targetStoreCode]); + } + return $targetUrl; + } +} diff --git a/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php b/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php new file mode 100644 index 0000000000000..129f5538f5cdc --- /dev/null +++ b/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Setup\Patch\Data; + +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use \Magento\Framework\App\Config\MutableScopeConfigInterface; + +/** + * Class CreateDefaultPages + * @package Magento\Cms\Setup\Patch + */ +class DisableSid implements DataPatchInterface, PatchVersionInterface +{ + + /** + * Config path for flag whether use SID on frontend + */ + const XML_PATH_USE_FRONTEND_SID = 'web/session/use_frontend_sid'; + + /** + * @var \Magento\Framework\App\Config\MutableScopeConfigInterface + */ + private $mutableScopeConfig; + + /** + * @var string + */ + protected $_scopeType; + + /** + * Disable Sid constructor. + * @param MutableScopeConfigInterface $mutableScopeConfig + * @param string $scopeType + */ + public function __construct( + MutableScopeConfigInterface $mutableScopeConfig, + $scopeType + ) { + $this->mutableScopeConfig=$mutableScopeConfig; + $this->_scopeType=$scopeType; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function apply() + { + $this->mutableScopeConfig->setValue(self::XML_PATH_USE_FRONTEND_SID, 0, $this->_scopeType); + } + + /** + * {@inheritdoc} + */ + public static function getDependencies() + { + return []; + } + + /** + * {@inheritdoc} + */ + public static function getVersion() + { + return '2.0.0'; + } + + /** + * {@inheritdoc} + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php new file mode 100644 index 0000000000000..a8d4472145deb --- /dev/null +++ b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Store\Test\Unit\Controller\Store; + +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Customer\Model\Session as CustomerSession; +use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; + +/** + * Test class for \Magento\Store\Controller\Store\SwitchAction + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SwitchRequestTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Store\Controller\Store\SwitchRequest + */ + private $model; + + /** + * @var StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeRepositoryMock; + + /** + * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerSessionMock; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepositoryMock; + + /** + * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $deploymentConfigMock; + + /** + * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + + /** + * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $responseMock; + + /** + * @return void + */ + protected function setUp() + { + $this->customerSessionMock = $this->getMockBuilder(CustomerSession::class) + ->disableOriginalConstructor()->getMock(); + $this->customerRepositoryMock = + $this->getMockBuilder(CustomerRepositoryInterface::class)->getMock(); + $this->storeRepositoryMock = + $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) + ->disableOriginalConstructor()->setMethods(['get'])->getMockForAbstractClass(); + + $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)->getMock(); + $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setRedirect']) + ->getMockForAbstractClass(); + $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) + ->disableOriginalConstructor()->getMock(); + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getBaseUrl']) + ->getMockForAbstractClass(); + + $this->model = (new ObjectManager($this))->getObject( + \Magento\Store\Controller\Store\SwitchRequest::class, + [ + 'customerSession' => $this->customerRepositoryMock, + 'deploymentConfig' => $this->deploymentConfigMock, + 'storeRepository' => $this->storeRepositoryMock, + 'customerRepository' => $this->customerRepositoryMock, + '_request' => $this->requestMock, + '_response' => $this->responseMock, + ] + ); + } + + /** + * @return void + */ + public function testExecute() + { + $fromStoreCode = 'sv2'; + $targetStoreCode = 'default'; + $expectedRedirectUrl='/'; + $customerId=5; + $timestamp='1556131830'; + + $this->requestMock->method('getParam') + ->willReturnMap([ + ['___from_store', null, $fromStoreCode], + ['customer_id', null, $customerId], + ['time_stamp', null, $timestamp], + ['___to_store', null, $targetStoreCode], + ['signature', null, 'cbc099b3cc4a9a8f3a78a97e7a579ceff19a2b26a6c88b08f0f58442ea5bd968'] + ]); + + $this->storeRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($fromStoreCode) + ->willReturn($this->storeMock); + + $this->responseMock->expects($this->once())->method('setRedirect')->with($expectedRedirectUrl); + $this->model->execute(); + } +} \ No newline at end of file diff --git a/app/code/Magento/Store/etc/config.xml b/app/code/Magento/Store/etc/config.xml index b9e7ac1c6aca0..23182c8d76470 100644 --- a/app/code/Magento/Store/etc/config.xml +++ b/app/code/Magento/Store/etc/config.xml @@ -83,7 +83,7 @@ <use_http_via>0</use_http_via> <use_http_x_forwarded_for>0</use_http_x_forwarded_for> <use_http_user_agent>0</use_http_user_agent> - <use_frontend_sid>1</use_frontend_sid> + <use_frontend_sid>0</use_frontend_sid> </session> <browser_capabilities> <cookies>1</cookies> diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index defe0694d018d..62f6f41424025 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -436,6 +436,7 @@ <item name="cleanTargetUrl" xsi:type="object">Magento\Store\Model\StoreSwitcher\CleanTargetUrl</item> <item name="manageStoreCookie" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManageStoreCookie</item> <item name="managePrivateContent" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManagePrivateContent</item> + <item name="hashGenerator" xsi:type="object">Magento\Store\Model\StoreSwitcher\HashGenerator</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Store/etc/frontend/sections.xml b/app/code/Magento/Store/etc/frontend/sections.xml index b1a9fc3cb1d71..b7dbfe405263b 100644 --- a/app/code/Magento/Store/etc/frontend/sections.xml +++ b/app/code/Magento/Store/etc/frontend/sections.xml @@ -8,4 +8,5 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd"> <action name="stores/store/switch"/> + <action name="stores/store/switchrequest"/> </config> diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SidResolverTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SidResolverTest.php deleted file mode 100644 index 5e70eb491b50c..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Framework/Session/SidResolverTest.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\Session; - -use Magento\Framework\App\State; -use Zend\Stdlib\Parameters; - -class SidResolverTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Framework\Session\SidResolver - */ - protected $model; - - /** - * @var \Magento\Framework\Session\Generic - */ - protected $session; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\Store - */ - protected $store; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Config\ScopeConfigInterface - */ - protected $scopeConfig; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\UrlInterface - */ - protected $urlBuilder; - - /** - * @var string - */ - protected $customSessionName = 'csn'; - - /** - * @var string - */ - protected $customSessionQueryParam = 'csqp'; - - /** - * @var \Magento\Framework\App\RequestInterface - */ - protected $request; - - /** - * @var State|\PHPUnit_Framework_MockObject_MockObject - */ - private $appState; - - protected function setUp() - { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - /** @var \Magento\Framework\Session\Generic _model */ - $this->session = $objectManager->get(\Magento\Framework\Session\Generic::class); - - $this->scopeConfig = $this->getMockBuilder( - \Magento\Framework\App\Config\ScopeConfigInterface::class - )->setMethods( - ['getValue'] - )->disableOriginalConstructor()->getMockForAbstractClass(); - - $this->urlBuilder = $this->getMockBuilder( - \Magento\Framework\Url::class - )->setMethods( - ['isOwnOriginUrl'] - )->disableOriginalConstructor()->getMockForAbstractClass(); - - $this->request = $objectManager->get(\Magento\Framework\App\RequestInterface::class); - - $this->appState = $this->getMockBuilder(State::class) - ->setMethods(['getAreaCode']) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = $objectManager->create( - \Magento\Framework\Session\SidResolver::class, - [ - 'scopeConfig' => $this->scopeConfig, - 'urlBuilder' => $this->urlBuilder, - 'sidNameMap' => [$this->customSessionName => $this->customSessionQueryParam], - 'request' => $this->request, - 'appState' => $this->appState, - ] - ); - } - - public function tearDown() - { - $this->request->setQuery(new Parameters()); - } - - /** - * @param mixed $sid - * @param bool $useFrontedSid - * @param bool $isOwnOriginUrl - * @param mixed $testSid - * @dataProvider dataProviderTestGetSid - */ - public function testGetSid($sid, $useFrontedSid, $isOwnOriginUrl, $testSid) - { - $this->appState->expects($this->atLeastOnce()) - ->method('getAreaCode') - ->willReturn(\Magento\Framework\App\Area::AREA_FRONTEND); - - $this->scopeConfig->expects( - $this->any() - )->method( - 'isSetFlag' - )->with( - \Magento\Framework\Session\SidResolver::XML_PATH_USE_FRONTEND_SID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - )->will( - $this->returnValue($useFrontedSid) - ); - - $this->urlBuilder->expects($this->any())->method('isOwnOriginUrl')->will($this->returnValue($isOwnOriginUrl)); - - if ($testSid) { - $this->request->getQuery()->set($this->model->getSessionIdQueryParam($this->session), $testSid); - } - $this->assertEquals($sid, $this->model->getSid($this->session)); - $this->assertEquals($useFrontedSid, $this->model->getUseSessionInUrl()); - } - - /** - * @return array - */ - public function dataProviderTestGetSid() - { - return [ - [null, false, false, 'test-sid'], - [null, false, true, 'test-sid'], - [null, false, false, 'test-sid'], - [null, true, false, 'test-sid'], - [null, false, true, 'test-sid'], - ['test-sid', true, true, 'test-sid'], - [null, true, true, null] - ]; - } - - public function testGetSessionIdQueryParam() - { - $this->assertEquals(SidResolver::SESSION_ID_QUERY_PARAM, $this->model->getSessionIdQueryParam($this->session)); - } - - public function testGetSessionIdQueryParamCustom() - { - $this->session->destroy(); - $oldSessionName = $this->session->getName(); - $this->session->setName($this->customSessionName); - $this->assertEquals($this->customSessionQueryParam, $this->model->getSessionIdQueryParam($this->session)); - $this->session->setName($oldSessionName); - $this->session->start(); - } - - public function testSetGetUseSessionVar() - { - $this->assertFalse($this->model->getUseSessionVar()); - $this->model->setUseSessionVar(true); - $this->assertTrue($this->model->getUseSessionVar()); - } - - /** - * Variations of Use SID on frontend value. - * - * @return array - */ - public function dataProviderSessionInUrl() - { - return [ - [true], - [false], - ]; - } - - /** - * Testing "Use SID in URLs" flag. - * Checking that the method returns config value if not explicitly - * overridden. - * - * @param bool $configValue Use SID on frontend config value. - * @dataProvider dataProviderSessionInUrl - */ - public function testSetGetUseSessionInUrl($configValue) - { - $this->scopeConfig->expects( - $this->any() - )->method( - 'isSetFlag' - )->with( - \Magento\Framework\Session\SidResolver::XML_PATH_USE_FRONTEND_SID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - )->will( - $this->returnValue($configValue) - ); - - $this->assertEquals($configValue, $this->model->getUseSessionInUrl()); - $this->model->setUseSessionInUrl(!$configValue); - $this->assertEquals(!$configValue, $this->model->getUseSessionInUrl()); - } -} diff --git a/lib/internal/Magento/Framework/Session/SidResolver.php b/lib/internal/Magento/Framework/Session/SidResolver.php index 1208aeb31eaee..117c46f3443a7 100644 --- a/lib/internal/Magento/Framework/Session/SidResolver.php +++ b/lib/internal/Magento/Framework/Session/SidResolver.php @@ -11,6 +11,7 @@ /** * Class SidResolver + * @deprecated 2.3.2 */ class SidResolver implements SidResolverInterface { From 25e7ae6d50d6fe4acbb7637361cda7842c9af820 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 25 Apr 2019 12:43:29 -0500 Subject: [PATCH 0015/2437] MC-15901: Recursion in filter widget --- .../Catalog/Model/Category/Attribute/Source/Sortby.php | 1 + .../Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php | 2 ++ .../Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php | 1 + .../Product/Form/Modifier/Data/AssociatedProducts.php | 5 +++++ 4 files changed, 9 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php index 97bc00bc7dd64..f279cc5150d7b 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php @@ -50,6 +50,7 @@ public function getAllOptions() $this->_options[] = [ 'label' => __($attribute['frontend_label']), 'value' => $attribute['attribute_code'], + '__disableTmpl' => true ]; } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 209fd235bcd6e..bc15c269df1c9 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -671,6 +671,7 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC 'scopeLabel' => $this->getScopeLabel($attribute), 'globalScope' => $this->isScopeGlobal($attribute), 'sortOrder' => $sortOrder * self::SORT_ORDER_MULTIPLIER, + '__disableTmpl' => ['label' => true, 'code' => true] ]); // TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done @@ -816,6 +817,7 @@ public function setupAttributeContainerMeta(ProductAttributeInterface $attribute 'breakLine' => false, 'label' => $attribute->getDefaultFrontendLabel(), 'required' => $attribute->getIsRequired(), + '__disableTmpl' => ['label' => true] ] ); diff --git a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php index 4874dc8ea03ae..a7bdaaab64cb4 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php +++ b/app/code/Magento/ConfigurableProduct/Block/Adminhtml/Product/Edit/Tab/Variations/Config/Matrix.php @@ -411,6 +411,7 @@ private function prepareAttributes( 'id' => $attribute->getAttributeId(), 'position' => $configurableAttributes[$attribute->getAttributeId()]['position'], 'chosen' => [], + '__disableTmpl' => true ]; foreach ($attribute->getOptions() as $option) { if (!empty($option->getValue())) { diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php index c474acbec5094..45a48a37e5ceb 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php @@ -213,6 +213,7 @@ public function getConfigurableAttributesData() 'code' => $attribute['code'], 'label' => $attribute['label'], 'position' => $attribute['position'], + '__disableTmpl' => true ]; foreach ($attribute['chosen'] as $chosenOption) { @@ -261,6 +262,7 @@ protected function prepareVariations() 'id' => $attribute->getAttributeId(), 'position' => $configurableAttributes[$attribute->getAttributeId()]['position'], 'chosen' => [], + '__disableTmpl' => true ]; foreach ($attribute->getOptions() as $option) { if (!empty($option->getValue())) { @@ -270,6 +272,7 @@ protected function prepareVariations() 'id' => $option->getValue(), 'label' => $option->getLabel(), 'value' => $option->getValue(), + '__disableTmpl' => true ]; } } @@ -281,6 +284,7 @@ protected function prepareVariations() 'id' => $optionId, 'label' => $variation[$attribute->getId()]['label'], 'value' => $optionId, + '__disableTmpl' => true ]; $variationOptions[] = $variationOption; $attributes[$attribute->getAttributeId()]['chosen'][$optionId] = $variationOption; @@ -306,6 +310,7 @@ protected function prepareVariations() 'newProduct' => 0, 'attributes' => $this->getTextAttributes($variationOptions), 'thumbnail_image' => $this->imageHelper->init($product, 'product_thumbnail_image')->getUrl(), + '__disableTmpl' => true ]; $productIds[] = $product->getId(); } From 2d3bb5074882cff729697338c0d6030f495f06f2 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 25 Apr 2019 13:30:29 -0500 Subject: [PATCH 0016/2437] MC-15811: Incorrect Url Fix static errors. Modify unit test. Fix store patch. --- .../Store/Controller/Store/SwitchRequest.php | 12 ++++++--- .../Model/StoreSwitcher/HashGenerator.php | 26 ++++++++++++++----- .../Store/Setup/Patch/Data/DisableSid.php | 26 ++++++++----------- .../Controller/Store/SwitchRequestTest.php | 23 +++++++++++----- app/code/Magento/Store/composer.json | 3 ++- 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index af2d20b5fba85..d962e6be64de9 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -8,7 +8,7 @@ namespace Magento\Store\Controller\Store; use Magento\Framework\App\Action\Context; -use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Customer\Model\Session as CustomerSession; @@ -16,11 +16,12 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Customer\Api\CustomerRepositoryInterface; use \Magento\Framework\Exception\LocalizedException; +use Magento\Store\Model\StoreIsInactiveException; /** * Builds correct url to target store and performs redirect. */ -class SwitchRequest extends \Magento\Framework\App\Action\Action +class SwitchRequest extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface { /** * @var StoreRepositoryInterface @@ -78,10 +79,12 @@ public function execute() $error = null; try { - /** @var \Magento\Store\Model\Store $fromStore */ $fromStore = $this->storeRepository->get($fromStoreCode); + $this->storeRepository->getActiveStoreByCode($targetStoreCode); } catch (NoSuchEntityException $e) { $error = __('Requested store is not found.'); + } catch (StoreIsInactiveException $e) { + $error = __('Requested store is inactive.'); } if ($this->validateHash($customerId, $timeStamp, $signature, $fromStoreCode)) { @@ -101,7 +104,8 @@ public function execute() if ($error !== null) { $this->messageManager->addErrorMessage($error); - $this->getResponse()->setRedirect('/'); + //redirect to previous store + $this->getResponse()->setRedirect($fromStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK)); } else { $this->getResponse()->setRedirect("/$targetStoreCode"); } diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 4a8549f299ed0..56a865f8e2fd3 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -13,6 +13,7 @@ use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Authorization\Model\UserContextInterface; /** * Generate one time token and build redirect url @@ -35,19 +36,27 @@ class HashGenerator implements StoreSwitcherInterface */ private $urlHelper; + /** + * @var UserContextInterface + */ + private $currentUser; + /** * @param CustomerSession $customerSession * @param DeploymentConfig $deploymentConfig * @param UrlHelper $urlHelper + * @param UserContextInterface $currentUser */ public function __construct( CustomerSession $customerSession, DeploymentConfig $deploymentConfig, - UrlHelper $urlHelper + UrlHelper $urlHelper, + UserContextInterface $currentUser ) { $this->customerSession = $customerSession; $this->deploymentConfig = $deploymentConfig; - $this->urlHelper=$urlHelper; + $this->urlHelper = $urlHelper; + $this->currentUser = $currentUser; } /** @@ -61,22 +70,27 @@ public function __construct( public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string { $targetUrl = $redirectUrl; - $customerId = $this->customerSession->getId(); + $customerId = null; + + if ($this->currentUser->getUserType() == UserContextInterface::USER_TYPE_CUSTOMER) { + $customerId = $this->currentUser->getUserId(); + } if ($customerId) { + // phpcs:ignore $urlParts = parse_url($targetUrl); $host = $urlParts['host']; $scheme = $urlParts['scheme']; $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); $timeStamp = time(); $fromStoreCode = $fromStore->getCode(); - $targetStoreCode=$targetStore->getCode(); + $targetStoreCode = $targetStore->getCode(); $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); $signature = hash_hmac('sha256', $data, $key); - $targetUrl = $scheme . "://" . $host.'/stores/store/switchrequest'; + $targetUrl = $scheme . "://" . $host . '/stores/store/switchrequest'; $targetUrl = $this->urlHelper->addRequestParam( $targetUrl, - ['customer_id' => $this->customerSession->getId()] + ['customer_id' => $customerId] ); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => time()]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); diff --git a/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php b/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php index 129f5538f5cdc..95df83043f15f 100644 --- a/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php +++ b/app/code/Magento/Store/Setup/Patch/Data/DisableSid.php @@ -11,8 +11,7 @@ use \Magento\Framework\App\Config\MutableScopeConfigInterface; /** - * Class CreateDefaultPages - * @package Magento\Cms\Setup\Patch + * Disable default frontend SID */ class DisableSid implements DataPatchInterface, PatchVersionInterface { @@ -28,34 +27,31 @@ class DisableSid implements DataPatchInterface, PatchVersionInterface private $mutableScopeConfig; /** - * @var string + * scope type */ - protected $_scopeType; + const SCOPE_STORE = 'store'; /** * Disable Sid constructor. + * * @param MutableScopeConfigInterface $mutableScopeConfig - * @param string $scopeType */ public function __construct( - MutableScopeConfigInterface $mutableScopeConfig, - $scopeType + MutableScopeConfigInterface $mutableScopeConfig ) { - $this->mutableScopeConfig=$mutableScopeConfig; - $this->_scopeType=$scopeType; + $this->mutableScopeConfig = $mutableScopeConfig; } /** - * {@inheritdoc} - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @inheritdoc */ public function apply() { - $this->mutableScopeConfig->setValue(self::XML_PATH_USE_FRONTEND_SID, 0, $this->_scopeType); + $this->mutableScopeConfig->setValue(self::XML_PATH_USE_FRONTEND_SID, 0, self::SCOPE_STORE); } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -63,7 +59,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -71,7 +67,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php index a8d4472145deb..ebd85d89c23a9 100644 --- a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php +++ b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php @@ -13,7 +13,7 @@ use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; /** - * Test class for \Magento\Store\Controller\Store\SwitchAction + * Test class for \Magento\Store\Controller\Store\SwitchRequest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SwitchRequestTest extends \PHPUnit\Framework\TestCase @@ -51,7 +51,7 @@ class SwitchRequestTest extends \PHPUnit\Framework\TestCase /** * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject */ - private $storeMock; + private $fromStoreMock; /** * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject @@ -69,7 +69,7 @@ protected function setUp() $this->getMockBuilder(CustomerRepositoryInterface::class)->getMock(); $this->storeRepositoryMock = $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) - ->disableOriginalConstructor()->setMethods(['get'])->getMockForAbstractClass(); + ->disableOriginalConstructor()->setMethods(['get', 'getActiveStoreByCode'])->getMockForAbstractClass(); $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)->getMock(); $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) @@ -78,7 +78,7 @@ protected function setUp() ->getMockForAbstractClass(); $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor()->getMock(); - $this->storeMock = $this->getMockBuilder(StoreInterface::class) + $this->fromStoreMock = $this->getMockBuilder(StoreInterface::class) ->disableOriginalConstructor() ->setMethods(['getBaseUrl']) ->getMockForAbstractClass(); @@ -120,9 +120,20 @@ public function testExecute() ->expects($this->once()) ->method('get') ->with($fromStoreCode) - ->willReturn($this->storeMock); + ->willReturn($this->fromStoreMock); + + $this->storeRepositoryMock + ->expects($this->once()) + ->method('getActiveStoreByCode') + ->with($targetStoreCode); + + $this->fromStoreMock + ->expects($this->once()) + ->method('getBaseUrl') + ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) + ->willReturn($expectedRedirectUrl); $this->responseMock->expects($this->once())->method('setRedirect')->with($expectedRedirectUrl); $this->model->execute(); } -} \ No newline at end of file +} diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index da408f105ccb6..390c6214d59a1 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -12,7 +12,8 @@ "magento/module-config": "*", "magento/module-directory": "*", "magento/module-media-storage": "*", - "magento/module-ui": "*" + "magento/module-ui": "*", + "magento/module-customer":"*" }, "suggest": { "magento/module-deploy": "*" From 25a8d8c0312a6347a47b292cea65c36e0903e849 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 25 Apr 2019 15:39:42 -0500 Subject: [PATCH 0017/2437] MC-15811: Incorrect Url Add composer dependency. Fix Unit test. Fix static failure in controller. --- .../Magento/Store/Controller/Store/SwitchRequest.php | 2 +- .../Store/Model/StoreSwitcher/HashGenerator.php | 10 ---------- app/code/Magento/Store/composer.json | 3 ++- .../testsuite/Magento/Framework/UrlTest.php | 7 ------- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index d962e6be64de9..e243cc912134f 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -132,4 +132,4 @@ private function validateHash(int $customerId, string $timeStamp, string $signat } return false; } -} \ No newline at end of file +} diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 56a865f8e2fd3..be110f965f74c 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -9,7 +9,6 @@ use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreSwitcherInterface; -use Magento\Customer\Model\Session as CustomerSession; use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Framework\Config\ConfigOptionsListConstants; @@ -20,12 +19,6 @@ */ class HashGenerator implements StoreSwitcherInterface { - - /** - * @var \Magento\Customer\Model\Session - */ - private $customerSession; - /** * @var \Magento\Framework\App\DeploymentConfig */ @@ -42,18 +35,15 @@ class HashGenerator implements StoreSwitcherInterface private $currentUser; /** - * @param CustomerSession $customerSession * @param DeploymentConfig $deploymentConfig * @param UrlHelper $urlHelper * @param UserContextInterface $currentUser */ public function __construct( - CustomerSession $customerSession, DeploymentConfig $deploymentConfig, UrlHelper $urlHelper, UserContextInterface $currentUser ) { - $this->customerSession = $customerSession; $this->deploymentConfig = $deploymentConfig; $this->urlHelper = $urlHelper; $this->currentUser = $currentUser; diff --git a/app/code/Magento/Store/composer.json b/app/code/Magento/Store/composer.json index 390c6214d59a1..8af8db8d935a0 100644 --- a/app/code/Magento/Store/composer.json +++ b/app/code/Magento/Store/composer.json @@ -13,7 +13,8 @@ "magento/module-directory": "*", "magento/module-media-storage": "*", "magento/module-ui": "*", - "magento/module-customer":"*" + "magento/module-customer": "*", + "magento/module-authorization": "*" }, "suggest": { "magento/module-deploy": "*" diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index 9d1d8761a16a7..47e814c96d505 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -20,13 +20,6 @@ protected function setUp() $this->model = Bootstrap::getObjectManager()->create(\Magento\Framework\Url::class); } - public function testSetGetUseSession() - { - $this->assertTrue((bool)$this->model->getUseSession()); - $this->model->setUseSession(false); - $this->assertFalse($this->model->getUseSession()); - } - public function testSetRouteFrontName() { $value = 'route'; From be6ade8dcd4a9a156cd8e18d7377baba7e538801 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 25 Apr 2019 16:01:35 -0500 Subject: [PATCH 0018/2437] MC-15901: Recursion in filter widget --- .../Catalog/Model/Category/Attribute/Source/Sortby.php | 3 +-- app/code/Magento/Catalog/Model/Category/DataProvider.php | 3 +++ .../Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php | 6 ++++++ .../Product/Form/Modifier/Data/AssociatedProducts.php | 4 ++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php index f279cc5150d7b..2e152a5696192 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php @@ -49,8 +49,7 @@ public function getAllOptions() foreach ($this->_getCatalogConfig()->getAttributesUsedForSortBy() as $attribute) { $this->_options[] = [ 'label' => __($attribute['frontend_label']), - 'value' => $attribute['attribute_code'], - '__disableTmpl' => true + 'value' => $attribute['attribute_code'] ]; } } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index a4127c9a97ffd..d3c4bfb8494bd 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -340,6 +340,9 @@ public function getAttributesMeta(Type $entityType) } if ($attribute->usesSource()) { $meta[$code]['options'] = $attribute->getSource()->getAllOptions(); + foreach ($meta[$code]['options'] as &$option) { + $option['__disableTmpl'] = true; + } } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 91a09c907de65..ddaa520d7f75f 100755 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -563,6 +563,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], ], 'default_null_prod_not_new_locked_and_required' => [ @@ -582,6 +583,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], 'locked' => true, ], @@ -602,6 +604,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], ], 'default_null_prod_new_and_not_required' => [ @@ -621,6 +624,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], ], 'default_null_prod_new_locked_and_not_required' => [ @@ -640,6 +644,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], 'locked' => true, ], @@ -660,6 +665,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => true ], ] ]; diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php index 45a48a37e5ceb..e98da6b6e4258 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/Data/AssociatedProducts.php @@ -21,6 +21,8 @@ use Magento\Framework\Escaper; /** + * Loads data for product configurations. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AssociatedProducts @@ -232,6 +234,7 @@ public function getConfigurableAttributesData() * * @return void * @throws \Zend_Currency_Exception + * phpcs:disable Generic.Metrics.NestingLevel.TooHigh */ protected function prepareVariations() { @@ -321,6 +324,7 @@ protected function prepareVariations() $this->productIds = $productIds; $this->productAttributes = array_values($attributes); } + //phpcs: enable /** * Get JSON string that contains attribute code and value From 2d924eff1fff9fdc3e16511a99d8806595fd1340 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 25 Apr 2019 17:53:08 -0500 Subject: [PATCH 0019/2437] MC-15901: Recursion in filter widget --- .../Catalog/Model/Category/Attribute/Source/Sortby.php | 2 +- app/code/Magento/Catalog/Model/Category/DataProvider.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php index 2e152a5696192..4dda2fe5786e3 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/Sortby.php @@ -40,7 +40,7 @@ protected function _getCatalogConfig() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAllOptions() { diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index d3c4bfb8494bd..8db6ab8c228ca 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -210,6 +210,8 @@ public function getMeta() } /** + * Add 'use default checkbox' to attributes that can have it. + * * @param Category $category * @param array $meta * @return array @@ -501,6 +503,7 @@ private function convertValues($category, $categoryData) $stat = $fileInfo->getStat($fileName); $mime = $fileInfo->getMimeType($fileName); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $categoryData[$attributeCode][0]['name'] = basename($fileName); if ($fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { @@ -536,6 +539,8 @@ public function getDefaultMetaData($result) } /** + * List form field sets and fields. + * * @return array * @since 101.0.0 */ From 045f5520373222ea49e21a0bc7ef9d52124a18d1 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 25 Apr 2019 18:43:49 -0500 Subject: [PATCH 0020/2437] MC-15811: Incorrect Url Add class annotation. --- dev/tests/integration/testsuite/Magento/Framework/UrlTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index 47e814c96d505..21f0ebd5b77fc 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -8,6 +8,9 @@ use Zend\Stdlib\Parameters; use Magento\TestFramework\Helper\Bootstrap; +/** + * Test class for \Magento\Framework\Url + */ class UrlTest extends \PHPUnit\Framework\TestCase { /** From 38f2f3ad7451fb73b2ffc5e6be179840d8e9445f Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 26 Apr 2019 11:48:28 -0500 Subject: [PATCH 0021/2437] MC-15901: Recursion in filter widget --- .../DataProvider/Product/Form/Modifier/EavTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index ddaa520d7f75f..599d10c24027c 100755 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -563,7 +563,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], ], 'default_null_prod_not_new_locked_and_required' => [ @@ -583,7 +583,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], 'locked' => true, ], @@ -604,7 +604,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], ], 'default_null_prod_new_and_not_required' => [ @@ -624,7 +624,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], ], 'default_null_prod_new_locked_and_not_required' => [ @@ -644,7 +644,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], 'locked' => true, ], @@ -665,7 +665,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, - '__disableTmpl' => true + '__disableTmpl' => ['label' => true, 'code' => true] ], ] ]; From 2e9c607c12f2c065a74c4390cdaf23e9c5ff2485 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Apr 2019 12:20:23 -0500 Subject: [PATCH 0022/2437] MC-15901: Recursion in filter widget --- .../app/Magento/Catalog/Test/Handler/Category/Curl.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php index 5c54b366b7ab4..fb9d6e9772c74 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php @@ -251,8 +251,13 @@ protected function getBlockId($landingName) $curl->write($url, [], CurlInterface::GET); $response = $curl->read(); $curl->close(); - preg_match('~\{"value":"(\d+)","label":"' . preg_quote($landingName) . '"\}~', $response, $matches); - $id = isset($matches[1]) ? (int)$matches[1] : null; + $id = null; + //Finding block option in 'Add block' options UI data. + preg_match('~\{[^\{\}]*?"label":"' . preg_quote($landingName) . '"[^\{\}]*?\}~', $response, $matches); + if (!empty($matches)) { + $blockOption = json_decode($matches[0], true); + $id = (int)$blockOption['value']; + } return $id; } From b5cb8f12faf14a013477bb4c164a336324a962c5 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 29 Apr 2019 15:47:46 -0500 Subject: [PATCH 0023/2437] MC-16045: Update symfony/http-foundation --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index 06ab71ee75970..96c36a8b7cf34 100644 --- a/composer.lock +++ b/composer.lock @@ -8983,16 +8983,16 @@ }, { "name": "symfony/http-foundation", - "version": "v4.2.4", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "850a667d6254ccf6c61d853407b16f21c4579c77" + "reference": "6ebbe61f48069033225c9d3fa7eb5ed116d766d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/850a667d6254ccf6c61d853407b16f21c4579c77", - "reference": "850a667d6254ccf6c61d853407b16f21c4579c77", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6ebbe61f48069033225c9d3fa7eb5ed116d766d6", + "reference": "6ebbe61f48069033225c9d3fa7eb5ed116d766d6", "shasum": "" }, "require": { @@ -9033,7 +9033,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-02-26T08:03:39+00:00" + "time": "2019-04-17T14:56:00+00:00" }, { "name": "symfony/options-resolver", From 7c86433a2202eb1002dd29f423962ee5e6b95b94 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 30 Apr 2019 00:27:17 +0300 Subject: [PATCH 0024/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Implemented validation URL key during category creation --- .../CategoryUrlPathAutogeneratorObserver.php | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 713dd6ac0c736..95ddceed6ec8f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -3,13 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Category; use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Framework\Event\Observer; use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\Store; @@ -19,6 +20,14 @@ */ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface { + + /** + * Reserved endpoint names. + * + * @var array + */ + private $invalidValues = ['admin', 'soap', 'rest', 'graphql']; + /** * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator */ @@ -72,7 +81,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($category->getUrlKey() !== false && !$useDefaultAttribute) { $resultUrlKey = $this->categoryUrlPathGenerator->getUrlKey($category); $this->updateUrlKey($category, $resultUrlKey); - } else if ($useDefaultAttribute) { + } elseif ($useDefaultAttribute) { $resultUrlKey = $category->formatUrlKey($category->getOrigData('name')); $this->updateUrlKey($category, $resultUrlKey); $category->setUrlKey(null)->setUrlPath(null); @@ -92,6 +101,17 @@ private function updateUrlKey($category, $urlKey) if (empty($urlKey)) { throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key')); } + + if (in_array($urlKey, $this->invalidValues)) { + throw new \Magento\Framework\Exception\LocalizedException( + __( + 'URL key "%1" conflicts with reserved endpoint names: %2. Try another url key.', + $urlKey, + implode(', ', $this->invalidValues) + ) + ); + } + $category->setUrlKey($urlKey) ->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category)); if (!$category->isObjectNew()) { From 489e329de82838b469ca532eab6ab2168b5433d8 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Tue, 30 Apr 2019 13:23:51 +0300 Subject: [PATCH 0025/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Added error processing --- .../Model/Order/Shipment/TrackRepository.php | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index 5bcf579a1cbf4..b9911518ad58c 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Order\Shipment; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; @@ -14,7 +16,12 @@ use Magento\Sales\Api\Data\ShipmentTrackSearchResultInterfaceFactory; use Magento\Sales\Api\ShipmentTrackRepositoryInterface; use Magento\Sales\Model\Spi\ShipmentTrackResourceInterface; +use \Magento\Sales\Model\OrderRepository; +use \Magento\Framework\App\ObjectManager; +/** + * Repository of shipment tracking information + */ class TrackRepository implements ShipmentTrackRepositoryInterface { /** @@ -37,23 +44,32 @@ class TrackRepository implements ShipmentTrackRepositoryInterface */ private $collectionProcessor; + /** + * @var OrderRepository + */ + private $orderRepository; + /** * @param ShipmentTrackResourceInterface $trackResource * @param ShipmentTrackInterfaceFactory $trackFactory * @param ShipmentTrackSearchResultInterfaceFactory $searchResultFactory * @param CollectionProcessorInterface $collectionProcessor + * @param OrderRepository $orderRepository */ public function __construct( ShipmentTrackResourceInterface $trackResource, ShipmentTrackInterfaceFactory $trackFactory, ShipmentTrackSearchResultInterfaceFactory $searchResultFactory, - CollectionProcessorInterface $collectionProcessor + CollectionProcessorInterface $collectionProcessor, + OrderRepository $orderRepository = null ) { $this->trackResource = $trackResource; $this->trackFactory = $trackFactory; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor; + $this->orderRepository = $orderRepository ?: + ObjectManager::getInstance()->get(OrderRepository::class); } /** @@ -95,6 +111,16 @@ public function delete(ShipmentTrackInterface $entity) */ public function save(ShipmentTrackInterface $entity) { + $shipmentCollection = $this->orderRepository->get($entity['order_id'])->getShipmentsCollection(); + $shipmentId = []; + foreach ($shipmentCollection as $shipment) { + $shipmentId[] = $shipment->getId(); + } + + if (array_search($entity['parent_id'], $shipmentId) === false) { + throw new CouldNotSaveException(__('The shipment doesn\'t belong to the order.')); + } + try { $this->trackResource->save($entity); } catch (\Exception $e) { From 9672ace6359e41fd39825d732d747e3d9c149db0 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 1 May 2019 09:55:22 -0500 Subject: [PATCH 0026/2437] MC-15811: Incorrect Url Add integration test for token validator. Refactor validation logic from controller. --- .../Store/Controller/Store/SwitchRequest.php | 43 ++---- .../Model/StoreSwitcher/HashGenerator.php | 25 +++- .../Magento/Store/Model/HashGeneratorTest.php | 137 ++++++++++++++++++ 3 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index e243cc912134f..6d97487df67b8 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -13,7 +13,7 @@ use Magento\Store\Api\StoreRepositoryInterface; use Magento\Customer\Model\Session as CustomerSession; use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; -use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Store\Model\StoreSwitcher\HashGenerator; use Magento\Customer\Api\CustomerRepositoryInterface; use \Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\StoreIsInactiveException; @@ -43,25 +43,33 @@ class SwitchRequest extends \Magento\Framework\App\Action\Action implements Http */ private $customerRepository; + /** + * @var HashGenerator + */ + private $hashGenerator; + /** * @param Context $context * @param StoreRepositoryInterface $storeRepository * @param CustomerSession $session * @param DeploymentConfig $deploymentConfig * @param CustomerRepositoryInterface $customerRepository + * @param HashGenerator $hashGenerator */ public function __construct( Context $context, StoreRepositoryInterface $storeRepository, CustomerSession $session, DeploymentConfig $deploymentConfig, - CustomerRepositoryInterface $customerRepository + CustomerRepositoryInterface $customerRepository, + HashGenerator $hashGenerator ) { parent::__construct($context); $this->storeRepository = $storeRepository; $this->customerSession = $session; $this->deploymentConfig = $deploymentConfig; - $this->customerRepository=$customerRepository; + $this->customerRepository = $customerRepository; + $this->hashGenerator = $hashGenerator; } /** @@ -80,14 +88,15 @@ public function execute() try { $fromStore = $this->storeRepository->get($fromStoreCode); - $this->storeRepository->getActiveStoreByCode($targetStoreCode); + $targetStore=$this->storeRepository->getActiveStoreByCode($targetStoreCode); + $targetUrl=$targetStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); } catch (NoSuchEntityException $e) { $error = __('Requested store is not found.'); } catch (StoreIsInactiveException $e) { $error = __('Requested store is inactive.'); } - if ($this->validateHash($customerId, $timeStamp, $signature, $fromStoreCode)) { + if ($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])) { try { $customer = $this->customerRepository->getById($customerId); if (!$this->customerSession->isLoggedIn()) { @@ -107,29 +116,7 @@ public function execute() //redirect to previous store $this->getResponse()->setRedirect($fromStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK)); } else { - $this->getResponse()->setRedirect("/$targetStoreCode"); - } - } - - /** - * Validates one time token - * - * @param int $customerId - * @param string $timeStamp - * @param string $signature - * @param string $fromStoreCode - * @return bool - */ - private function validateHash(int $customerId, string $timeStamp, string $signature, string $fromStoreCode): bool - { - - if ($customerId && $timeStamp && $signature) { - $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); - $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); - if (time() - $timeStamp <= 5 && hash_equals($signature, hash_hmac('sha256', $data, $key))) { - return true; - } + $this->getResponse()->setRedirect($targetUrl); } - return false; } } diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index be110f965f74c..0e0e77b04f94f 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -71,6 +71,7 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s $urlParts = parse_url($targetUrl); $host = $urlParts['host']; $scheme = $urlParts['scheme']; + $path=$urlParts['path']; $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); $timeStamp = time(); $fromStoreCode = $fromStore->getCode(); @@ -82,11 +83,33 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s $targetUrl, ['customer_id' => $customerId] ); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => time()]); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => $timeStamp]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___from_store' => $fromStoreCode]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___to_store' => $targetStoreCode]); + $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['path' => $path]); } return $targetUrl; } + + /** + * Validates one time token + * + * @param string $signature + * @param array $data + * @return bool + */ + public function validateHash(string $signature, array $data): bool + { + if (!empty($signature) && !empty($data)) { + $timeStamp = $data[1] ?? 0; + $value = implode(",", $data); + $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + + if (time() - $timeStamp <= 5 && hash_equals($signature, hash_hmac('sha256', $value, $key))) { + return true; + } + } + return false; + } } diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php new file mode 100644 index 0000000000000..7b5bb357b9345 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Model; + +use Magento\Framework\ObjectManagerInterface as ObjectManager; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Store\Model\StoreSwitcher\HashGenerator; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\Session; +use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; + +/** + * Test class for \Magento\Store\Model\StoreSwitcher\HashGenerator + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ + +class HashGeneratorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var HashGenerator + */ + private $hashGenerator; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * + */ + private $customerId; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** + * @var \Magento\Customer\Model\Authorization\CustomerSessionUserContext + */ + private $customerSessionUserContext; + + /** + * @var \Magento\Framework\App\DeploymentConfig + */ + private $deploymentConfig; + + /** + * + */ + private $key; + + /** + * Class dependencies initialization + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $session = $this->objectManager->create( + Session::class + ); + $this->accountManagement = $this->objectManager->create(AccountManagementInterface::class); + $customer = $this->accountManagement->authenticate('customer@example.com', 'password'); + $session->setCustomerDataAsLoggedIn($customer); + $this->customerSessionUserContext = $this->objectManager->create( + \Magento\Customer\Model\Authorization\CustomerSessionUserContext::class, + ['customerSession' => $session] + ); + $this->hashGenerator = $this->objectManager->create( + StoreSwitcher\HashGenerator::class, + ['currentUser' => $this->customerSessionUserContext] + ); + $this->customerId = $customer->getId(); + $this->deploymentConfig = $this->objectManager->get(DeploymentConfig::class); + $this->key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + } + + /** + * @magentoDataFixture Magento/Store/_files/store.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSwitch(): void + { + $redirectUrl = "http://domain.com/"; + $fromStoreCode = 'test'; + $toStoreCode = 'fixture_second_store'; + /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ + $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); + $fromStore = $storeRepository->get($fromStoreCode); + $toStore = $storeRepository->get($toStoreCode); + $timeStamp = time(); + $data = implode(',', [$this->customerId, $timeStamp, $fromStoreCode]); + $signature = hash_hmac('sha256', $data, $this->key); + $customerId = $this->customerId; + + $expectedUrl = "http://domain.com/stores/store/switchrequest?customer_id=$customerId"; + $expectedUrl .= "&time_stamp=$timeStamp&signature=$signature"; + $expectedUrl .= "&___from_store=$fromStoreCode&___to_store=$toStoreCode"; + $this->assertEquals($expectedUrl, $this->hashGenerator->switch($fromStore, $toStore, $redirectUrl)); + } + + /** + * @return void + */ + public function testValidateHashWithCorrectData(): void + { + $timeStamp = time(); + $customerId = $this->customerId; + $fromStoreCode = 'test'; + $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); + $signature = hash_hmac('sha256', $data, $this->key); + $this->assertTrue($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])); + } + + /** + * @return void + */ + public function testValidateHashWithInCorrectData(): void + { + $timeStamp = 0; + $customerId = 8; + $fromStoreCode = 'test'; + $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); + $signature = hash_hmac('sha256', $data, $this->key); + $this->assertFalse($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])); + } +} From b4562e0a0d738c65818f4a8bb5a194fa275ee3a0 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Wed, 1 May 2019 15:51:31 -0500 Subject: [PATCH 0027/2437] MC-15975: Fixed incorrect behavior of template variables --- lib/internal/Magento/Framework/Filter/Template.php | 3 ++- .../Magento/Framework/Filter/Test/Unit/TemplateTest.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 6a04e8e8c6953..38198bb9a717b 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -81,7 +81,8 @@ class Template implements \Zend_Filter_Interface 'settemplateprocessor', 'gettemplateprocessor', 'vardirective', - 'delete' + 'delete', + 'getdatausingmethod' ]; /** diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php index 0ee3a06ce5420..b7f76cb35953a 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TemplateTest.php @@ -445,7 +445,8 @@ public function disallowedMethods() ['setTemplateProcessor'], ['getTemplateProcessor'], ['varDirective'], - ['delete'] + ['delete'], + ['getDataUsingMethod'] ]; } } From 02babd14eaf25752c8bef5949773c09a0f3ebcc0 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Thu, 2 May 2019 11:19:47 +0300 Subject: [PATCH 0028/2437] MC-13886: Downloadable Product controller|API save changes --- .../Helper/Plugin/Downloadable.php | 81 +++++++++++++++- .../Downloadable/Model/Link/Builder.php | 13 ++- .../Model/Link/ContentValidator.php | 80 +++++++++++----- .../Downloadable/Model/LinkRepository.php | 96 ++++++++++++++----- .../Model/Sample/ContentValidator.php | 55 ++++++++--- .../Downloadable/Model/SampleRepository.php | 38 ++++---- .../Helper/Plugin/DownloadableTest.php | 19 +++- .../Unit/Model/Link/ContentValidatorTest.php | 25 ++++- .../Model/Sample/ContentValidatorTest.php | 25 ++++- .../Product/Form/Modifier/Data/Links.php | 20 +++- .../Product/Form/Modifier/Data/Samples.php | 18 +++- .../Downloadable/Api/LinkRepositoryTest.php | 69 +++++++++++++ .../Api/ProductRepositoryTest.php | 5 +- .../Downloadable/Api/SampleRepositoryTest.php | 31 ++++++ 14 files changed, 484 insertions(+), 91 deletions(-) diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php index a283891afc406..f310b376633d3 100644 --- a/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php +++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Product/Initialization/Helper/Plugin/Downloadable.php @@ -5,11 +5,14 @@ */ namespace Magento\Downloadable\Controller\Adminhtml\Product\Initialization\Helper\Plugin; -use Magento\Framework\App\RequestInterface; +use Magento\Downloadable\Api\Data\LinkInterfaceFactory; +use Magento\Downloadable\Api\Data\SampleInterfaceFactory; +use Magento\Downloadable\Helper\Download; use Magento\Downloadable\Model\Link\Builder as LinkBuilder; +use Magento\Downloadable\Model\Product\Type; +use Magento\Downloadable\Model\ResourceModel\Sample\Collection; use Magento\Downloadable\Model\Sample\Builder as SampleBuilder; -use Magento\Downloadable\Api\Data\SampleInterfaceFactory; -use Magento\Downloadable\Api\Data\LinkInterfaceFactory; +use Magento\Framework\App\RequestInterface; /** * Class for initialization downloadable info from request. @@ -42,8 +45,6 @@ class Downloadable private $linkBuilder; /** - * Constructor - * * @param RequestInterface $request * @param LinkBuilder $linkBuilder * @param SampleBuilder $sampleBuilder @@ -79,14 +80,18 @@ public function afterInitialize( \Magento\Catalog\Model\Product $product ) { if ($downloadable = $this->request->getPost('downloadable')) { + $product->setTypeId(Type::TYPE_DOWNLOADABLE); $product->setDownloadableData($downloadable); $extension = $product->getExtensionAttributes(); + $productLinks = $product->getTypeInstance()->getLinks($product); + $productSamples = $product->getTypeInstance()->getSamples($product); if (isset($downloadable['link']) && is_array($downloadable['link'])) { $links = []; foreach ($downloadable['link'] as $linkData) { if (!$linkData || (isset($linkData['is_delete']) && $linkData['is_delete'])) { continue; } else { + $linkData = $this->processLink($linkData, $productLinks); $links[] = $this->linkBuilder->setData( $linkData )->build( @@ -104,6 +109,7 @@ public function afterInitialize( if (!$sampleData || (isset($sampleData['is_delete']) && (bool)$sampleData['is_delete'])) { continue; } else { + $sampleData = $this->processSample($sampleData, $productSamples); $samples[] = $this->sampleBuilder->setData( $sampleData )->build( @@ -124,4 +130,69 @@ public function afterInitialize( } return $product; } + + /** + * Check Links type and status. + * + * @param array $linkData + * @param array $productLinks + * @return array + */ + private function processLink(array $linkData, array $productLinks): array + { + $linkId = $linkData['link_id'] ?? null; + if ($linkId && isset($productLinks[$linkId])) { + $linkData = $this->processFileStatus($linkData, $productLinks[$linkId]->getLinkFile()); + $linkData['sample'] = $this->processFileStatus( + $linkData['sample'] ?? [], + $productLinks[$linkId]->getSampleFile() + ); + } else { + $linkData = $this->processFileStatus($linkData, null); + $linkData['sample'] = $this->processFileStatus($linkData['sample'] ?? [], null); + } + + return $linkData; + } + + /** + * Check Sample type and status. + * + * @param array $sampleData + * @param Collection $productSamples + * @return array + */ + private function processSample(array $sampleData, Collection $productSamples): array + { + $sampleId = $sampleData['sample_id'] ?? null; + /** @var \Magento\Downloadable\Model\Sample $productSample */ + $productSample = $sampleId ? $productSamples->getItemById($sampleId) : null; + if ($sampleId && $productSample) { + $sampleData = $this->processFileStatus($sampleData, $productSample->getSampleFile()); + } else { + $sampleData = $this->processFileStatus($sampleData, null); + } + + return $sampleData; + } + + /** + * Compare file path from request with DB and set status. + * + * @param array $data + * @param string|null $file + * @return array + */ + private function processFileStatus(array $data, ?string $file): array + { + if (isset($data['type']) && $data['type'] === Download::LINK_TYPE_FILE && isset($data['file']['0']['file'])) { + if ($data['file'][0]['file'] !== $file) { + $data['file'][0]['status'] = 'new'; + } else { + $data['file'][0]['status'] = 'old'; + } + } + + return $data; + } } diff --git a/app/code/Magento/Downloadable/Model/Link/Builder.php b/app/code/Magento/Downloadable/Model/Link/Builder.php index 83d01f76fe9cd..ff76f7eeda440 100644 --- a/app/code/Magento/Downloadable/Model/Link/Builder.php +++ b/app/code/Magento/Downloadable/Model/Link/Builder.php @@ -69,6 +69,8 @@ public function __construct( } /** + * Set Data. + * * @param array $data * @return $this * @since 100.1.0 @@ -80,6 +82,8 @@ public function setData(array $data) } /** + * Build correct data structure. + * * @param \Magento\Downloadable\Api\Data\LinkInterface $link * @return \Magento\Downloadable\Api\Data\LinkInterface * @throws \Magento\Framework\Exception\LocalizedException @@ -134,6 +138,8 @@ public function build(\Magento\Downloadable\Api\Data\LinkInterface $link) } /** + * Reset data. + * * @return void */ private function resetData() @@ -142,6 +148,8 @@ private function resetData() } /** + * Get existing component or create new. + * * @return Link */ private function getComponent() @@ -153,6 +161,8 @@ private function getComponent() } /** + * Build correct sample structure. + * * @param \Magento\Downloadable\Api\Data\LinkInterface $link * @param array $sample * @return \Magento\Downloadable\Api\Data\LinkInterface @@ -174,7 +184,8 @@ private function buildSample(\Magento\Downloadable\Api\Data\LinkInterface $link, ), \Magento\Downloadable\Api\Data\LinkInterface::class ); - if ($link->getSampleType() === \Magento\Downloadable\Helper\Download::LINK_TYPE_FILE) { + if ($link->getSampleType() === \Magento\Downloadable\Helper\Download::LINK_TYPE_FILE + && isset($sample['file'])) { $linkSampleFileName = $this->downloadableFile->moveFileFromTmp( $this->getComponent()->getBaseSampleTmpPath(), $this->getComponent()->getBaseSamplePath(), diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php index 088356caefad0..8497bf7de6592 100644 --- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php @@ -6,10 +6,16 @@ namespace Magento\Downloadable\Model\Link; use Magento\Downloadable\Api\Data\LinkInterface; +use Magento\Downloadable\Helper\File; use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator; use Magento\Framework\Exception\InputException; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Url\Validator as UrlValidator; +/** + * Class to validate Link Content. + */ class ContentValidator { /** @@ -22,20 +28,28 @@ class ContentValidator */ protected $urlValidator; + /** + * @var File + */ + private $fileHelper; + /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator + * @param File|null $fileHelper */ public function __construct( FileContentValidator $fileContentValidator, - UrlValidator $urlValidator + UrlValidator $urlValidator, + File $fileHelper = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; + $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); } /** - * Check if link content is valid + * Check if link content is valid. * * @param LinkInterface $link * @param bool $validateLinkContent @@ -63,50 +77,66 @@ public function isValid(LinkInterface $link, $validateLinkContent = true, $valid if ($validateSampleContent) { $this->validateSampleResource($link); } + return true; } /** - * Validate link resource (file or URL) + * Validate link resource (file or URL). * * @param LinkInterface $link - * @throws InputException * @return void + * @throws InputException */ protected function validateLinkResource(LinkInterface $link) { - if ($link->getLinkType() == 'url' - && !$this->urlValidator->isValid($link->getLinkUrl()) - ) { - throw new InputException(__('Link URL must have valid format.')); - } - if ($link->getLinkType() == 'file' - && (!$link->getLinkFileContent() - || !$this->fileContentValidator->isValid($link->getLinkFileContent())) - ) { - throw new InputException(__('Provided file content must be valid base64 encoded data.')); + if ($link->getLinkType() === 'url') { + if (!$this->urlValidator->isValid($link->getLinkUrl())) { + throw new InputException(__('Link URL must have valid format.')); + } + } elseif ($link->getLinkFileContent()) { + if (!$this->fileContentValidator->isValid($link->getLinkFileContent())) { + throw new InputException(__('Provided file content must be valid base64 encoded data.')); + } + } elseif (!$this->isFileValid($link->getBasePath() . $link->getLinkFile())) { + throw new InputException(__('Link file not found. Please try again.')); } } /** - * Validate sample resource (file or URL) + * Validate sample resource (file or URL). * * @param LinkInterface $link - * @throws InputException * @return void + * @throws InputException */ protected function validateSampleResource(LinkInterface $link) { - if ($link->getSampleType() == 'url' - && !$this->urlValidator->isValid($link->getSampleUrl()) - ) { - throw new InputException(__('Sample URL must have valid format.')); + if ($link->getSampleType() === 'url') { + if (!$this->urlValidator->isValid($link->getSampleUrl())) { + throw new InputException(__('Sample URL must have valid format.')); + } + } elseif ($link->getSampleFileContent()) { + if (!$this->fileContentValidator->isValid($link->getSampleFileContent())) { + throw new InputException(__('Provided file content must be valid base64 encoded data.')); + } + } elseif (!$this->isFileValid($link->getBaseSamplePath() . $link->getSampleFile())) { + throw new InputException(__('Link sample file not found. Please try again.')); } - if ($link->getSampleType() == 'file' - && (!$link->getSampleFileContent() - || !$this->fileContentValidator->isValid($link->getSampleFileContent())) - ) { - throw new InputException(__('Provided file content must be valid base64 encoded data.')); + } + + /** + * Check that Links File or Sample is valid. + * + * @param string $file + * @return bool + */ + private function isFileValid(string $file): bool + { + try { + return $this->fileHelper->ensureFileInFilesystem($file); + } catch (ValidatorException $e) { + return false; } } } diff --git a/app/code/Magento/Downloadable/Model/LinkRepository.php b/app/code/Magento/Downloadable/Model/LinkRepository.php index 0898f1924e538..57e0fee5c1181 100644 --- a/app/code/Magento/Downloadable/Model/LinkRepository.php +++ b/app/code/Magento/Downloadable/Model/LinkRepository.php @@ -97,7 +97,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getList($sku) { @@ -107,6 +107,8 @@ public function getList($sku) } /** + * @inheritdoc + * * @param \Magento\Catalog\Api\Data\ProductInterface $product * @return array */ @@ -166,9 +168,11 @@ protected function setBasicFields($resourceData, $dataObject) } /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws InputException */ public function save($sku, LinkInterface $link, $isGlobalScopeContent = true) { @@ -181,24 +185,23 @@ public function save($sku, LinkInterface $link, $isGlobalScopeContent = true) __('The product needs to be the downloadable type. Verify the product and try again.') ); } - $validateLinkContent = !($link->getLinkType() === 'file' && $link->getLinkFile()); - $validateSampleContent = !($link->getSampleType() === 'file' && $link->getSampleFile()); - if (!$this->contentValidator->isValid($link, $validateLinkContent, $validateSampleContent)) { + $this->validateLinkType($link); + $validateSampleContent = $this->isValidateSample($link); + if (!$this->contentValidator->isValid($link, true, $validateSampleContent)) { throw new InputException(__('The link information is invalid. Verify the link and try again.')); } - - if (!in_array($link->getLinkType(), ['url', 'file'], true)) { - throw new InputException(__('The link type is invalid. Verify and try again.')); - } $title = $link->getTitle(); if (empty($title)) { throw new InputException(__('The link title is empty. Enter the link title and try again.')); } + return $this->saveLink($product, $link, $isGlobalScopeContent); } } /** + * Construct Data structure and Save it. + * * @param \Magento\Catalog\Api\Data\ProductInterface $product * @param LinkInterface $link * @param bool $isGlobalScopeContent @@ -220,7 +223,7 @@ protected function saveLink( 'is_shareable' => $link->getIsShareable(), ]; - if ($link->getLinkType() == 'file' && $link->getLinkFile() === null) { + if ($link->getLinkType() == 'file' && $link->getLinkFileContent() !== null) { $linkData['file'] = $this->jsonEncoder->encode( [ $this->fileContentUploader->upload($link->getLinkFileContent(), 'link_file'), @@ -242,7 +245,7 @@ protected function saveLink( if ($link->getSampleType() == 'file') { $linkData['sample']['type'] = 'file'; - if ($link->getSampleFile() === null) { + if ($link->getSampleFileContent() !== null) { $fileData = [ $this->fileContentUploader->upload($link->getSampleFileContent(), 'link_sample_file'), ]; @@ -269,6 +272,8 @@ protected function saveLink( } /** + * Update existing Link. + * * @param \Magento\Catalog\Api\Data\ProductInterface $product * @param LinkInterface $link * @param bool $isGlobalScopeContent @@ -298,9 +303,9 @@ protected function updateLink( __("The downloadable link isn't related to the product. Verify the link and try again.") ); } - $validateLinkContent = !($link->getLinkFileContent() === null); - $validateSampleContent = !($link->getSampleFileContent() === null); - if (!$this->contentValidator->isValid($link, $validateLinkContent, $validateSampleContent)) { + $this->validateLinkType($link); + $validateSampleContent = $this->isValidateSample($link); + if (!$this->contentValidator->isValid($link, true, $validateSampleContent)) { throw new InputException(__('The link information is invalid. Verify the link and try again.')); } if ($isGlobalScopeContent) { @@ -312,20 +317,16 @@ protected function updateLink( throw new InputException(__('The link title is empty. Enter the link title and try again.')); } } - - if ($link->getLinkType() == 'file' && $link->getLinkFileContent() === null && !$link->getLinkFile()) { - $link->setLinkFile($existingLink->getLinkFile()); - } - if ($link->getSampleType() == 'file' && $link->getSampleFileContent() === null && !$link->getSampleFile()) { - $link->setSampleFile($existingLink->getSampleFile()); + if (!$validateSampleContent) { + $this->resetLinkSampleContent($link, $existingLink); } - $this->saveLink($product, $link, $isGlobalScopeContent); + return $existingLink->getId(); } /** - * {@inheritdoc} + * @inheritdoc */ public function delete($id) { @@ -344,6 +345,57 @@ public function delete($id) return true; } + /** + * Check that Link type exist. + * + * @param LinkInterface $link + * @throws \Magento\Framework\Exception\InputException + */ + private function validateLinkType(LinkInterface $link): void + { + if (!in_array($link->getLinkType(), ['url', 'file'], true)) { + throw new InputException(__('The link type is invalid. Verify and try again.')); + } + } + + /** + * Check that Link sample type exist. + * + * @param \Magento\Downloadable\Api\Data\LinkInterface $link + * @return bool + * @throws \Magento\Framework\Exception\InputException + */ + private function isValidateSample(LinkInterface $link): bool + { + if ($link->hasSampleType()) { + if (in_array($link->getSampleType(), ['url', 'file'], true)) { + return true; + } else { + throw new InputException(__('The link sample type is invalid. Verify and try again.')); + } + } + + return false; + } + + /** + * Reset Sample type and file. + * + * @param LinkInterface $link + * @param LinkInterface $existingLink + * @return void + */ + private function resetLinkSampleContent(LinkInterface $link, LinkInterface $existingLink): void + { + $existingType = $existingLink->getSampleType(); + $link->setSampleType($existingType); + if ($existingType === 'file') { + $link->setSampleFile($existingLink->getSampleFile()); + } else { + $link->setSampleUrl($existingLink->getSampleUrl()); + } + } + /** * Get MetadataPool instance * diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php index 6a273bfe5d34e..7348b04793a8f 100644 --- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php @@ -6,10 +6,16 @@ namespace Magento\Downloadable\Model\Sample; use Magento\Downloadable\Api\Data\SampleInterface; +use Magento\Downloadable\Helper\File; use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Url\Validator as UrlValidator; +/** + * Class to validate Sample Content. + */ class ContentValidator { /** @@ -22,20 +28,28 @@ class ContentValidator */ protected $fileContentValidator; + /** + * @var File + */ + private $fileHelper; + /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator + * @param File|null $fileHelper */ public function __construct( FileContentValidator $fileContentValidator, - UrlValidator $urlValidator + UrlValidator $urlValidator, + File $fileHelper = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; + $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); } /** - * Check if sample content is valid + * Check if sample content is valid. * * @param SampleInterface $sample * @param bool $validateSampleContent @@ -51,29 +65,44 @@ public function isValid(SampleInterface $sample, $validateSampleContent = true) if ($validateSampleContent) { $this->validateSampleResource($sample); } + return true; } /** - * Validate sample resource (file or URL) + * Validate sample resource (file or URL). * * @param SampleInterface $sample - * @throws InputException * @return void + * @throws InputException */ protected function validateSampleResource(SampleInterface $sample) { - $sampleFile = $sample->getSampleFileContent(); - if ($sample->getSampleType() == 'file' - && (!$sampleFile || !$this->fileContentValidator->isValid($sampleFile)) - ) { - throw new InputException(__('Provided file content must be valid base64 encoded data.')); + if ($sample->getSampleType() === 'url') { + if (!$this->urlValidator->isValid($sample->getSampleUrl())) { + throw new InputException(__('Sample URL must have valid format.')); + } + } elseif ($sample->getSampleFileContent()) { + if (!$this->fileContentValidator->isValid($sample->getSampleFileContent())) { + throw new InputException(__('Provided file content must be valid base64 encoded data.')); + } + } elseif (!$this->isFileValid($sample->getBasePath() . $sample->getSampleFile())) { + throw new InputException(__('Sample file not found. Please try again.')); } + } - if ($sample->getSampleType() == 'url' - && !$this->urlValidator->isValid($sample->getSampleUrl()) - ) { - throw new InputException(__('Sample URL must have valid format.')); + /** + * Check that Samples file is valid. + * + * @param string $file + * @return bool + */ + private function isFileValid(string $file): bool + { + try { + return $this->fileHelper->ensureFileInFilesystem($file); + } catch (ValidatorException $e) { + return false; } } } diff --git a/app/code/Magento/Downloadable/Model/SampleRepository.php b/app/code/Magento/Downloadable/Model/SampleRepository.php index 07c7631fade13..37f376e666243 100644 --- a/app/code/Magento/Downloadable/Model/SampleRepository.php +++ b/app/code/Magento/Downloadable/Model/SampleRepository.php @@ -189,17 +189,12 @@ public function save( __('The product needs to be the downloadable type. Verify the product and try again.') ); } - $validateSampleContent = !($sample->getSampleType() === 'file' && $sample->getSampleFile()); - if (!$this->contentValidator->isValid($sample, $validateSampleContent)) { + $this->validateSampleType($sample); + if (!$this->contentValidator->isValid($sample, true)) { throw new InputException( __('The sample information is invalid. Verify the information and try again.') ); } - - if (!in_array($sample->getSampleType(), ['url', 'file'], true)) { - throw new InputException(__('The sample type is invalid. Verify the sample type and try again.')); - } - $title = $sample->getTitle(); if (empty($title)) { throw new InputException(__('The sample title is empty. Enter the title and try again.')); @@ -230,7 +225,7 @@ protected function saveSample( 'title' => $sample->getTitle(), ]; - if ($sample->getSampleType() === 'file' && $sample->getSampleFile() === null) { + if ($sample->getSampleType() === 'file' && $sample->getSampleFileContent() !== null) { $sampleData['file'] = $this->jsonEncoder->encode( [ $this->fileContentUploader->upload($sample->getSampleFileContent(), 'sample'), @@ -293,9 +288,8 @@ protected function updateSample( __("The downloadable sample isn't related to the product. Verify the link and try again.") ); } - - $validateFileContent = $sample->getSampleFileContent() === null ? false : true; - if (!$this->contentValidator->isValid($sample, $validateFileContent)) { + $this->validateSampleType($sample); + if (!$this->contentValidator->isValid($sample, true)) { throw new InputException(__('The sample information is invalid. Verify the information and try again.')); } if ($isGlobalScopeContent) { @@ -312,14 +306,8 @@ protected function updateSample( } else { $existingSample->setTitle($sample->getTitle()); } - - if ($sample->getSampleType() === 'file' - && $sample->getSampleFileContent() === null - && $sample->getSampleFile() !== null - ) { - $existingSample->setSampleFile($sample->getSampleFile()); - } $this->saveSample($product, $sample, $isGlobalScopeContent); + return $existingSample->getId(); } @@ -343,6 +331,20 @@ public function delete($id) return true; } + /** + * Check that Sample type exist. + * + * @param SampleInterface $sample + * @throws InputException + * @return void + */ + private function validateSampleType(SampleInterface $sample): void + { + if (!in_array($sample->getSampleType(), ['url', 'file'], true)) { + throw new InputException(__('The sample type is invalid. Verify the sample type and try again.')); + } + } + /** * Get MetadataPool instance * diff --git a/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php b/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php index 25a5d86b0385c..508ef930f5d04 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php @@ -7,6 +7,9 @@ use Magento\Catalog\Api\Data\ProductExtensionInterface; +/** + * Unit tests for \Magento\Downloadable\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Downloadable. + */ class DownloadableTest extends \PHPUnit\Framework\TestCase { /** @@ -34,12 +37,17 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase */ private $extensionAttributesMock; + /** + * @var \Magento\Downloadable\Model\Product\Type|\Magento\Catalog\Api\Data\ProductExtensionInterface + */ + private $downloadableProductTypeMock; + protected function setUp() { $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); $this->productMock = $this->createPartialMock( \Magento\Catalog\Model\Product::class, - ['setDownloadableData', 'getExtensionAttributes', '__wakeup'] + ['setDownloadableData', 'getExtensionAttributes', '__wakeup', 'getTypeInstance'] ); $this->subjectMock = $this->createMock( \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::class @@ -62,6 +70,10 @@ protected function setUp() $sampleBuilderMock = $this->getMockBuilder(\Magento\Downloadable\Model\Sample\Builder::class) ->disableOriginalConstructor() ->getMock(); + $this->downloadableProductTypeMock = $this->createPartialMock( + \Magento\Downloadable\Model\Product\Type::class, + ['getLinks', 'getSamples'] + ); $this->downloadablePlugin = new \Magento\Downloadable\Controller\Adminhtml\Product\Initialization\Helper\Plugin\Downloadable( $this->requestMock, @@ -86,6 +98,11 @@ public function testAfterInitializeWithNoDataToSave($downloadable) $this->productMock->expects($this->once()) ->method('getExtensionAttributes') ->willReturn($this->extensionAttributesMock); + $this->productMock->expects($this->exactly(2)) + ->method('getTypeInstance') + ->willReturn($this->downloadableProductTypeMock); + $this->downloadableProductTypeMock->expects($this->once())->method('getLinks')->willReturn([]); + $this->downloadableProductTypeMock->expects($this->once())->method('getSamples')->willReturn([]); $this->extensionAttributesMock->expects($this->once()) ->method('setDownloadableProductLinks') ->with([]); diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php index 2639c22ff2ca2..5484e39bd57fc 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php @@ -5,8 +5,12 @@ */ namespace Magento\Downloadable\Test\Unit\Model\Link; +use Magento\Downloadable\Helper\File; use Magento\Downloadable\Model\Link\ContentValidator; +/** + * Unit tests for Magento\Downloadable\Model\Link\ContentValidator. + */ class ContentValidatorTest extends \PHPUnit\Framework\TestCase { /** @@ -34,13 +38,32 @@ class ContentValidatorTest extends \PHPUnit\Framework\TestCase */ protected $sampleFileMock; + /** + * @var File|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileMock; + + /** + * @inheritdoc + */ protected function setUp() { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->fileValidatorMock = $this->createMock(\Magento\Downloadable\Model\File\ContentValidator::class); $this->urlValidatorMock = $this->createMock(\Magento\Framework\Url\Validator::class); $this->linkFileMock = $this->createMock(\Magento\Downloadable\Api\Data\File\ContentInterface::class); $this->sampleFileMock = $this->createMock(\Magento\Downloadable\Api\Data\File\ContentInterface::class); - $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); + $this->fileMock = $this->createMock(File::class); + + $this->validator = $objectManager->getObject( + ContentValidator::class, + [ + 'fileContentValidator' => $this->fileValidatorMock, + 'urlValidator' => $this->urlValidatorMock, + 'fileHelper' => $this->fileMock, + ] + ); } public function testIsValid() diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php index c863fb7ad62ff..90bcfa2e39bef 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php @@ -6,7 +6,11 @@ namespace Magento\Downloadable\Test\Unit\Model\Sample; use Magento\Downloadable\Model\Sample\ContentValidator; +use Magento\Downloadable\Helper\File; +/** + * Unit tests for Magento\Downloadable\Model\Sample\ContentValidator. + */ class ContentValidatorTest extends \PHPUnit\Framework\TestCase { /** @@ -34,12 +38,31 @@ class ContentValidatorTest extends \PHPUnit\Framework\TestCase */ protected $sampleFileMock; + /** + * @var File|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileMock; + + /** + * @inheritdoc + */ protected function setUp() { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->fileValidatorMock = $this->createMock(\Magento\Downloadable\Model\File\ContentValidator::class); $this->urlValidatorMock = $this->createMock(\Magento\Framework\Url\Validator::class); $this->sampleFileMock = $this->createMock(\Magento\Downloadable\Api\Data\File\ContentInterface::class); - $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock); + $this->fileMock = $this->createMock(File::class); + + $this->validator = $objectManager->getObject( + ContentValidator::class, + [ + 'fileContentValidator' => $this->fileValidatorMock, + 'urlValidator' => $this->urlValidatorMock, + 'fileHelper' => $this->fileMock, + ] + ); } public function testIsValid() diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php index f29708cc9a2c6..7a8d201d09c4f 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php @@ -13,6 +13,7 @@ use Magento\Framework\UrlInterface; use Magento\Downloadable\Model\Link as LinkModel; use Magento\Downloadable\Api\Data\LinkInterface; +use Magento\Framework\Exception\ValidatorException; /** * Class Links @@ -155,7 +156,7 @@ protected function addSampleFile(array $linkData, LinkInterface $link) $sampleFile = $link->getSampleFile(); if ($sampleFile) { $file = $this->downloadableFile->getFilePath($this->linkModel->getBaseSamplePath(), $sampleFile); - if ($this->downloadableFile->ensureFileInFilesystem($file)) { + if ($this->checkLinksFile($file)) { $linkData['sample']['file'][0] = [ 'file' => $sampleFile, 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), @@ -184,7 +185,7 @@ protected function addLinkFile(array $linkData, LinkInterface $link) $linkFile = $link->getLinkFile(); if ($linkFile) { $file = $this->downloadableFile->getFilePath($this->linkModel->getBasePath(), $linkFile); - if ($this->downloadableFile->ensureFileInFilesystem($file)) { + if ($this->checkLinksFile($file)) { $linkData['file'][0] = [ 'file' => $linkFile, 'name' => $this->downloadableFile->getFileFromPathFile($linkFile), @@ -201,6 +202,21 @@ protected function addLinkFile(array $linkData, LinkInterface $link) return $linkData; } + /** + * Check that Links File or Sample is valid. + * + * @param string $file + * @return bool + */ + private function checkLinksFile(string $file): bool + { + try { + return $this->downloadableFile->ensureFileInFilesystem($file); + } catch (ValidatorException $e) { + return false; + } + } + /** * Return formatted price with two digits after decimal point * diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php index b000de487b775..ac8f3eb962957 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php @@ -11,6 +11,7 @@ use Magento\Catalog\Model\Locator\LocatorInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Downloadable\Helper\File as DownloadableFile; +use Magento\Framework\Exception\ValidatorException; use Magento\Framework\UrlInterface; use Magento\Downloadable\Api\Data\SampleInterface; @@ -136,7 +137,7 @@ protected function addSampleFile(array $sampleData, SampleInterface $sample) $sampleFile = $sample->getSampleFile(); if ($sampleFile) { $file = $this->downloadableFile->getFilePath($this->sampleModel->getBasePath(), $sampleFile); - if ($this->downloadableFile->ensureFileInFilesystem($file)) { + if ($this->checkSamplesFile($file)) { $sampleData['file'][0] = [ 'file' => $sampleFile, 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), @@ -152,4 +153,19 @@ protected function addSampleFile(array $sampleData, SampleInterface $sample) return $sampleData; } + + /** + * Check that Sample file is valid. + * + * @param string $file + * @return bool + */ + private function checkSamplesFile(string $file): bool + { + try { + return $this->downloadableFile->ensureFileInFilesystem($file); + } catch (ValidatorException $e) { + return false; + } + } } diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index c881969a3b679..3fa60f93fc683 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -12,6 +12,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; +/** + * API tests for Magento\Downloadable\Model\LinkRepository. + */ class LinkRepositoryTest extends WebapiAbstract { /** @@ -135,10 +138,12 @@ public function testCreateUploadsProvidedFileContent() 'number_of_downloads' => 100, 'link_type' => 'file', 'link_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], 'sample_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], @@ -292,6 +297,64 @@ public function testCreateThrowsExceptionIfLinkFileContentIsNotAValidBase64Encod $this->_webApiCall($this->createServiceInfo, $requestData); } + /** + * Check that error appears when link file not existing in filesystem. + * + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + * @expectedException \Exception + * @expectedExceptionMessage Link file not found. Please try again. + * @return void + */ + public function testCreateThrowsExceptionIfLinkFileNotFoundInSystem(): void + { + $requestData = [ + 'isGlobalScopeContent' => false, + 'sku' => 'downloadable-product', + 'link' => [ + 'title' => 'Link Title', + 'sort_order' => 1, + 'price' => 10, + 'is_shareable' => 1, + 'number_of_downloads' => 100, + 'link_type' => 'file', + 'link_file' => '/n/o/nexistfile.png', + 'sample_type' => 'url', + 'sample_file' => 'http://google.com', + ], + ]; + + $this->_webApiCall($this->createServiceInfo, $requestData); + } + + /** + * Check that error appears when link sample file not existing in filesystem. + * + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + * @expectedException \Exception + * @expectedExceptionMessage Link sample file not found. Please try again. + * @return void + */ + public function testCreateThrowsExceptionIfLinkSampleFileNotFoundInSystem(): void + { + $requestData = [ + 'isGlobalScopeContent' => false, + 'sku' => 'downloadable-product', + 'link' => [ + 'title' => 'Link Title', + 'sort_order' => 1, + 'price' => 10, + 'is_shareable' => 1, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://www.example.com/', + 'sample_type' => 'file', + 'sample_file' => '/n/o/nexistfile.png', + ], + ]; + + $this->_webApiCall($this->createServiceInfo, $requestData); + } + /** * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php * @expectedException \Exception @@ -339,6 +402,7 @@ public function testCreateThrowsExceptionIfLinkFileNameContainsForbiddenCharacte 'number_of_downloads' => 100, 'link_type' => 'file', 'link_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], @@ -370,6 +434,7 @@ public function testCreateThrowsExceptionIfSampleFileNameContainsForbiddenCharac 'link_url' => 'http://www.example.com/', 'sample_type' => 'file', 'sample_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], @@ -610,7 +675,9 @@ public function testUpdate() 'is_shareable' => 0, 'number_of_downloads' => 50, 'link_type' => 'url', + 'link_url' => 'http://google.com', 'sample_type' => 'url', + 'sample_url' => 'http://google.com', ], ]; $this->assertEquals($linkId, $this->_webApiCall($this->updateServiceInfo, $requestData)); @@ -643,7 +710,9 @@ public function testUpdateSavesDataInGlobalScopeAndDoesNotAffectValuesStoredInSt 'is_shareable' => 0, 'number_of_downloads' => 50, 'link_type' => 'url', + 'link_url' => 'http://google.com', 'sample_type' => 'url', + 'sample_url' => 'http://google.com', ], ]; diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php index 769abadf20585..782c15a99b4d4 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -227,7 +227,9 @@ public function testUpdateDownloadableProductLinks() 'price' => 5.0, 'number_of_downloads' => 999, 'link_type' => 'file', - 'sample_type' => 'file' + 'link_file' => $linkFile, + 'sample_type' => 'file', + 'sample_file' => $sampleFile, ]; $linkData = $this->getLinkData(); @@ -273,6 +275,7 @@ public function testUpdateDownloadableProductLinks() 'number_of_downloads' => 999, 'link_type' => 'file', 'sample_type' => 'file', + 'sample_file' => '/s/a/sample2.jpg', ]; $expectedLinkData = array_merge($expectedLinkData, $this->getExpectedLinkData()); $this->assertEquals($expectedLinkData, $resultLinks); diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php index b537947d5e4db..b339e97d8d69d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php @@ -11,6 +11,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; +/** + * API tests for Magento\Downloadable\Model\SampleRepository. + */ class SampleRepositoryTest extends WebapiAbstract { /** @@ -131,6 +134,7 @@ public function testCreateUploadsProvidedFileContent() 'title' => 'Title', 'sort_order' => 1, 'sample_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'image.jpg', ], @@ -223,6 +227,30 @@ public function testCreateThrowsExceptionIfSampleTypeIsInvalid() $this->_webApiCall($this->createServiceInfo, $requestData); } + /** + * Check that error appears when sample file not existing in filesystem. + * + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + * @expectedException \Exception + * @expectedExceptionMessage Sample file not found. Please try again. + * @return void + */ + public function testCreateThrowsExceptionIfSampleFileNotFoundInSystem(): void + { + $requestData = [ + 'isGlobalScopeContent' => false, + 'sku' => 'downloadable-product', + 'sample' => [ + 'title' => 'Link Title', + 'sort_order' => 1, + 'sample_type' => 'file', + 'sample_file' => '/n/o/nexistfile.png', + ], + ]; + + $this->_webApiCall($this->createServiceInfo, $requestData); + } + /** * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php * @expectedException \Exception @@ -262,6 +290,7 @@ public function testCreateThrowsExceptionIfSampleFileNameContainsForbiddenCharac 'sort_order' => 15, 'sample_type' => 'file', 'sample_file_content' => [ + //phpcs:ignore Magento2.Functions.DiscouragedFunction 'file_data' => base64_encode(file_get_contents($this->testImagePath)), 'name' => 'name/with|forbidden{characters', ], @@ -380,6 +409,7 @@ public function testUpdate() 'title' => 'Updated Title', 'sort_order' => 2, 'sample_type' => 'url', + 'sample_url' => 'http://google.com', ], ]; @@ -408,6 +438,7 @@ public function testUpdateSavesDataInGlobalScopeAndDoesNotAffectValuesStoredInSt 'title' => 'Updated Title', 'sort_order' => 2, 'sample_type' => 'url', + 'sample_url' => 'http://google.com', ], ]; From 01a7ea0f8f3977b700a05ccf650108476582c390 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Thu, 2 May 2019 11:59:44 +0300 Subject: [PATCH 0029/2437] MC-13886: Downloadable Product controller|API save changes --- .../Downloadable/Test/Unit/Model/LinkRepositoryTest.php | 5 ++++- .../Downloadable/Test/Unit/Model/SampleRepositoryTest.php | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php index 821f251929f8b..4494877b70f6c 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php @@ -162,7 +162,8 @@ protected function getLinkMock(array $linkData) 'getNumberOfDownloads', 'getIsShareable', 'getLinkUrl', - 'getLinkFile' + 'getLinkFile', + 'hasSampleType', ] ) ->getMockForAbstractClass(); @@ -436,6 +437,8 @@ public function testUpdateThrowsExceptionIfTitleIsEmptyAndScopeIsGlobal() 'price' => 10.1, 'number_of_downloads' => 100, 'is_shareable' => true, + 'link_type' => 'url', + 'link_url' => 'https://google.com', ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php index 8e13bd83b039e..f1ca30bd7dd36 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php @@ -353,6 +353,8 @@ public function testUpdateThrowsExceptionIfTitleIsEmptyAndScopeIsGlobal() 'id' => $sampleId, 'title' => '', 'sort_order' => 1, + 'sample_type' => 'url', + 'sample_url' => 'https://google.com', ]; $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true) ->will($this->returnValue($this->productMock)); From 56edf648db291846e72cd50137b085c52797da03 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Thu, 2 May 2019 12:37:01 +0300 Subject: [PATCH 0030/2437] MC-13886: Downloadable Product controller|API save changes --- .../testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php index 782c15a99b4d4..cf99b34207124 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -275,7 +275,6 @@ public function testUpdateDownloadableProductLinks() 'number_of_downloads' => 999, 'link_type' => 'file', 'sample_type' => 'file', - 'sample_file' => '/s/a/sample2.jpg', ]; $expectedLinkData = array_merge($expectedLinkData, $this->getExpectedLinkData()); $this->assertEquals($expectedLinkData, $resultLinks); From a7b52c225775fab2591732986c2ca582ff6f3710 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 2 May 2019 10:28:12 -0500 Subject: [PATCH 0031/2437] MC-16044: Update symfony/dependency-injection --- composer.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index 5d9f7fbdf695c..cbeaac07eaa08 100644 --- a/composer.lock +++ b/composer.lock @@ -8853,16 +8853,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.2.4", + "version": "v4.2.7", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "cdadb3765df7c89ac93628743913b92bb91f1704" + "reference": "2748643dd378626c4d348a31ad12394e2d6f7ea8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/cdadb3765df7c89ac93628743913b92bb91f1704", - "reference": "cdadb3765df7c89ac93628743913b92bb91f1704", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2748643dd378626c4d348a31ad12394e2d6f7ea8", + "reference": "2748643dd378626c4d348a31ad12394e2d6f7ea8", "shasum": "" }, "require": { @@ -8922,7 +8922,7 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-02-23T15:17:42+00:00" + "time": "2019-04-16T11:19:53+00:00" }, { "name": "symfony/dom-crawler", From eb3249e41e3a9b052b492407bcccbc24f56ae935 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Thu, 2 May 2019 17:50:35 +0300 Subject: [PATCH 0032/2437] MC-13886: Downloadable Product controller|API save changes --- .../Downloadable/Model/LinkRepository.php | 30 ++++++++----------- .../Helper/Plugin/DownloadableTest.php | 3 ++ .../Product/Form/Modifier/Data/Links.php | 6 ++-- .../Product/Form/Modifier/Data/Samples.php | 4 +-- .../Downloadable/Api/LinkRepositoryTest.php | 4 +-- .../Downloadable/Api/SampleRepositoryTest.php | 2 +- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/LinkRepository.php b/app/code/Magento/Downloadable/Model/LinkRepository.php index 57e0fee5c1181..68fbba2a7d385 100644 --- a/app/code/Magento/Downloadable/Model/LinkRepository.php +++ b/app/code/Magento/Downloadable/Model/LinkRepository.php @@ -180,14 +180,14 @@ public function save($sku, LinkInterface $link, $isGlobalScopeContent = true) if ($link->getId() !== null) { return $this->updateLink($product, $link, $isGlobalScopeContent); } else { - if ($product->getTypeId() !== \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) { + if ($product->getTypeId() !== Type::TYPE_DOWNLOADABLE) { throw new InputException( __('The product needs to be the downloadable type. Verify the product and try again.') ); } $this->validateLinkType($link); - $validateSampleContent = $this->isValidateSample($link); - if (!$this->contentValidator->isValid($link, true, $validateSampleContent)) { + $this->validateSampleType($link); + if (!$this->contentValidator->isValid($link, true, $link->hasSampleType())) { throw new InputException(__('The link information is invalid. Verify the link and try again.')); } $title = $link->getTitle(); @@ -304,7 +304,8 @@ protected function updateLink( ); } $this->validateLinkType($link); - $validateSampleContent = $this->isValidateSample($link); + $this->validateSampleType($link); + $validateSampleContent = $link->hasSampleType(); if (!$this->contentValidator->isValid($link, true, $validateSampleContent)) { throw new InputException(__('The link information is invalid. Verify the link and try again.')); } @@ -349,7 +350,8 @@ public function delete($id) * Check that Link type exist. * * @param LinkInterface $link - * @throws \Magento\Framework\Exception\InputException + * @return void + * @throws InputException */ private function validateLinkType(LinkInterface $link): void { @@ -361,21 +363,15 @@ private function validateLinkType(LinkInterface $link): void /** * Check that Link sample type exist. * - * @param \Magento\Downloadable\Api\Data\LinkInterface $link - * @return bool - * @throws \Magento\Framework\Exception\InputException + * @param LinkInterface $link + * @return void + * @throws InputException */ - private function isValidateSample(LinkInterface $link): bool + private function validateSampleType(LinkInterface $link): void { - if ($link->hasSampleType()) { - if (in_array($link->getSampleType(), ['url', 'file'], true)) { - return true; - } else { - throw new InputException(__('The link sample type is invalid. Verify and try again.')); - } + if ($link->hasSampleType() && !in_array($link->getSampleType(), ['url', 'file'], true)) { + throw new InputException(__('The link sample type is invalid. Verify and try again.')); } - - return false; } /** diff --git a/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php b/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php index 508ef930f5d04..55353c16b4727 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/DownloadableTest.php @@ -42,6 +42,9 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase */ private $downloadableProductTypeMock; + /** + * @inheritdoc + */ protected function setUp() { $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php index 7a8d201d09c4f..0a3ea2fc6ba19 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php @@ -156,7 +156,7 @@ protected function addSampleFile(array $linkData, LinkInterface $link) $sampleFile = $link->getSampleFile(); if ($sampleFile) { $file = $this->downloadableFile->getFilePath($this->linkModel->getBaseSamplePath(), $sampleFile); - if ($this->checkLinksFile($file)) { + if ($this->isLinkFileValid($file)) { $linkData['sample']['file'][0] = [ 'file' => $sampleFile, 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), @@ -185,7 +185,7 @@ protected function addLinkFile(array $linkData, LinkInterface $link) $linkFile = $link->getLinkFile(); if ($linkFile) { $file = $this->downloadableFile->getFilePath($this->linkModel->getBasePath(), $linkFile); - if ($this->checkLinksFile($file)) { + if ($this->isLinkFileValid($file)) { $linkData['file'][0] = [ 'file' => $linkFile, 'name' => $this->downloadableFile->getFileFromPathFile($linkFile), @@ -208,7 +208,7 @@ protected function addLinkFile(array $linkData, LinkInterface $link) * @param string $file * @return bool */ - private function checkLinksFile(string $file): bool + private function isLinkFileValid(string $file): bool { try { return $this->downloadableFile->ensureFileInFilesystem($file); diff --git a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php index ac8f3eb962957..988f429de1d87 100644 --- a/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php +++ b/app/code/Magento/Downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Samples.php @@ -137,7 +137,7 @@ protected function addSampleFile(array $sampleData, SampleInterface $sample) $sampleFile = $sample->getSampleFile(); if ($sampleFile) { $file = $this->downloadableFile->getFilePath($this->sampleModel->getBasePath(), $sampleFile); - if ($this->checkSamplesFile($file)) { + if ($this->isSampleFileValid($file)) { $sampleData['file'][0] = [ 'file' => $sampleFile, 'name' => $this->downloadableFile->getFileFromPathFile($sampleFile), @@ -160,7 +160,7 @@ protected function addSampleFile(array $sampleData, SampleInterface $sample) * @param string $file * @return bool */ - private function checkSamplesFile(string $file): bool + private function isSampleFileValid(string $file): bool { try { return $this->downloadableFile->ensureFileInFilesystem($file); diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index 3fa60f93fc683..1c239fba244ac 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -305,7 +305,7 @@ public function testCreateThrowsExceptionIfLinkFileContentIsNotAValidBase64Encod * @expectedExceptionMessage Link file not found. Please try again. * @return void */ - public function testCreateThrowsExceptionIfLinkFileNotFoundInSystem(): void + public function testCreateLinkWithMissingFileThrowsException(): void { $requestData = [ 'isGlobalScopeContent' => false, @@ -334,7 +334,7 @@ public function testCreateThrowsExceptionIfLinkFileNotFoundInSystem(): void * @expectedExceptionMessage Link sample file not found. Please try again. * @return void */ - public function testCreateThrowsExceptionIfLinkSampleFileNotFoundInSystem(): void + public function testCreateLinkWithMissingSampleThrowsException(): void { $requestData = [ 'isGlobalScopeContent' => false, diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php index b339e97d8d69d..a97e4c5d9e119 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php @@ -235,7 +235,7 @@ public function testCreateThrowsExceptionIfSampleTypeIsInvalid() * @expectedExceptionMessage Sample file not found. Please try again. * @return void */ - public function testCreateThrowsExceptionIfSampleFileNotFoundInSystem(): void + public function testCreateSampleWithMissingFileThrowsException(): void { $requestData = [ 'isGlobalScopeContent' => false, From 32be5b967d5c8f2e680e8cc39b64ddc1851567be Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 2 May 2019 14:48:00 -0500 Subject: [PATCH 0033/2437] MC-15811: Incorrect Url Refactor controller validation logic. Fix integration test. --- .../Store/Controller/Store/SwitchRequest.php | 53 ++++++------------- .../Model/StoreSwitcher/HashGenerator.php | 10 ++-- .../Magento/Store/Model/HashGeneratorTest.php | 28 ++++++++-- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index 6d97487df67b8..e220941017c34 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -10,34 +10,24 @@ use Magento\Framework\App\Action\Context; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Store\Api\StoreRepositoryInterface; use Magento\Customer\Model\Session as CustomerSession; -use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Store\Model\StoreSwitcher\HashGenerator; use Magento\Customer\Api\CustomerRepositoryInterface; use \Magento\Framework\Exception\LocalizedException; -use Magento\Store\Model\StoreIsInactiveException; +use Magento\Framework\Url\DecoderInterface; +use \Magento\Framework\App\ActionInterface; /** * Builds correct url to target store and performs redirect. */ class SwitchRequest extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface { - /** - * @var StoreRepositoryInterface - */ - private $storeRepository; /** * @var customerSession */ private $customerSession; - /** - * @var \Magento\Framework\App\DeploymentConfig - */ - private $deploymentConfig; - /** * @var CustomerRepositoryInterface */ @@ -48,28 +38,30 @@ class SwitchRequest extends \Magento\Framework\App\Action\Action implements Http */ private $hashGenerator; + /** + * @var DecoderInterface + */ + private $urlDecoder; + /** * @param Context $context - * @param StoreRepositoryInterface $storeRepository * @param CustomerSession $session - * @param DeploymentConfig $deploymentConfig * @param CustomerRepositoryInterface $customerRepository * @param HashGenerator $hashGenerator + * @param DecoderInterface $urlDecoder */ public function __construct( Context $context, - StoreRepositoryInterface $storeRepository, CustomerSession $session, - DeploymentConfig $deploymentConfig, CustomerRepositoryInterface $customerRepository, - HashGenerator $hashGenerator + HashGenerator $hashGenerator, + DecoderInterface $urlDecoder ) { parent::__construct($context); - $this->storeRepository = $storeRepository; $this->customerSession = $session; - $this->deploymentConfig = $deploymentConfig; $this->customerRepository = $customerRepository; $this->hashGenerator = $hashGenerator; + $this->urlDecoder = $urlDecoder; } /** @@ -82,41 +74,30 @@ public function execute() $fromStoreCode = (string)$this->_request->getParam('___from_store'); $customerId = (int)$this->_request->getParam('customer_id'); $timeStamp = (string)$this->_request->getParam('time_stamp'); - $targetStoreCode = $this->_request->getParam('___to_store'); $signature = (string)$this->_request->getParam('signature'); $error = null; + $encodedUrl = (string)$this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED); + $targetUrl = $this->urlDecoder->decode($encodedUrl); + $data=[$customerId, $timeStamp, $fromStoreCode]; - try { - $fromStore = $this->storeRepository->get($fromStoreCode); - $targetStore=$this->storeRepository->getActiveStoreByCode($targetStoreCode); - $targetUrl=$targetStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK); - } catch (NoSuchEntityException $e) { - $error = __('Requested store is not found.'); - } catch (StoreIsInactiveException $e) { - $error = __('Requested store is inactive.'); - } - - if ($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])) { + if ($targetUrl && $this->hashGenerator->validateHash($signature, $data)) { try { $customer = $this->customerRepository->getById($customerId); if (!$this->customerSession->isLoggedIn()) { $this->customerSession->setCustomerDataAsLoggedIn($customer); } + $this->getResponse()->setRedirect($targetUrl); } catch (NoSuchEntityException $e) { $error = __('The requested customer does not exist.'); } catch (LocalizedException $e) { $error = __('There was an error retrieving the customer record.'); } } else { - $error = __('Invalid request. Store switching action cannot be performed at this time.'); + $error = __('The requested store cannot be found. Please check the request and try again.'); } if ($error !== null) { $this->messageManager->addErrorMessage($error); - //redirect to previous store - $this->getResponse()->setRedirect($fromStore->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_LINK)); - } else { - $this->getResponse()->setRedirect($targetUrl); } } } diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 0e0e77b04f94f..945b5f36a0a8c 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -13,6 +13,7 @@ use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Authorization\Model\UserContextInterface; +use \Magento\Framework\App\ActionInterface; /** * Generate one time token and build redirect url @@ -61,6 +62,7 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s { $targetUrl = $redirectUrl; $customerId = null; + $encodedUrl = $this->urlHelper->getEncodedUrl($redirectUrl); if ($this->currentUser->getUserType() == UserContextInterface::USER_TYPE_CUSTOMER) { $customerId = $this->currentUser->getUserId(); @@ -71,11 +73,9 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s $urlParts = parse_url($targetUrl); $host = $urlParts['host']; $scheme = $urlParts['scheme']; - $path=$urlParts['path']; $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); $timeStamp = time(); $fromStoreCode = $fromStore->getCode(); - $targetStoreCode = $targetStore->getCode(); $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); $signature = hash_hmac('sha256', $data, $key); $targetUrl = $scheme . "://" . $host . '/stores/store/switchrequest'; @@ -86,8 +86,10 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => $timeStamp]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___from_store' => $fromStoreCode]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___to_store' => $targetStoreCode]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['path' => $path]); + $targetUrl = $this->urlHelper->addRequestParam( + $targetUrl, + [ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl] + ); } return $targetUrl; } diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 7b5bb357b9345..8b0cec3dfa20e 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -14,6 +14,8 @@ use Magento\Customer\Model\Session; use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants; +use \Magento\Framework\App\ActionInterface; +use Magento\Framework\Url\Helper\Data as UrlHelper; /** * Test class for \Magento\Store\Model\StoreSwitcher\HashGenerator @@ -33,7 +35,7 @@ class HashGeneratorTest extends \PHPUnit\Framework\TestCase private $objectManager; /** - * + * @var int */ private $customerId; @@ -51,10 +53,15 @@ class HashGeneratorTest extends \PHPUnit\Framework\TestCase private $deploymentConfig; /** - * + * @var string */ private $key; + /** + * @var UrlHelper + */ + private $urlHelper; + /** * Class dependencies initialization * @return void @@ -80,6 +87,7 @@ protected function setUp() $this->customerId = $customer->getId(); $this->deploymentConfig = $this->objectManager->get(DeploymentConfig::class); $this->key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + $this->urlHelper=$this->objectManager->create(UrlHelper::class); } /** @@ -94,6 +102,7 @@ public function testSwitch(): void $redirectUrl = "http://domain.com/"; $fromStoreCode = 'test'; $toStoreCode = 'fixture_second_store'; + $encodedUrl=$this->urlHelper->getEncodedUrl($redirectUrl); /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); $fromStore = $storeRepository->get($fromStoreCode); @@ -103,9 +112,18 @@ public function testSwitch(): void $signature = hash_hmac('sha256', $data, $this->key); $customerId = $this->customerId; - $expectedUrl = "http://domain.com/stores/store/switchrequest?customer_id=$customerId"; - $expectedUrl .= "&time_stamp=$timeStamp&signature=$signature"; - $expectedUrl .= "&___from_store=$fromStoreCode&___to_store=$toStoreCode"; + $expectedUrl = "http://domain.com/stores/store/switchrequest"; + $expectedUrl = $this->urlHelper->addRequestParam( + $expectedUrl, + ['customer_id' => $customerId] + ); + $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['time_stamp' => $timeStamp]); + $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['signature' => $signature]); + $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['___from_store' => $fromStoreCode]); + $expectedUrl = $this->urlHelper->addRequestParam( + $expectedUrl, + [ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl] + ); $this->assertEquals($expectedUrl, $this->hashGenerator->switch($fromStore, $toStore, $redirectUrl)); } From 5bffe14b6ec4eab02a1e87b01c0bd62bfa2f7775 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 2 May 2019 15:04:46 -0500 Subject: [PATCH 0034/2437] MC-15811: Incorrect Url Remove redundant unit test. --- .../Controller/Store/SwitchRequestTest.php | 139 ------------------ 1 file changed, 139 deletions(-) delete mode 100644 app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php diff --git a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php b/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php deleted file mode 100644 index ebd85d89c23a9..0000000000000 --- a/app/code/Magento/Store/Test/Unit/Controller/Store/SwitchRequestTest.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Store\Test\Unit\Controller\Store; - -use Magento\Store\Api\StoreRepositoryInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Customer\Model\Session as CustomerSession; -use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; - -/** - * Test class for \Magento\Store\Controller\Store\SwitchRequest - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class SwitchRequestTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Store\Controller\Store\SwitchRequest - */ - private $model; - - /** - * @var StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeRepositoryMock; - - /** - * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject - */ - private $customerSessionMock; - - /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $requestMock; - - /** - * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $customerRepositoryMock; - - /** - * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject - */ - private $deploymentConfigMock; - - /** - * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject - */ - private $fromStoreMock; - - /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $responseMock; - - /** - * @return void - */ - protected function setUp() - { - $this->customerSessionMock = $this->getMockBuilder(CustomerSession::class) - ->disableOriginalConstructor()->getMock(); - $this->customerRepositoryMock = - $this->getMockBuilder(CustomerRepositoryInterface::class)->getMock(); - $this->storeRepositoryMock = - $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class) - ->disableOriginalConstructor()->setMethods(['get', 'getActiveStoreByCode'])->getMockForAbstractClass(); - - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)->getMock(); - $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setRedirect']) - ->getMockForAbstractClass(); - $this->deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) - ->disableOriginalConstructor()->getMock(); - $this->fromStoreMock = $this->getMockBuilder(StoreInterface::class) - ->disableOriginalConstructor() - ->setMethods(['getBaseUrl']) - ->getMockForAbstractClass(); - - $this->model = (new ObjectManager($this))->getObject( - \Magento\Store\Controller\Store\SwitchRequest::class, - [ - 'customerSession' => $this->customerRepositoryMock, - 'deploymentConfig' => $this->deploymentConfigMock, - 'storeRepository' => $this->storeRepositoryMock, - 'customerRepository' => $this->customerRepositoryMock, - '_request' => $this->requestMock, - '_response' => $this->responseMock, - ] - ); - } - - /** - * @return void - */ - public function testExecute() - { - $fromStoreCode = 'sv2'; - $targetStoreCode = 'default'; - $expectedRedirectUrl='/'; - $customerId=5; - $timestamp='1556131830'; - - $this->requestMock->method('getParam') - ->willReturnMap([ - ['___from_store', null, $fromStoreCode], - ['customer_id', null, $customerId], - ['time_stamp', null, $timestamp], - ['___to_store', null, $targetStoreCode], - ['signature', null, 'cbc099b3cc4a9a8f3a78a97e7a579ceff19a2b26a6c88b08f0f58442ea5bd968'] - ]); - - $this->storeRepositoryMock - ->expects($this->once()) - ->method('get') - ->with($fromStoreCode) - ->willReturn($this->fromStoreMock); - - $this->storeRepositoryMock - ->expects($this->once()) - ->method('getActiveStoreByCode') - ->with($targetStoreCode); - - $this->fromStoreMock - ->expects($this->once()) - ->method('getBaseUrl') - ->with(\Magento\Framework\UrlInterface::URL_TYPE_LINK) - ->willReturn($expectedRedirectUrl); - - $this->responseMock->expects($this->once())->method('setRedirect')->with($expectedRedirectUrl); - $this->model->execute(); - } -} From 6195986fe755284ce68aa0bb24a3c51548108f07 Mon Sep 17 00:00:00 2001 From: Anthoula Wojczak <awojczak@adobe.com> Date: Mon, 15 Apr 2019 14:35:56 -0500 Subject: [PATCH 0035/2437] MC-13900: Email to a Friend updates - default config option --- app/code/Magento/SendFriend/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SendFriend/etc/config.xml b/app/code/Magento/SendFriend/etc/config.xml index d65e5a4a073dd..6239a4da591e2 100644 --- a/app/code/Magento/SendFriend/etc/config.xml +++ b/app/code/Magento/SendFriend/etc/config.xml @@ -9,7 +9,7 @@ <default> <sendfriend> <email> - <enabled>1</enabled> + <enabled>0</enabled> <template>sendfriend_email_template</template> <allow_guest>0</allow_guest> <max_recipients>5</max_recipients> From 4c7c050f2e7cb15055b5429e4bbd15eb6f858552 Mon Sep 17 00:00:00 2001 From: Anthoula Wojczak <awojczak@adobe.com> Date: Thu, 18 Apr 2019 13:42:07 -0500 Subject: [PATCH 0036/2437] MC-13900: Email to a Friend updates - update integration tests with compatible config --- .../SendFriend/Controller/Product/CustomerSendmailTest.php | 4 +++- .../testsuite/Magento/SendFriend/Controller/SendmailTest.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php index 8794dfdff8fd7..686af841c7ff1 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php @@ -58,6 +58,7 @@ protected function setUp() } /** + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php */ @@ -95,6 +96,7 @@ public function testExecute() * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoConfigFixture default_store customer/captcha/forms product_sendtofriend_form + * @magentoConfigFixture default_store sendfriend/email/enabled 1 */ public function testWithCaptchaFailed() { @@ -133,7 +135,7 @@ public function testWithCaptchaFailed() * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoConfigFixture default_store customer/captcha/forms product_sendtofriend_form - * + * @magentoConfigFixture default_store sendfriend/email/enabled 1 */ public function testWithCaptchaSuccess() { diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index a075398e9cdb7..5c2ddf86d6f96 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -26,6 +26,7 @@ class SendmailTest extends AbstractController * * @magentoDbIsolation enabled * @magentoAppIsolation enabled + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoDataFixture Magento/SendFriend/_files/disable_allow_guest_config.php * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/products.php From b19cd09cca57b6daae9e1ed4078cbdcef2e2d3c4 Mon Sep 17 00:00:00 2001 From: Anthoula Wojczak <awojczak@adobe.com> Date: Tue, 23 Apr 2019 12:49:09 -0500 Subject: [PATCH 0037/2437] MC-13900: Email to a Friend updates - add doc comment --- .../SendFriend/Controller/Product/CustomerSendmailTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php index 686af841c7ff1..a94a96c5cbefb 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/Product/CustomerSendmailTest.php @@ -17,6 +17,9 @@ use Magento\Framework\Message\MessageInterface; use Magento\Captcha\Helper\Data as CaptchaHelper; +/** + * Class CustomerSendmailTest + */ class CustomerSendmailTest extends AbstractController { /** From 18907a694d6e99d2a00ce4ca905b9505f44839cb Mon Sep 17 00:00:00 2001 From: Anthoula Wojczak <awojczak@adobe.com> Date: Mon, 29 Apr 2019 10:51:52 -0500 Subject: [PATCH 0038/2437] MC-13900: Email to a Friend updates - add translation --- app/code/Magento/SendFriend/etc/adminhtml/system.xml | 5 ++++- app/code/Magento/SendFriend/i18n/en_US.csv | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SendFriend/etc/adminhtml/system.xml b/app/code/Magento/SendFriend/etc/adminhtml/system.xml index 785b7a8bb40c8..564f7bd306f8b 100644 --- a/app/code/Magento/SendFriend/etc/adminhtml/system.xml +++ b/app/code/Magento/SendFriend/etc/adminhtml/system.xml @@ -13,8 +13,11 @@ <resource>Magento_Config::sendfriend</resource> <group id="email" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Email Templates</label> - <field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> + <comment> + <![CDATA[We strongly recommend to enable a <a href="https://devdocs.magento.com/guides/v2.2/security/google-recaptcha.html" target="_blank">CAPTCHA solution</a> alongside enabling "Email to a Friend" to ensure abuse of this feature does not occur.]]> + </comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="template" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> diff --git a/app/code/Magento/SendFriend/i18n/en_US.csv b/app/code/Magento/SendFriend/i18n/en_US.csv index eee540c89a7b0..24fdea50be536 100644 --- a/app/code/Magento/SendFriend/i18n/en_US.csv +++ b/app/code/Magento/SendFriend/i18n/en_US.csv @@ -45,3 +45,5 @@ Enabled,Enabled "Max Recipients","Max Recipients" "Max Products Sent in 1 Hour","Max Products Sent in 1 Hour" "Limit Sending By","Limit Sending By" +"We strongly recommend to enable a <a href=""%1"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur.","We strongly recommend to enable a <a href=""%1"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur." + From 07fa87ae48a09e2f0eff5ec38617eeaae01b2c8f Mon Sep 17 00:00:00 2001 From: Anthoula Wojczak <awojczak@adobe.com> Date: Mon, 29 Apr 2019 11:40:20 -0500 Subject: [PATCH 0039/2437] MC-13900: Email to a Friend updates - add translation --- app/code/Magento/SendFriend/etc/adminhtml/system.xml | 2 +- app/code/Magento/SendFriend/i18n/en_US.csv | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SendFriend/etc/adminhtml/system.xml b/app/code/Magento/SendFriend/etc/adminhtml/system.xml index 564f7bd306f8b..5cace4bcf92db 100644 --- a/app/code/Magento/SendFriend/etc/adminhtml/system.xml +++ b/app/code/Magento/SendFriend/etc/adminhtml/system.xml @@ -16,7 +16,7 @@ <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Enabled</label> <comment> - <![CDATA[We strongly recommend to enable a <a href="https://devdocs.magento.com/guides/v2.2/security/google-recaptcha.html" target="_blank">CAPTCHA solution</a> alongside enabling "Email to a Friend" to ensure abuse of this feature does not occur.]]> + <![CDATA[We strongly recommend to enable a <a href="https://devdocs.magento.com/guides/v2.3/security/google-recaptcha.html" target="_blank">CAPTCHA solution</a> alongside enabling "Email to a Friend" to ensure abuse of this feature does not occur.]]> </comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/SendFriend/i18n/en_US.csv b/app/code/Magento/SendFriend/i18n/en_US.csv index 24fdea50be536..8d5b596fe1ca6 100644 --- a/app/code/Magento/SendFriend/i18n/en_US.csv +++ b/app/code/Magento/SendFriend/i18n/en_US.csv @@ -45,5 +45,4 @@ Enabled,Enabled "Max Recipients","Max Recipients" "Max Products Sent in 1 Hour","Max Products Sent in 1 Hour" "Limit Sending By","Limit Sending By" -"We strongly recommend to enable a <a href=""%1"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur.","We strongly recommend to enable a <a href=""%1"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur." - +"We strongly recommend to enable a <a href=""https://devdocs.magento.com/guides/v2.3/security/google-recaptcha.html"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur.","We strongly recommend to enable a <a href=""https://devdocs.magento.com/guides/v2.3/security/google-recaptcha.html"" target="_blank">CAPTCHA solution</a> alongside enabling ""Email to a Friend"" to ensure abuse of this feature does not occur." From 54c07ca9a7559a433f0ea932715b6f2b4d0f2ada Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 3 May 2019 11:45:29 +0300 Subject: [PATCH 0040/2437] MC-13886: Downloadable Product controller|API save changes --- .../testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index 1c239fba244ac..0eb3da755c5f0 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -305,7 +305,7 @@ public function testCreateThrowsExceptionIfLinkFileContentIsNotAValidBase64Encod * @expectedExceptionMessage Link file not found. Please try again. * @return void */ - public function testCreateLinkWithMissingFileThrowsException(): void + public function testCreateLinkWithMissingLinkFileThrowsException(): void { $requestData = [ 'isGlobalScopeContent' => false, @@ -334,7 +334,7 @@ public function testCreateLinkWithMissingFileThrowsException(): void * @expectedExceptionMessage Link sample file not found. Please try again. * @return void */ - public function testCreateLinkWithMissingSampleThrowsException(): void + public function testCreateLinkWithMissingSampleFileThrowsException(): void { $requestData = [ 'isGlobalScopeContent' => false, From 686e79a0b3d9356012e9f1396efe351f54146a45 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Fri, 3 May 2019 09:45:50 -0500 Subject: [PATCH 0041/2437] MC-15811: Incorrect Url Fix static errors. --- .../Magento/Store/Model/StoreSwitcher/HashGenerator.php | 1 + .../integration/testsuite/Magento/Framework/UrlTest.php | 7 ------- .../testsuite/Magento/Store/Model/HashGeneratorTest.php | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 945b5f36a0a8c..ac195c699a96a 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -57,6 +57,7 @@ public function __construct( * @param StoreInterface $targetStore store where to go to * @param string $redirectUrl original url requested for redirect after switching * @return string redirect url + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string { diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index 21f0ebd5b77fc..9d07c07e824d4 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -485,13 +485,6 @@ public function testSessionUrlVar() $this->assertEquals('<a href="http://example.com/?SID=' . $sessionId . '">www.example.com</a>', $sessionUrl); } - public function testUseSessionIdForUrl() - { - $_SERVER['HTTP_HOST'] = 'localhost'; - $this->assertFalse($this->model->useSessionIdForUrl(true)); - $this->assertFalse($this->model->useSessionIdForUrl(false)); - } - /** * Note: isolation flushes the URL memory cache * @magentoAppIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 8b0cec3dfa20e..c56f2bf347a7b 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -21,7 +21,6 @@ * Test class for \Magento\Store\Model\StoreSwitcher\HashGenerator * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ - class HashGeneratorTest extends \PHPUnit\Framework\TestCase { /** From 315ee90a45065b5bd35e4e8a6a4a401e05c5768b Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Fri, 3 May 2019 12:17:00 -0500 Subject: [PATCH 0042/2437] MC-15977: Email template preview --- .../Block/Adminhtml/Template/Preview.php | 16 +- .../Block/Adminhtml/Template/PreviewTest.php | 163 +++++++++++++----- 2 files changed, 131 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php index acc367de742dd..5b2705a1989e8 100644 --- a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php @@ -55,19 +55,27 @@ public function __construct( * Prepare html output * * @return string + * @throws \Exception */ protected function _toHtml() { + /** @var $request \Magento\Framework\App\Request\Http */ + $request = $this->getRequest(); + + if(!$request->isSafeMethod()) { + throw new \Exception('Wrong request.'); + } + $storeId = $this->getAnyStoreView()->getId(); /** @var $template \Magento\Email\Model\Template */ $template = $this->_emailFactory->create(); - if ($id = (int)$this->getRequest()->getParam('id')) { + if ($id = (int)$request->getParam('id')) { $template->load($id); } else { - $template->setTemplateType($this->getRequest()->getParam('type')); - $template->setTemplateText($this->getRequest()->getParam('text')); - $template->setTemplateStyles($this->getRequest()->getParam('styles')); + $template->setTemplateType($request->getParam('type')); + $template->setTemplateText($request->getParam('text')); + $template->setTemplateStyles($request->getParam('styles')); } \Magento\Framework\Profiler::start($this->profilerName); diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php index b91d94edb589e..4363bf223157e 100644 --- a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php @@ -18,26 +18,42 @@ class PreviewTest extends \PHPUnit\Framework\TestCase const MALICIOUS_TEXT = 'test malicious'; + /** + * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + */ + protected $request; + + /** + * @var \Magento\Email\Block\Adminhtml\Template\Preview + */ + protected $preview; + + /** + * @var \Magento\Framework\Filter\Input\MaliciousCode|\PHPUnit_Framework_MockObject_MockObject + */ + protected $maliciousCode; + + /** + * @var \Magento\Email\Model\Template|\PHPUnit_Framework_MockObject_MockObject + */ + protected $template; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $storeManager; + /** * Init data */ protected function setUp() { $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - } - /** - * Check of processing email templates - * - * @param array $requestParamMap - * - * @dataProvider toHtmlDataProvider - * @param $requestParamMap - */ - public function testToHtml($requestParamMap) - { $storeId = 1; - $template = $this->getMockBuilder(\Magento\Email\Model\Template::class) + $designConfigData = []; + + $this->template = $this->getMockBuilder(\Magento\Email\Model\Template::class) ->setMethods([ 'setDesignConfig', 'getDesignConfig', @@ -48,36 +64,46 @@ public function testToHtml($requestParamMap) ]) ->disableOriginalConstructor() ->getMock(); - $template->expects($this->once()) + + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); + + $this->maliciousCode = $this->createPartialMock( + \Magento\Framework\Filter\Input\MaliciousCode::class, ['filter'] + ); + + $this->template->expects($this->once()) ->method('getProcessedTemplate') ->with($this->equalTo([])) ->willReturn(self::MALICIOUS_TEXT); - $designConfigData = []; - $template->expects($this->atLeastOnce()) - ->method('getDesignConfig') + + $this->template->method('getDesignConfig') ->willReturn(new \Magento\Framework\DataObject( $designConfigData )); + $emailFactory = $this->createPartialMock(\Magento\Email\Model\TemplateFactory::class, ['create']); $emailFactory->expects($this->any()) ->method('create') - ->willReturn($template); + ->willReturn($this->template); - $request = $this->createMock(\Magento\Framework\App\RequestInterface::class); - $request->expects($this->any())->method('getParam')->willReturnMap($requestParamMap); $eventManage = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); $scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); $design = $this->createMock(\Magento\Framework\View\DesignInterface::class); $store = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getId', '__wakeup']); - $store->expects($this->any())->method('getId')->willReturn($storeId); - $storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $storeManager->expects($this->atLeastOnce()) - ->method('getDefaultStoreView') + + $store->expects($this->any()) + ->method('getId') + ->willReturn($storeId); + + $this->storeManager->method('getDefaultStoreView') ->willReturn($store); - $storeManager->expects($this->any())->method('getDefaultStoreView')->willReturn(null); - $storeManager->expects($this->any())->method('getStores')->willReturn([$store]); + + $this->storeManager->expects($this->any())->method('getDefaultStoreView')->willReturn(null); + $this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]); $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) ->setConstructorArgs([ $scopeConfig @@ -87,36 +113,85 @@ public function testToHtml($requestParamMap) ->getMock(); $appState->expects($this->any()) ->method('emulateAreaCode') - ->with(\Magento\Email\Model\AbstractTemplate::DEFAULT_DESIGN_AREA, [$template, 'getProcessedTemplate']) - ->willReturn($template->getProcessedTemplate()); + ->with(\Magento\Email\Model\AbstractTemplate::DEFAULT_DESIGN_AREA, [$this->template, 'getProcessedTemplate']) + ->willReturn($this->template->getProcessedTemplate()); $context = $this->createPartialMock( \Magento\Backend\Block\Template\Context::class, ['getRequest', 'getEventManager', 'getScopeConfig', 'getDesignPackage', 'getStoreManager', 'getAppState'] ); - $context->expects($this->any())->method('getRequest')->willReturn($request); - $context->expects($this->any())->method('getEventManager')->willReturn($eventManage); - $context->expects($this->any())->method('getScopeConfig')->willReturn($scopeConfig); - $context->expects($this->any())->method('getDesignPackage')->willReturn($design); - $context->expects($this->any())->method('getStoreManager')->willReturn($storeManager); - $context->expects($this->once())->method('getAppState')->willReturn($appState); - - $maliciousCode = $this->createPartialMock(\Magento\Framework\Filter\Input\MaliciousCode::class, ['filter']); - $maliciousCode->expects($this->once()) - ->method('filter') - ->with($this->equalTo($requestParamMap[1][2])) - ->willReturn(self::MALICIOUS_TEXT); + $context->expects($this->any()) + ->method('getRequest') + ->willReturn($this->request); + $context->expects($this->any()) + ->method('getEventManager') + ->willReturn($eventManage); + $context->expects($this->any()) + ->method('getScopeConfig') + ->willReturn($scopeConfig); + $context->expects($this->any()) + ->method('getDesignPackage') + ->willReturn($design); + $context->expects($this->any()) + ->method('getStoreManager') + ->willReturn($this->storeManager); + $context->expects($this->once()) + ->method('getAppState') + ->willReturn($appState); /** @var \Magento\Email\Block\Adminhtml\Template\Preview $preview */ - $preview = $this->objectManagerHelper->getObject( + $this->preview = $this->objectManagerHelper->getObject( \Magento\Email\Block\Adminhtml\Template\Preview::class, [ 'context' => $context, - 'maliciousCode' => $maliciousCode, + 'maliciousCode' => $this->maliciousCode, 'emailFactory' => $emailFactory ] ); - $this->assertEquals(self::MALICIOUS_TEXT, $preview->toHtml()); + } + + /** + * Check of processing email templates + * + * @param array $requestParamMap + * + * @dataProvider toHtmlDataProvider + * @param $requestParamMap + */ + public function testToHtml($requestParamMap) + { + $this->request->expects($this->atLeastOnce()) + ->method('isSafeMethod') + ->willReturn(true); + $this->request->expects($this->any()) + ->method('getParam') + ->willReturnMap($requestParamMap); + $this->template + ->expects($this->atLeastOnce()) + ->method('getDesignConfig'); + $this->storeManager->expects($this->atLeastOnce()) + ->method('getDefaultStoreView'); + $this->maliciousCode->expects($this->once()) + ->method('filter') + ->with($this->equalTo($requestParamMap[1][2])) + ->willReturn(self::MALICIOUS_TEXT); + + $this->assertEquals(self::MALICIOUS_TEXT, $this->preview->toHtml()); + } + + /** + * @expectedException \Exception + */ + public function testToHtmlWithException() + { + $this->request->expects($this->atLeastOnce()) + ->method('isSafeMethod') + ->willReturn(false); + $this->template + ->expects($this->never()) + ->method('getDesignConfig'); + $this->expectException(\Exception::class); + $this->preview->toHtml(); } /** From 1e1b69be141d00ad3cbacdccb42b08593c5fe1fd Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Tue, 7 May 2019 13:21:24 -0500 Subject: [PATCH 0043/2437] MC-15977: Email template preview --- .../Block/Adminhtml/Template/Preview.php | 6 +-- .../Block/Adminhtml/Template/PreviewTest.php | 39 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php index 5b2705a1989e8..d9cd5ef7f0c84 100644 --- a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php @@ -55,15 +55,15 @@ public function __construct( * Prepare html output * * @return string - * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException */ protected function _toHtml() { /** @var $request \Magento\Framework\App\Request\Http */ $request = $this->getRequest(); - if(!$request->isSafeMethod()) { - throw new \Exception('Wrong request.'); + if (!$request->isSafeMethod()) { + throw new \Magento\Framework\Exception\LocalizedException(__('Wrong request.')); } $storeId = $this->getAnyStoreView()->getId(); diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php index 4363bf223157e..55757476ea825 100644 --- a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php @@ -72,7 +72,8 @@ protected function setUp() $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); $this->maliciousCode = $this->createPartialMock( - \Magento\Framework\Filter\Input\MaliciousCode::class, ['filter'] + \Magento\Framework\Filter\Input\MaliciousCode::class, + ['filter'] ); $this->template->expects($this->once()) @@ -113,31 +114,22 @@ protected function setUp() ->getMock(); $appState->expects($this->any()) ->method('emulateAreaCode') - ->with(\Magento\Email\Model\AbstractTemplate::DEFAULT_DESIGN_AREA, [$this->template, 'getProcessedTemplate']) + ->with( + \Magento\Email\Model\AbstractTemplate::DEFAULT_DESIGN_AREA, + [$this->template, 'getProcessedTemplate'] + ) ->willReturn($this->template->getProcessedTemplate()); $context = $this->createPartialMock( \Magento\Backend\Block\Template\Context::class, ['getRequest', 'getEventManager', 'getScopeConfig', 'getDesignPackage', 'getStoreManager', 'getAppState'] ); - $context->expects($this->any()) - ->method('getRequest') - ->willReturn($this->request); - $context->expects($this->any()) - ->method('getEventManager') - ->willReturn($eventManage); - $context->expects($this->any()) - ->method('getScopeConfig') - ->willReturn($scopeConfig); - $context->expects($this->any()) - ->method('getDesignPackage') - ->willReturn($design); - $context->expects($this->any()) - ->method('getStoreManager') - ->willReturn($this->storeManager); - $context->expects($this->once()) - ->method('getAppState') - ->willReturn($appState); + $context->expects($this->any())->method('getRequest')->willReturn($this->request); + $context->expects($this->any())->method('getEventManager')->willReturn($eventManage); + $context->expects($this->any())->method('getScopeConfig')->willReturn($scopeConfig); + $context->expects($this->any())->method('getDesignPackage')->willReturn($design); + $context->expects($this->any())->method('getStoreManager')->willReturn($this->storeManager); + $context->expects($this->once())->method('getAppState')->willReturn($appState); /** @var \Magento\Email\Block\Adminhtml\Template\Preview $preview */ $this->preview = $this->objectManagerHelper->getObject( @@ -180,7 +172,7 @@ public function testToHtml($requestParamMap) } /** - * @expectedException \Exception + * @expectedException \Magento\Framework\Exception\LocalizedException */ public function testToHtmlWithException() { @@ -190,7 +182,10 @@ public function testToHtmlWithException() $this->template ->expects($this->never()) ->method('getDesignConfig'); - $this->expectException(\Exception::class); + $this->expectException(\Magento\Framework\Exception\LocalizedException::class); + $this->expectExceptionMessage( + (string)__('Wrong request.') + ); $this->preview->toHtml(); } From 42c73b6d279f99b13d3166bc63da95cf8d289c64 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 7 May 2019 15:05:04 -0500 Subject: [PATCH 0044/2437] MC-6293: Fixed incorrect import behavior --- .../Model/Import/Uploader.php | 29 +++++++++--- .../Block/Adminhtml/Import/Edit/Form.php | 7 ++- .../Controller/Adminhtml/Import/Start.php | 44 +++++++++++++++++-- .../Magento/ImportExport/Model/Import.php | 29 +++++++++++- app/code/Magento/ImportExport/etc/config.xml | 1 + .../Model/Import/UploaderTest.php | 21 +++++++++ .../Magento/ImportExport/Model/ImportTest.php | 33 ++++++++++++-- 7 files changed, 147 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index 4ce1c0e39d6de..b5d7364dedc47 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -7,6 +7,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\ValidatorException; +use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\DriverPool; /** @@ -111,13 +113,18 @@ class Uploader extends \Magento\MediaStorage\Model\File\Uploader */ private $random; + /** + * @var Filesystem + */ + private $fileSystem; + /** * @param \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDb * @param \Magento\MediaStorage\Helper\File\Storage $coreFileStorage * @param \Magento\Framework\Image\AdapterFactory $imageFactory * @param \Magento\MediaStorage\Model\File\Validator\NotProtectedExtension $validator - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Filesystem\File\ReadFactory $readFactory + * @param Filesystem $filesystem + * @param Filesystem\File\ReadFactory $readFactory * @param string|null $filePath * @param \Magento\Framework\Math\Random|null $random * @throws \Magento\Framework\Exception\FileSystemException @@ -128,8 +135,8 @@ public function __construct( \Magento\MediaStorage\Helper\File\Storage $coreFileStorage, \Magento\Framework\Image\AdapterFactory $imageFactory, \Magento\MediaStorage\Model\File\Validator\NotProtectedExtension $validator, - \Magento\Framework\Filesystem $filesystem, - \Magento\Framework\Filesystem\File\ReadFactory $readFactory, + Filesystem $filesystem, + Filesystem\File\ReadFactory $readFactory, $filePath = null, \Magento\Framework\Math\Random $random = null ) { @@ -137,6 +144,7 @@ public function __construct( $this->_coreFileStorageDb = $coreFileStorageDb; $this->_coreFileStorage = $coreFileStorage; $this->_validator = $validator; + $this->fileSystem = $filesystem; $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); $this->_readFactory = $readFactory; if ($filePath !== null) { @@ -236,7 +244,18 @@ private function downloadFileFromUrl($url, $driver) */ protected function _setUploadFile($filePath) { - if (!$this->_directory->isReadable($filePath)) { + try { + $fullPath = $this->_directory->getAbsolutePath($filePath); + if ($this->getTmpDir()) { + $tmpDir = $this->fileSystem->getDirectoryReadByPath($this->_directory->getAbsolutePath($this->getTmpDir())); + } else { + $tmpDir = $this->_directory; + } + $readable = $tmpDir->isReadable($fullPath); + } catch (ValidatorException $exception) { + $readable = false; + } + if (!$readable) { throw new \Magento\Framework\Exception\LocalizedException( __('File \'%1\' was not found or has read restriction.', $filePath) ); diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index d6b96a28afcc9..c53ef36c8a2cc 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -231,8 +231,11 @@ protected function _prepareForm() 'required' => false, 'class' => 'input-text', 'note' => __( - 'For Type "Local Server" use relative path to Magento installation, - e.g. var/export, var/import, var/export/some/dir' + $this->escapeHtml( + 'For Type "Local Server" use relative path to <Magento installation>/' + .$this->_scopeConfig->getValue('general/file/import_images_base_dir') + .', e.g. product_images, import_images/batch1' + ) ), ] ); diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php index e850f6af86cf9..721ccabad9925 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php @@ -6,9 +6,14 @@ namespace Magento\ImportExport\Controller\Adminhtml\Import; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\ImportExport\Controller\Adminhtml\ImportResult as ImportResultController; use Magento\Framework\Controller\ResultFactory; use Magento\ImportExport\Model\Import; +use Magento\ImportExport\Model\ImportFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\App\Filesystem\DirectoryList; /** * Controller responsible for initiating the import process @@ -25,25 +30,50 @@ class Start extends ImportResultController implements HttpPostActionInterface */ private $exceptionMessageFactory; + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var ImportFactory + */ + private $importFactory; + + /** + * @var Filesystem + */ + private $fileSystem; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\ImportExport\Model\Report\ReportProcessorInterface $reportProcessor * @param \Magento\ImportExport\Model\History $historyModel * @param \Magento\ImportExport\Helper\Report $reportHelper - * @param \Magento\ImportExport\Model\Import $importModel + * @param Import $importModel * @param \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory + * @param ScopeConfigInterface|null $config + * @param ImportFactory|null $importFactory + * @param Filesystem|null $fileSystem + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\ImportExport\Model\Report\ReportProcessorInterface $reportProcessor, \Magento\ImportExport\Model\History $historyModel, \Magento\ImportExport\Helper\Report $reportHelper, - \Magento\ImportExport\Model\Import $importModel, - \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory + Import $importModel, + \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory, + ?ScopeConfigInterface $config = null, + ?ImportFactory $importFactory = null, + ?Filesystem $fileSystem = null ) { parent::__construct($context, $reportProcessor, $historyModel, $reportHelper); - $this->importModel = $importModel; + $this->exceptionMessageFactory = $exceptionMessageFactory; + $this->config = $config ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->importFactory = $importFactory ?? ObjectManager::getInstance()->get(ImportFactory::class); + $this->fileSystem = $fileSystem ?? ObjectManager::getInstance()->get(Filesystem::class); } /** @@ -53,6 +83,12 @@ public function __construct( */ public function execute() { + $imagesDirectoryPath = $this->config->getValue('general/file/import_images_base_dir'); + $imagesDirectory = $this->fileSystem->getDirectoryReadByPath( + $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath($imagesDirectoryPath) + ); + $this->importModel = $this->importFactory->create(['imagesTempDirectoryBase' => $imagesDirectory]); + $data = $this->getRequest()->getPostValue(); if ($data) { /** @var \Magento\Framework\View\Result\Layout $resultLayout */ diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 04f4111d3a0a8..1113165d4159f 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -13,6 +13,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Filesystem; use Magento\Framework\HTTP\Adapter\FileTransferFactory; use Magento\Framework\Indexer\IndexerRegistry; @@ -198,6 +199,11 @@ class Import extends AbstractModel */ private $random; + /** + * @var Filesystem\Directory\Read|null + */ + private $imagesTempDirectoryBase; + /** * @param LoggerInterface $logger * @param Filesystem $filesystem @@ -216,6 +222,7 @@ class Import extends AbstractModel * @param array $data * @param ManagerInterface|null $messageManager * @param Random|null $random + * @param Filesystem\Directory\Read|null $imagesTempDirectoryBase * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -235,7 +242,8 @@ public function __construct( DateTime $localeDate, array $data = [], ManagerInterface $messageManager = null, - Random $random = null + Random $random = null, + ?Filesystem\Directory\Read $imagesTempDirectoryBase = null ) { $this->_importExportData = $importExportData; $this->_coreConfig = $coreConfig; @@ -254,6 +262,7 @@ public function __construct( ->get(ManagerInterface::class); $this->random = $random ?: ObjectManager::getInstance() ->get(Random::class); + $this->imagesTempDirectoryBase = $imagesTempDirectoryBase; parent::__construct($logger, $filesystem, $data); } @@ -464,8 +473,24 @@ public function importSource() { $this->setData('entity', $this->getDataSourceModel()->getEntityTypeCode()); $this->setData('behavior', $this->getDataSourceModel()->getBehavior()); - $this->importHistoryModel->updateReport($this); + //Validating images temporary directory path if the constraint has been provided + if ($this->imagesTempDirectoryBase) { + if (!$this->imagesTempDirectoryBase->isReadable()) { + $rootWrite = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT); + $rootWrite->create($this->imagesTempDirectoryBase->getAbsolutePath()); + } + try { + $this->setData( + self::FIELD_NAME_IMG_FILE_DIR, + $this->imagesTempDirectoryBase->getAbsolutePath($this->getData(self::FIELD_NAME_IMG_FILE_DIR)) + ); + $this->_getEntityAdapter()->setParameters($this->getData()); + } catch (ValidatorException $exception) { + throw new LocalizedException(__('Images file directory is outside required directory'), $exception); + } + } + $this->importHistoryModel->updateReport($this); $this->addLogComment(__('Begin import of "%1" with "%2" behavior', $this->getEntity(), $this->getBehavior())); $result = $this->processImport(); diff --git a/app/code/Magento/ImportExport/etc/config.xml b/app/code/Magento/ImportExport/etc/config.xml index 7aee9bdd2fd6d..b8ce1c70ee16d 100644 --- a/app/code/Magento/ImportExport/etc/config.xml +++ b/app/code/Magento/ImportExport/etc/config.xml @@ -18,6 +18,7 @@ </available> </importexport_local_valid_paths> <bunch_size>100</bunch_size> + <import_images_base_dir>var/import/images</import_images_base_dir> </file> </general> <import> diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php index f61aa7578d4a3..a8672abd52c89 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php @@ -73,6 +73,27 @@ public function testMoveWithValidFile(): void $this->assertTrue($this->directory->isExist($this->uploader->getTmpDir() . '/' . $fileName)); } + /** + * Check validation against temporary directory. + * + * @magentoAppIsolation enabled + * @return void + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testMoveWithFileOutsideTemp(): void + { + $tmpDir = $this->uploader->getTmpDir(); + if (!$this->directory->create($newTmpDir = $tmpDir .'/test1')) { + throw new \RuntimeException('Failed to create temp dir'); + } + $this->uploader->setTmpDir($newTmpDir); + $fileName = 'magento_additional_image_one.jpg'; + $filePath = $this->directory->getAbsolutePath($tmpDir . '/' . $fileName); + copy(__DIR__ . '/_files/' . $fileName, $filePath); + $this->uploader->move('../' .$fileName); + $this->assertTrue($this->directory->isExist($tmpDir . '/' . $fileName)); + } + /** * @magentoAppIsolation enabled * @return void diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php index 0c1f2d2fcc8d7..0fa37f6a753b3 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php @@ -5,8 +5,11 @@ */ namespace Magento\ImportExport\Model; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; use Magento\Framework\Phrase; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; +use Magento\TestFramework\Helper\Bootstrap; /** * @magentoDataFixture Magento/ImportExport/_files/import_data.php @@ -65,13 +68,35 @@ class ImportTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->_importConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $this->_importConfig = Bootstrap::getObjectManager()->create( \Magento\ImportExport\Model\Import\Config::class ); - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + /** @var Filesystem $fileSystem */ + $fileSystem = Bootstrap::getObjectManager()->get(Filesystem::class); + $this->_model = Bootstrap::getObjectManager()->create( Import::class, - ['importConfig' => $this->_importConfig] + [ + 'importConfig' => $this->_importConfig, + 'imagesTempDirectoryBase' => $fileSystem->getDirectoryRead(DirectoryList::VAR_DIR) + ] + ); + } + + /** + * Test validation of images directory against provided base directory. + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Images file directory is outside required directory + * @return void + */ + public function testImagesDirBase(): void + { + $this->_model->setData( + Import::FIELD_NAME_VALIDATION_STRATEGY, + ProcessingErrorAggregatorInterface::VALIDATION_STRATEGY_SKIP_ERRORS ); + $this->_model->setData(Import::FIELD_NAME_IMG_FILE_DIR, '../_files'); + $this->_model->importSource(); } /** @@ -80,7 +105,7 @@ protected function setUp() public function testImportSource() { /** @var $customersCollection \Magento\Customer\Model\ResourceModel\Customer\Collection */ - $customersCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $customersCollection = Bootstrap::getObjectManager()->create( \Magento\Customer\Model\ResourceModel\Customer\Collection::class ); From 567e512064353792db50597028382fef256ef507 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 7 May 2019 18:33:26 -0500 Subject: [PATCH 0045/2437] MC-6293: Fixed incorrect import behavior --- .../Magento/CatalogImportExport/Model/Import/Uploader.php | 4 +++- .../ImportExport/Controller/Adminhtml/Import/Start.php | 2 ++ .../Magento/CatalogImportExport/Model/Import/UploaderTest.php | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index b5d7364dedc47..09c3cc4daf1d9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -247,7 +247,9 @@ protected function _setUploadFile($filePath) try { $fullPath = $this->_directory->getAbsolutePath($filePath); if ($this->getTmpDir()) { - $tmpDir = $this->fileSystem->getDirectoryReadByPath($this->_directory->getAbsolutePath($this->getTmpDir())); + $tmpDir = $this->fileSystem->getDirectoryReadByPath( + $this->_directory->getAbsolutePath($this->getTmpDir()) + ); } else { $tmpDir = $this->_directory; } diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php index 721ccabad9925..b18490505fee5 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php @@ -17,6 +17,8 @@ /** * Controller responsible for initiating the import process + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Start extends ImportResultController implements HttpPostActionInterface { diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php index a8672abd52c89..3961a77927314 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php @@ -68,6 +68,7 @@ public function testMoveWithValidFile(): void { $fileName = 'magento_additional_image_one.jpg'; $filePath = $this->directory->getAbsolutePath($this->uploader->getTmpDir() . '/' . $fileName); + //phpcs:ignore copy(__DIR__ . '/_files/' . $fileName, $filePath); $this->uploader->move($fileName); $this->assertTrue($this->directory->isExist($this->uploader->getTmpDir() . '/' . $fileName)); @@ -89,6 +90,7 @@ public function testMoveWithFileOutsideTemp(): void $this->uploader->setTmpDir($newTmpDir); $fileName = 'magento_additional_image_one.jpg'; $filePath = $this->directory->getAbsolutePath($tmpDir . '/' . $fileName); + //phpcs:ignore copy(__DIR__ . '/_files/' . $fileName, $filePath); $this->uploader->move('../' .$fileName); $this->assertTrue($this->directory->isExist($tmpDir . '/' . $fileName)); @@ -104,6 +106,7 @@ public function testMoveWithInvalidFile(): void { $fileName = 'media_import_image.php'; $filePath = $this->directory->getAbsolutePath($this->uploader->getTmpDir() . '/' . $fileName); + //phpcs:ignore copy(__DIR__ . '/_files/' . $fileName, $filePath); $this->uploader->move($fileName); $this->assertFalse($this->directory->isExist($this->uploader->getTmpDir() . '/' . $fileName)); From 293667e01dd4c76f59a66e34f320f0056f513998 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Thu, 9 May 2019 10:27:39 -0500 Subject: [PATCH 0046/2437] MC-15977: Email template preview --- app/code/Magento/Email/Block/Adminhtml/Template/Preview.php | 2 +- .../Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php index d9cd5ef7f0c84..5b7979c3d8607 100644 --- a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php @@ -62,7 +62,7 @@ protected function _toHtml() /** @var $request \Magento\Framework\App\Request\Http */ $request = $this->getRequest(); - if (!$request->isSafeMethod()) { + if (!$request instanceof \Magento\Framework\App\RequestSafetyInterface || !$request->isSafeMethod()) { throw new \Magento\Framework\Exception\LocalizedException(__('Wrong request.')); } diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php index 55757476ea825..8143995f3b2af 100644 --- a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php @@ -146,9 +146,7 @@ protected function setUp() * Check of processing email templates * * @param array $requestParamMap - * * @dataProvider toHtmlDataProvider - * @param $requestParamMap */ public function testToHtml($requestParamMap) { From 703f00d90ad97aaefb26a41dfd971a7885deeb60 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Thu, 9 May 2019 11:02:46 -0500 Subject: [PATCH 0047/2437] MC-15977: Email template preview --- app/code/Magento/Email/Block/Adminhtml/Template/Preview.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php index 5b7979c3d8607..4f0479a9573f4 100644 --- a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php @@ -59,7 +59,6 @@ public function __construct( */ protected function _toHtml() { - /** @var $request \Magento\Framework\App\Request\Http */ $request = $this->getRequest(); if (!$request instanceof \Magento\Framework\App\RequestSafetyInterface || !$request->isSafeMethod()) { From 24ee35853e53ba77b1954eb43829cbbdc982c2ed Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 9 May 2019 15:42:12 -0500 Subject: [PATCH 0048/2437] MC-15132: Update XML Design --- .../UiComponent/Argument/Interpreter/ConfigurableObject.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 35a5fffd45269..5135ad4d7f86d 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -61,7 +61,10 @@ public function evaluate(array $data) throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); } - if (in_array(ltrim($arguments['class'], '\\'), $this->classBlacklist)) { + if (in_array( + ltrim(strtolower($arguments['class']), '\\'), + array_map('strtolower', $this->classBlacklist) + )) { throw new \InvalidArgumentException(sprintf( 'Class argument is invalid: %s', $arguments['class'] From 6528d9abf3613433bebe8ec0b8e11327469483da Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 10 May 2019 11:45:24 -0500 Subject: [PATCH 0049/2437] MC-6293: Fixed incorrect import behavior --- .../Block/Adminhtml/Import/Edit/Form.php | 14 +++- .../Controller/Adminhtml/Import/Start.php | 41 +++--------- .../Magento/ImportExport/Model/Import.php | 27 ++++---- .../Import/ImageDirectoryBaseProvider.php | 64 +++++++++++++++++++ .../Magento/ImportExport/Model/ImportTest.php | 15 ++--- 5 files changed, 106 insertions(+), 55 deletions(-) create mode 100644 app/code/Magento/ImportExport/Model/Import/ImageDirectoryBaseProvider.php diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index c53ef36c8a2cc..af5377a6227ca 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -5,6 +5,7 @@ */ namespace Magento\ImportExport\Block\Adminhtml\Import\Edit; +use Magento\Framework\App\ObjectManager; use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; @@ -32,6 +33,11 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic */ protected $_behaviorFactory; + /** + * @var Import\ImageDirectoryBaseProvider + */ + private $imagesDirectoryProvider; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry @@ -40,6 +46,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic * @param \Magento\ImportExport\Model\Source\Import\EntityFactory $entityFactory * @param \Magento\ImportExport\Model\Source\Import\Behavior\Factory $behaviorFactory * @param array $data + * @param Import\ImageDirectoryBaseProvider|null $imageDirProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -48,12 +55,15 @@ public function __construct( \Magento\ImportExport\Model\Import $importModel, \Magento\ImportExport\Model\Source\Import\EntityFactory $entityFactory, \Magento\ImportExport\Model\Source\Import\Behavior\Factory $behaviorFactory, - array $data = [] + array $data = [], + ?Import\ImageDirectoryBaseProvider $imageDirProvider = null ) { $this->_entityFactory = $entityFactory; $this->_behaviorFactory = $behaviorFactory; parent::__construct($context, $registry, $formFactory, $data); $this->_importModel = $importModel; + $this->imagesDirectoryProvider = $imageDirProvider + ?? ObjectManager::getInstance()->get(Import\ImageDirectoryBaseProvider::class); } /** @@ -233,7 +243,7 @@ protected function _prepareForm() 'note' => __( $this->escapeHtml( 'For Type "Local Server" use relative path to <Magento installation>/' - .$this->_scopeConfig->getValue('general/file/import_images_base_dir') + .$this->imagesDirectoryProvider->getDirectoryRelativePath() .', e.g. product_images, import_images/batch1' ) ), diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php index b18490505fee5..0cc1fd40bf7e4 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php @@ -10,15 +10,10 @@ use Magento\ImportExport\Controller\Adminhtml\ImportResult as ImportResultController; use Magento\Framework\Controller\ResultFactory; use Magento\ImportExport\Model\Import; -use Magento\ImportExport\Model\ImportFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\Filesystem; -use Magento\Framework\App\Filesystem\DirectoryList; /** * Controller responsible for initiating the import process * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Start extends ImportResultController implements HttpPostActionInterface { @@ -33,19 +28,9 @@ class Start extends ImportResultController implements HttpPostActionInterface private $exceptionMessageFactory; /** - * @var ScopeConfigInterface + * @var Import\ImageDirectoryBaseProvider */ - private $config; - - /** - * @var ImportFactory - */ - private $importFactory; - - /** - * @var Filesystem - */ - private $fileSystem; + private $imagesDirProvider; /** * @param \Magento\Backend\App\Action\Context $context @@ -54,9 +39,7 @@ class Start extends ImportResultController implements HttpPostActionInterface * @param \Magento\ImportExport\Helper\Report $reportHelper * @param Import $importModel * @param \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory - * @param ScopeConfigInterface|null $config - * @param ImportFactory|null $importFactory - * @param Filesystem|null $fileSystem + * @param Import\ImageDirectoryBaseProvider|null $imageDirectoryBaseProvider * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( @@ -66,16 +49,14 @@ public function __construct( \Magento\ImportExport\Helper\Report $reportHelper, Import $importModel, \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory, - ?ScopeConfigInterface $config = null, - ?ImportFactory $importFactory = null, - ?Filesystem $fileSystem = null + ?Import\ImageDirectoryBaseProvider $imageDirectoryBaseProvider = null ) { parent::__construct($context, $reportProcessor, $historyModel, $reportHelper); + $this->importModel = $importModel; $this->exceptionMessageFactory = $exceptionMessageFactory; - $this->config = $config ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); - $this->importFactory = $importFactory ?? ObjectManager::getInstance()->get(ImportFactory::class); - $this->fileSystem = $fileSystem ?? ObjectManager::getInstance()->get(Filesystem::class); + $this->imagesDirProvider = $imageDirectoryBaseProvider + ?? ObjectManager::getInstance()->get(Import\ImageDirectoryBaseProvider::class); } /** @@ -85,12 +66,6 @@ public function __construct( */ public function execute() { - $imagesDirectoryPath = $this->config->getValue('general/file/import_images_base_dir'); - $imagesDirectory = $this->fileSystem->getDirectoryReadByPath( - $this->fileSystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath($imagesDirectoryPath) - ); - $this->importModel = $this->importFactory->create(['imagesTempDirectoryBase' => $imagesDirectory]); - $data = $this->getRequest()->getPostValue(); if ($data) { /** @var \Magento\Framework\View\Result\Layout $resultLayout */ @@ -104,6 +79,8 @@ public function execute() ->addAction('hide', ['edit_form', 'upload_button', 'messages']); $this->importModel->setData($data); + //Images can be read only from given directory. + $this->importModel->setData(Import::IMAGES_BASE_DIR, $this->imagesDirProvider->getDirectory()); $errorAggregator = $this->importModel->getErrorAggregator(); $errorAggregator->initValidationStrategy( $this->importModel->getData(Import::FIELD_NAME_VALIDATION_STRATEGY), diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 1113165d4159f..8d3bef38e3cbe 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -72,6 +72,11 @@ class Import extends AbstractModel */ const FIELD_NAME_IMG_FILE_DIR = 'import_images_file_dir'; + /** + * ReadInterface of the directory constraint for images. + */ + const IMAGES_BASE_DIR = 'images_base_directory'; + /** * Allowed errors count field name */ @@ -199,11 +204,6 @@ class Import extends AbstractModel */ private $random; - /** - * @var Filesystem\Directory\Read|null - */ - private $imagesTempDirectoryBase; - /** * @param LoggerInterface $logger * @param Filesystem $filesystem @@ -222,7 +222,6 @@ class Import extends AbstractModel * @param array $data * @param ManagerInterface|null $messageManager * @param Random|null $random - * @param Filesystem\Directory\Read|null $imagesTempDirectoryBase * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -242,8 +241,7 @@ public function __construct( DateTime $localeDate, array $data = [], ManagerInterface $messageManager = null, - Random $random = null, - ?Filesystem\Directory\Read $imagesTempDirectoryBase = null + Random $random = null ) { $this->_importExportData = $importExportData; $this->_coreConfig = $coreConfig; @@ -262,7 +260,6 @@ public function __construct( ->get(ManagerInterface::class); $this->random = $random ?: ObjectManager::getInstance() ->get(Random::class); - $this->imagesTempDirectoryBase = $imagesTempDirectoryBase; parent::__construct($logger, $filesystem, $data); } @@ -474,15 +471,19 @@ public function importSource() $this->setData('entity', $this->getDataSourceModel()->getEntityTypeCode()); $this->setData('behavior', $this->getDataSourceModel()->getBehavior()); //Validating images temporary directory path if the constraint has been provided - if ($this->imagesTempDirectoryBase) { - if (!$this->imagesTempDirectoryBase->isReadable()) { + if ($this->hasData(self::IMAGES_BASE_DIR) + && $this->getData(self::IMAGES_BASE_DIR) instanceof Filesystem\Directory\ReadInterface + ) { + /** @var Filesystem\Directory\ReadInterface $imagesDirectory */ + $imagesDirectory = $this->getData(self::IMAGES_BASE_DIR); + if (!$imagesDirectory->isReadable()) { $rootWrite = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT); - $rootWrite->create($this->imagesTempDirectoryBase->getAbsolutePath()); + $rootWrite->create($imagesDirectory->getAbsolutePath()); } try { $this->setData( self::FIELD_NAME_IMG_FILE_DIR, - $this->imagesTempDirectoryBase->getAbsolutePath($this->getData(self::FIELD_NAME_IMG_FILE_DIR)) + $imagesDirectory->getAbsolutePath($this->getData(self::FIELD_NAME_IMG_FILE_DIR)) ); $this->_getEntityAdapter()->setParameters($this->getData()); } catch (ValidatorException $exception) { diff --git a/app/code/Magento/ImportExport/Model/Import/ImageDirectoryBaseProvider.php b/app/code/Magento/ImportExport/Model/Import/ImageDirectoryBaseProvider.php new file mode 100644 index 0000000000000..9c90b57c30ea8 --- /dev/null +++ b/app/code/Magento/ImportExport/Model/Import/ImageDirectoryBaseProvider.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ImportExport\Model\Import; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem; +use Magento\Framework\App\Filesystem\DirectoryList; + +/** + * Provides base directory to use for images when user imports entities. + */ +class ImageDirectoryBaseProvider +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @param ScopeConfigInterface $config + * @param Filesystem $filesystem + */ + public function __construct(ScopeConfigInterface $config, Filesystem $filesystem) + { + $this->config = $config; + $this->filesystem = $filesystem; + } + + /** + * Directory that users are allowed to place images for importing. + * + * @return ReadInterface + */ + public function getDirectory(): ReadInterface + { + $path = $this->getDirectoryRelativePath(); + + return $this->filesystem->getDirectoryReadByPath( + $this->filesystem->getDirectoryRead(DirectoryList::ROOT)->getAbsolutePath($path) + ); + } + + /** + * The directory's path relative to Magento root. + * + * @return string + */ + public function getDirectoryRelativePath(): string + { + return $this->config->getValue('general/file/import_images_base_dir'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php index 0fa37f6a753b3..c6abf51c08580 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php @@ -5,10 +5,9 @@ */ namespace Magento\ImportExport\Model; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem; use Magento\Framework\Phrase; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; +use Magento\ImportExport\Model\Import\ImageDirectoryBaseProvider; use Magento\TestFramework\Helper\Bootstrap; /** @@ -24,7 +23,7 @@ class ImportTest extends \PHPUnit\Framework\TestCase protected $_model; /** - * @var \Magento\ImportExport\Model\Import\Config + * @var Import\Config */ protected $_importConfig; @@ -69,17 +68,17 @@ class ImportTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->_importConfig = Bootstrap::getObjectManager()->create( - \Magento\ImportExport\Model\Import\Config::class + Import\Config::class ); - /** @var Filesystem $fileSystem */ - $fileSystem = Bootstrap::getObjectManager()->get(Filesystem::class); + /** @var ImageDirectoryBaseProvider $provider */ + $provider = Bootstrap::getObjectManager()->get(ImageDirectoryBaseProvider::class); $this->_model = Bootstrap::getObjectManager()->create( Import::class, [ - 'importConfig' => $this->_importConfig, - 'imagesTempDirectoryBase' => $fileSystem->getDirectoryRead(DirectoryList::VAR_DIR) + 'importConfig' => $this->_importConfig ] ); + $this->_model->setData(Import::IMAGES_BASE_DIR, $provider->getDirectory()); } /** From d188d7f2dee04ef3a7b2e76c159e6e9ea25dc69f Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 10 May 2019 14:17:38 -0500 Subject: [PATCH 0050/2437] MC-6293: Fixed incorrect import behavior --- .../ImportExport/Controller/Adminhtml/Import/Start.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php index 0cc1fd40bf7e4..70e6f66329b70 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php @@ -12,8 +12,9 @@ use Magento\ImportExport\Model\Import; /** - * Controller responsible for initiating the import process + * Controller responsible for initiating the import process. * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Start extends ImportResultController implements HttpPostActionInterface { @@ -40,7 +41,6 @@ class Start extends ImportResultController implements HttpPostActionInterface * @param Import $importModel * @param \Magento\Framework\Message\ExceptionMessageFactoryInterface $exceptionMessageFactory * @param Import\ImageDirectoryBaseProvider|null $imageDirectoryBaseProvider - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( \Magento\Backend\App\Action\Context $context, From 5456745f8726eec4df2303d1e054a1ceed3cb74e Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 13 May 2019 09:57:08 -0500 Subject: [PATCH 0051/2437] MC-15811: Incorrect Url Use data object for token generation. Modify unit and integration tests. Fix static error. --- .../Store/Controller/Store/SwitchRequest.php | 8 +- .../Model/StoreSwitcher/HashGenerator.php | 13 ++- .../StoreSwitcher/HashGenerator/HashData.php | 44 +++++++++ .../testsuite/Magento/Framework/UrlTest.php | 14 +++ .../Magento/Store/Model/HashGeneratorTest.php | 97 ++++++++++++------- 5 files changed, 135 insertions(+), 41 deletions(-) create mode 100644 app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index e220941017c34..24e07137497b1 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -16,6 +16,7 @@ use \Magento\Framework\Exception\LocalizedException; use Magento\Framework\Url\DecoderInterface; use \Magento\Framework\App\ActionInterface; +use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; /** * Builds correct url to target store and performs redirect. @@ -78,7 +79,12 @@ public function execute() $error = null; $encodedUrl = (string)$this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED); $targetUrl = $this->urlDecoder->decode($encodedUrl); - $data=[$customerId, $timeStamp, $fromStoreCode]; + + $data = new HashData([ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ]); if ($targetUrl && $this->hashGenerator->validateHash($signature, $data)) { try { diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index ac195c699a96a..0fe47412537c8 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -14,6 +14,7 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Authorization\Model\UserContextInterface; use \Magento\Framework\App\ActionInterface; +use Magento\Framework\DataObject; /** * Generate one time token and build redirect url @@ -99,14 +100,16 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s * Validates one time token * * @param string $signature - * @param array $data + * @param DataObject $hashData * @return bool */ - public function validateHash(string $signature, array $data): bool + public function validateHash(string $signature, DataObject $hashData): bool { - if (!empty($signature) && !empty($data)) { - $timeStamp = $data[1] ?? 0; - $value = implode(",", $data); + if (!empty($signature) && !empty($hashData)) { + $timeStamp = $hashData->getTimestamp(); + $fromStoreCode = $hashData->getFromStoreCode(); + $customerId = $hashData->getCustomerId(); + $value = implode(",", [$customerId, $timeStamp, $fromStoreCode]); $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); if (time() - $timeStamp <= 5 && hash_equals($signature, hash_hmac('sha256', $value, $key))) { diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php new file mode 100644 index 0000000000000..19ca8fe7ddadc --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Store\Model\StoreSwitcher\HashGenerator; + +use Magento\Framework\DataObject; + +/** + * HashData object for one time token + */ +class HashData extends DataObject +{ + /** + * Get CustomerId + * + * @return int + */ + public function getCustomerId() + { + return $this->getData('customer_id'); + } + + /** + * Get Timestamp + * + * @return int + */ + public function getTimestamp() + { + return $this->getData('time_stamp'); + } + + /** + * Get Fromstore + * + * @return string + */ + public function getFromStoreCode() + { + return $this->getData('___from_store'); + } +} \ No newline at end of file diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index 9d07c07e824d4..ae91d5042d418 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -23,6 +23,13 @@ protected function setUp() $this->model = Bootstrap::getObjectManager()->create(\Magento\Framework\Url::class); } + public function testSetGetUseSession() + { + $this->assertFalse((bool)$this->model->getUseSession()); + $this->model->setUseSession(false); + $this->assertFalse($this->model->getUseSession()); + } + public function testSetRouteFrontName() { $value = 'route'; @@ -485,6 +492,13 @@ public function testSessionUrlVar() $this->assertEquals('<a href="http://example.com/?SID=' . $sessionId . '">www.example.com</a>', $sessionUrl); } + public function testUseSessionIdForUrl() + { + $_SERVER['HTTP_HOST'] = 'localhost'; + $this->assertFalse($this->model->useSessionIdForUrl(true)); + $this->assertFalse($this->model->useSessionIdForUrl(false)); + } + /** * Note: isolation flushes the URL memory cache * @magentoAppIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index c56f2bf347a7b..0eda33645b6bc 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -11,11 +11,11 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreSwitcher\HashGenerator; use Magento\Customer\Api\AccountManagementInterface; -use Magento\Customer\Model\Session; +use Magento\Customer\Model\Session as CustomerSession; use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Framework\Config\ConfigOptionsListConstants; -use \Magento\Framework\App\ActionInterface; use Magento\Framework\Url\Helper\Data as UrlHelper; +use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; /** * Test class for \Magento\Store\Model\StoreSwitcher\HashGenerator @@ -61,6 +61,16 @@ class HashGeneratorTest extends \PHPUnit\Framework\TestCase */ private $urlHelper; + /** + * @var HashData + */ + private $hashData; + + /** + * @var CustomerSession + */ + private $customerSession; + /** * Class dependencies initialization * @return void @@ -69,15 +79,15 @@ class HashGeneratorTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $session = $this->objectManager->create( - Session::class + $this->customerSession = $this->objectManager->create( + CustomerSession::class ); $this->accountManagement = $this->objectManager->create(AccountManagementInterface::class); $customer = $this->accountManagement->authenticate('customer@example.com', 'password'); - $session->setCustomerDataAsLoggedIn($customer); + $this->customerSession->setCustomerDataAsLoggedIn($customer); $this->customerSessionUserContext = $this->objectManager->create( \Magento\Customer\Model\Authorization\CustomerSessionUserContext::class, - ['customerSession' => $session] + ['customerSession' => $this->customerSession] ); $this->hashGenerator = $this->objectManager->create( StoreSwitcher\HashGenerator::class, @@ -87,46 +97,53 @@ protected function setUp() $this->deploymentConfig = $this->objectManager->get(DeploymentConfig::class); $this->key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); $this->urlHelper=$this->objectManager->create(UrlHelper::class); + $this->hashData=$this->objectManager->create(HashData::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->logout(); + parent:tearDown(); } /** + * @magentoAppIsolation enabled * @magentoDataFixture Magento/Store/_files/store.php * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Customer/_files/customer.php * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSwitch(): void { $redirectUrl = "http://domain.com/"; $fromStoreCode = 'test'; - $toStoreCode = 'fixture_second_store'; - $encodedUrl=$this->urlHelper->getEncodedUrl($redirectUrl); - /** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */ - $storeRepository = $this->objectManager->create(\Magento\Store\Api\StoreRepositoryInterface::class); - $fromStore = $storeRepository->get($fromStoreCode); - $toStore = $storeRepository->get($toStoreCode); + $fromStore = $this->createPartialMock(Store::class, ['getCode']); + $toStore = $this->createPartialMock(Store::class, ['getCode']); + $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); $timeStamp = time(); - $data = implode(',', [$this->customerId, $timeStamp, $fromStoreCode]); - $signature = hash_hmac('sha256', $data, $this->key); - $customerId = $this->customerId; + $targetUrl=$this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); + // phpcs:ignore + $urlParts=parse_url($targetUrl, PHP_URL_QUERY); + $signature=''; + // phpcs:ignore + parse_str($urlParts, $params); - $expectedUrl = "http://domain.com/stores/store/switchrequest"; - $expectedUrl = $this->urlHelper->addRequestParam( - $expectedUrl, - ['customer_id' => $customerId] - ); - $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['time_stamp' => $timeStamp]); - $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['signature' => $signature]); - $expectedUrl = $this->urlHelper->addRequestParam($expectedUrl, ['___from_store' => $fromStoreCode]); - $expectedUrl = $this->urlHelper->addRequestParam( - $expectedUrl, - [ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl] - ); - $this->assertEquals($expectedUrl, $this->hashGenerator->switch($fromStore, $toStore, $redirectUrl)); + if (isset($params['signature'])) { + $signature=$params['signature']; + } + $this->assertTrue($this->hashGenerator->validateHash($signature, new HashData([ + "customer_id" => $this->customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ]))); } /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer.php * @return void */ public function testValidateHashWithCorrectData(): void @@ -134,12 +151,18 @@ public function testValidateHashWithCorrectData(): void $timeStamp = time(); $customerId = $this->customerId; $fromStoreCode = 'test'; - $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); - $signature = hash_hmac('sha256', $data, $this->key); - $this->assertTrue($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])); + $data = new HashData([ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ]); + $signature = hash_hmac('sha256', implode(',', [$this->customerId, $timeStamp, $fromStoreCode]), $this->key); + $this->assertTrue($this->hashGenerator->validateHash($signature, $data)); } /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer.php * @return void */ public function testValidateHashWithInCorrectData(): void @@ -147,8 +170,12 @@ public function testValidateHashWithInCorrectData(): void $timeStamp = 0; $customerId = 8; $fromStoreCode = 'test'; - $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); - $signature = hash_hmac('sha256', $data, $this->key); - $this->assertFalse($this->hashGenerator->validateHash($signature, [$customerId, $timeStamp, $fromStoreCode])); + $data = new HashData([ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ]); + $signature = hash_hmac('sha256', implode(',', [$this->customerId, $timeStamp, $fromStoreCode]), $this->key); + $this->assertFalse($this->hashGenerator->validateHash($signature, $data)); } } From 27af1c05e295f842b9efdb57174b257cb27b6afb Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 13 May 2019 11:29:07 -0500 Subject: [PATCH 0052/2437] MC-6293: Fixed incorrect import behavior --- .../ImportExport/Controller/Adminhtml/Import/Start.php | 2 +- app/code/Magento/ImportExport/Model/Import.php | 6 +++--- .../testsuite/Magento/ImportExport/Model/ImportTest.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php index 70e6f66329b70..5f036e51f66c4 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Start.php @@ -80,7 +80,7 @@ public function execute() $this->importModel->setData($data); //Images can be read only from given directory. - $this->importModel->setData(Import::IMAGES_BASE_DIR, $this->imagesDirProvider->getDirectory()); + $this->importModel->setData('images_base_directory', $this->imagesDirProvider->getDirectory()); $errorAggregator = $this->importModel->getErrorAggregator(); $errorAggregator->initValidationStrategy( $this->importModel->getData(Import::FIELD_NAME_VALIDATION_STRATEGY), diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 8d3bef38e3cbe..cbc5cf8ceeb88 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -471,11 +471,11 @@ public function importSource() $this->setData('entity', $this->getDataSourceModel()->getEntityTypeCode()); $this->setData('behavior', $this->getDataSourceModel()->getBehavior()); //Validating images temporary directory path if the constraint has been provided - if ($this->hasData(self::IMAGES_BASE_DIR) - && $this->getData(self::IMAGES_BASE_DIR) instanceof Filesystem\Directory\ReadInterface + if ($this->hasData('images_base_directory') + && $this->getData('images_base_directory') instanceof Filesystem\Directory\ReadInterface ) { /** @var Filesystem\Directory\ReadInterface $imagesDirectory */ - $imagesDirectory = $this->getData(self::IMAGES_BASE_DIR); + $imagesDirectory = $this->getData('images_base_directory'); if (!$imagesDirectory->isReadable()) { $rootWrite = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT); $rootWrite->create($imagesDirectory->getAbsolutePath()); diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php index c6abf51c08580..0f92da2230f7c 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/ImportTest.php @@ -78,7 +78,7 @@ protected function setUp() 'importConfig' => $this->_importConfig ] ); - $this->_model->setData(Import::IMAGES_BASE_DIR, $provider->getDirectory()); + $this->_model->setData('images_base_directory', $provider->getDirectory()); } /** From 7faf67bb2f1f77782edb45e64bcbda5c3d6d449a Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 13 May 2019 11:35:40 -0500 Subject: [PATCH 0053/2437] MC-15811: Incorrect Url Fix typo in function call. --- .../testsuite/Magento/Store/Model/HashGeneratorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 0eda33645b6bc..3f8a1768a1faf 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -106,7 +106,7 @@ protected function setUp() protected function tearDown() { $this->customerSession->logout(); - parent:tearDown(); + parent::tearDown(); } /** From 98239dd88a95456412403a8f588975caabfc6367 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 13 May 2019 14:28:45 -0500 Subject: [PATCH 0054/2437] MC-15811: Incorrect Url Fix phpcs static errors. Fix integration test. --- .../Store/Model/StoreSwitcher/HashGenerator/HashData.php | 2 +- dev/tests/integration/testsuite/Magento/Framework/UrlTest.php | 1 + .../testsuite/Magento/Store/Model/HashGeneratorTest.php | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php index 19ca8fe7ddadc..988b889b2d9ba 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php @@ -41,4 +41,4 @@ public function getFromStoreCode() { return $this->getData('___from_store'); } -} \ No newline at end of file +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php index ae91d5042d418..db830d228201c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/UrlTest.php @@ -494,6 +494,7 @@ public function testSessionUrlVar() public function testUseSessionIdForUrl() { + // phpcs:ignore $_SERVER['HTTP_HOST'] = 'localhost'; $this->assertFalse($this->model->useSessionIdForUrl(true)); $this->assertFalse($this->model->useSessionIdForUrl(false)); diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 3f8a1768a1faf..aa3d8d36de29f 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -120,8 +120,8 @@ public function testSwitch(): void { $redirectUrl = "http://domain.com/"; $fromStoreCode = 'test'; - $fromStore = $this->createPartialMock(Store::class, ['getCode']); - $toStore = $this->createPartialMock(Store::class, ['getCode']); + $fromStore = $this->createMock(Store::class); + $toStore = $this->createMock(Store::class); $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); $timeStamp = time(); $targetUrl=$this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); From 337b7625740633ac10245a4b13894e4faa92c80c Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 13 May 2019 15:12:28 -0500 Subject: [PATCH 0055/2437] MC-6293: Fixed incorrect import behavior --- app/code/Magento/ImportExport/Model/Import.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index cbc5cf8ceeb88..c8a5921b41b12 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -72,11 +72,6 @@ class Import extends AbstractModel */ const FIELD_NAME_IMG_FILE_DIR = 'import_images_file_dir'; - /** - * ReadInterface of the directory constraint for images. - */ - const IMAGES_BASE_DIR = 'images_base_directory'; - /** * Allowed errors count field name */ From cdd3fe01612f6a32fb4d5dd7a617f02a233fc987 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 13 May 2019 16:05:14 -0500 Subject: [PATCH 0056/2437] MC-15811: Incorrect Url Remove unused fixture. --- .../testsuite/Magento/Store/Model/HashGeneratorTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index aa3d8d36de29f..0ff9f40e4c9cb 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -111,8 +111,6 @@ protected function tearDown() /** * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Store/_files/store.php - * @magentoDataFixture Magento/Store/_files/second_store.php * @magentoDataFixture Magento/Customer/_files/customer.php * @return void */ @@ -120,8 +118,8 @@ public function testSwitch(): void { $redirectUrl = "http://domain.com/"; $fromStoreCode = 'test'; - $fromStore = $this->createMock(Store::class); - $toStore = $this->createMock(Store::class); + $fromStore = $this->createPartialMock(Store::class, ['getCode']); + $toStore = $this->createPartialMock(Store::class, ['getCode']); $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); $timeStamp = time(); $targetUrl=$this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); From eda0fcd0bf6a030e502a981dde7f2848f8534b10 Mon Sep 17 00:00:00 2001 From: David Haecker <dhaecker@magento.com> Date: Tue, 14 May 2019 12:33:16 -0500 Subject: [PATCH 0057/2437] MC-16261: Failing MFTF Test in 2.3.3-develop: MAGETWO-96164: Checking Catalog grid page number after Save and Close action - Fixing product grid reset in AdminGridPageNumberAfterSaveAndCloseActionTest --- .../Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml index b24ed7f9c9a81..aff5e89166328 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml @@ -24,9 +24,7 @@ <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" - dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridToDefaultView"/> <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteProductIfTheyExist"/> <createData stepKey="category1" entity="SimpleSubCategory"/> <createData stepKey="product1" entity="SimpleProduct"> From c22cbba6b5c022b4fdcb23b38a8462b22bb4187d Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 14 May 2019 17:58:16 +0300 Subject: [PATCH 0058/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Added API-functional test --- .../Sales/Service/V1/ShipmentAddTrackTest.php | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php index 054f91a295fd9..e170cd96ea82e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Service\V1; use Magento\Framework\Webapi\Rest\Request; @@ -74,6 +76,48 @@ public function testShipmentAddTrack() self::assertNotEmpty($result[ShipmentTrackInterface::ENTITY_ID]); self::assertEquals($shipment->getId(), $result[ShipmentTrackInterface::PARENT_ID]); } + + /** + * Shipment Tracking throw an error if order doesn't exist. + * + * @magentoApiDataFixture Magento/Sales/_files/shipment.php + */ + public function testShipmentTrackWithFailedOrderId() + { + /** @var \Magento\Sales\Model\Order $order */ + $orderCollection = $this->objectManager->get(\Magento\Sales\Model\ResourceModel\Order\Collection::class); + $order = $orderCollection->getLastItem(); + $failedOrderId = $order->getId() + 999999; + $shipmentCollection = $this->objectManager->get(Collection::class); + /** @var \Magento\Sales\Model\Order\Shipment $shipment */ + $shipment = $shipmentCollection->getFirstItem(); + $trackData = [ + ShipmentTrackInterface::ENTITY_ID => null, + ShipmentTrackInterface::ORDER_ID => $failedOrderId, + ShipmentTrackInterface::PARENT_ID => $shipment->getId(), + ShipmentTrackInterface::WEIGHT => 20, + ShipmentTrackInterface::QTY => 5, + ShipmentTrackInterface::TRACK_NUMBER => 2, + ShipmentTrackInterface::DESCRIPTION => 'Shipment description', + ShipmentTrackInterface::TITLE => 'Shipment title', + ShipmentTrackInterface::CARRIER_CODE => Track::CUSTOM_CARRIER_CODE, + ]; + $expectedMessage = 'The entity that was requested doesn\'t exist. Verify the entity and try again.'; + + try { + $this->_webApiCall($this->getServiceInfo(), ['entity' => $trackData]); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + 'SoapFault does not contain expected message.' + ); + } catch (\Exception $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + } + } + /** * Returns details about API endpoints and services. * From 24889de9b1cf149b64fa0bd29d0ae64946e9cefd Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 15 May 2019 17:58:43 +0400 Subject: [PATCH 0059/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Added automated test script --- .../ActionGroup/AdminCategoryActionGroup.xml | 12 ++ .../AdminCategorySidebarTreeSection.xml | 1 + ...goryWithRestrictedUrlKeyNotCreatedTest.xml | 125 ++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 90d732c9654e1..a944aa72e5ccd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -139,6 +139,18 @@ <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories"/> <dontSee selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="dontSeeCategoryInTree"/> </actionGroup> + <actionGroup name="deleteCategoryByName" extends="DeleteCategory"> + <arguments> + <argument name="categoryName" type="string" defaultValue="category1"/> + </arguments> + <remove keyForRemoval="clickCategoryLink"/> + <remove keyForRemoval="dontSeeCategoryInTree"/> + <remove keyForRemoval="expandToSeeAllCategories"/> + <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategories" after="waitForCategoryPageLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="clickCategory" after="expandCategories"/> + <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategoriesToSeeAll" after="seeDeleteSuccess"/> + <dontSee selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="dontSeeCategory" after="expandCategoriesToSeeAll"/> + </actionGroup> <!-- Actions to fill out a new category from the product page--> <!-- The action assumes that you are already on an admin product configuration page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml index fba28b3feaff1..c6f669ce5c13f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml @@ -17,5 +17,6 @@ <element name="lastCreatedCategory" type="block" selector=".x-tree-root-ct li li:last-child" /> <element name="treeContainer" type="block" selector=".tree-holder" /> <element name="expandRootCategory" type="text" selector="img.x-tree-elbow-end-plus"/> + <element name="categoryByName" type="text" selector="//div[contains(@class, 'categories-side-col')]//a/span[contains(text(), '{{categoryName}}')]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml new file mode 100644 index 0000000000000..58997aa7a5182 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCategoryWithRestrictedUrlKeyNotCreatedTest"> + <annotations> + <features value="CatalogUrlRewrite"/> + <title value="Category with restricted Url Key cannot be created"/> + <description value="Category with restricted Url Key cannot be created"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-71221"/> + <useCaseId value="MAGETWO-69825"/> + <group value="CatalogUrlRewrite"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete created categories--> + <comment userInput="Delete created categories" stepKey="commentDeleteCreatedCategories"/> + <actionGroup ref="deleteCategoryByName" stepKey="deleteAdminCategory"> + <argument name="categoryName" value="admin"/> + </actionGroup> + <actionGroup ref="deleteCategoryByName" stepKey="deleteSoapCategory"> + <argument name="categoryName" value="soap"/> + </actionGroup> + <actionGroup ref="deleteCategoryByName" stepKey="deleteRestCategory"> + <argument name="categoryName" value="rest"/> + </actionGroup> + <actionGroup ref="deleteCategoryByName" stepKey="deleteGraphQlCategory"> + <argument name="categoryName" value="graphql"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Check category creation with restricted url key 'admin'--> + <comment userInput="Check category creation with restricted url key 'admin'" stepKey="commentCheckAdminCategoryCreation"/> + <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateAdminCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminFirstCategoryForm"> + <argument name="categoryName" value="admin"/> + <argument name="categoryUrlKey" value=""/> + </actionGroup> + <see userInput='URL key "admin" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeAdminFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminSecondCategoryForm"> + <argument name="categoryName" value="{{SimpleSubCategory.name}}"/> + <argument name="categoryUrlKey" value="admin"/> + </actionGroup> + <see userInput='URL key "admin" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeAdminSecondErrorMessage"/> + <!--Create category with 'admin' name--> + <comment userInput="Create category with 'admin' name" stepKey="commentAdminCategoryCreation"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminThirdCategoryForm"> + <argument name="categoryName" value="admin"/> + <argument name="categoryUrlKey" value="{{SimpleSubCategory.name}}"/> + </actionGroup> + <see userInput="You saved the category." stepKey="seeAdminSuccessMessage"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('admin')}}" stepKey="seeAdminCategoryInTree"/> + <!--Check category creation with restricted url key 'soap'--> + <comment userInput="Check category creation with restricted url key 'soap'" stepKey="commentCheckSoapCategoryCreation"/> + <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateSoapCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapFirstCategoryForm"> + <argument name="categoryName" value="soap"/> + <argument name="categoryUrlKey" value=""/> + </actionGroup> + <see userInput='URL key "soap" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeSoapFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapSecondCategoryForm"> + <argument name="categoryName" value="{{ApiCategory.name}}"/> + <argument name="categoryUrlKey" value="soap"/> + </actionGroup> + <see userInput='URL key "soap" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeSoapSecondErrorMessage"/> + <!--Create category with 'soap' name--> + <comment userInput="Create category with 'soap' name" stepKey="commentSoapCategoryCreation"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapThirdCategoryForm"> + <argument name="categoryName" value="soap"/> + <argument name="categoryUrlKey" value="{{ApiCategory.name}}"/> + </actionGroup> + <see userInput="You saved the category." stepKey="seeSoapSuccessMessage"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('soap')}}" stepKey="seeSoapCategoryInTree"/> + <!--Check category creation with restricted url key 'rest'--> + <comment userInput="Check category creation with restricted url key 'rest'" stepKey="commentCheckRestCategoryCreation"/> + <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateRestCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestFirstCategoryForm"> + <argument name="categoryName" value="rest"/> + <argument name="categoryUrlKey" value=""/> + </actionGroup> + <see userInput='URL key "rest" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeRestFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestSecondCategoryForm"> + <argument name="categoryName" value="{{SubCategoryWithParent.name}}"/> + <argument name="categoryUrlKey" value="rest"/> + </actionGroup> + <see userInput='URL key "rest" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeRestSecondErrorMessage"/> + <!--Create category with 'rest' name--> + <comment userInput="Create category with 'rest' name" stepKey="commentRestCategoryCreation"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestThirdCategoryForm"> + <argument name="categoryName" value="rest"/> + <argument name="categoryUrlKey" value="{{SubCategoryWithParent.name}}"/> + </actionGroup> + <see userInput="You saved the category." stepKey="seeRestSuccessMesdgssage"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('rest')}}" stepKey="seeRestCategoryInTree"/> + <!--Check category creation with restricted url key 'graphql'--> + <comment userInput="Check category creation with restricted url key 'graphql'" stepKey="commentCheckGraphQlCategoryCreation"/> + <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlFirstCategoryForm"> + <argument name="categoryName" value="graphql"/> + <argument name="categoryUrlKey" value=""/> + </actionGroup> + <see userInput='URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlSecondCategoryForm"> + <argument name="categoryName" value="{{NewSubCategoryWithParent.name}}"/> + <argument name="categoryUrlKey" value="graphql"/> + </actionGroup> + <see userInput='URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlSecondErrorMessage"/> + <!--Create category with 'graphql' name--> + <comment userInput="Create category with 'graphql' name" stepKey="commentGraphQlCategoryCreation"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlThirdCategoryForm"> + <argument name="categoryName" value="graphql"/> + <argument name="categoryUrlKey" value="{{NewSubCategoryWithParent.name}}"/> + </actionGroup> + <see userInput="You saved the category." stepKey="seeGraphQlSuccessMessage"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('graphql')}}" stepKey="seeGraphQlCategoryInTree"/> + </test> +</tests> From fef28787f80873a508acdc404401c48f81bd2db4 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 15 May 2019 10:33:36 -0500 Subject: [PATCH 0060/2437] MC-15132: Update XML Design --- .../UiComponent/Argument/Interpreter/ConfigurableObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 5135ad4d7f86d..2810db6569421 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -43,7 +43,7 @@ public function __construct(ObjectManagerInterface $objectManager, InterpreterIn } /** - * {@inheritdoc} + * @inheritdoc */ public function evaluate(array $data) { From f5bf195926f907ac9f6fe1b4e56cbe730c1dfbaf Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Wed, 15 May 2019 12:46:47 -0500 Subject: [PATCH 0061/2437] MC-15811: Incorrect Url Fix method signature. Add type hint. Fix integration test. --- .../Model/StoreSwitcher/HashGenerator.php | 6 +-- .../StoreSwitcher/HashGenerator/HashData.php | 14 ++++--- .../Magento/Store/Model/HashGeneratorTest.php | 42 +++++++++---------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 0fe47412537c8..456941bd41c25 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -8,13 +8,13 @@ namespace Magento\Store\Model\StoreSwitcher; use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; use Magento\Store\Model\StoreSwitcherInterface; use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Authorization\Model\UserContextInterface; use \Magento\Framework\App\ActionInterface; -use Magento\Framework\DataObject; /** * Generate one time token and build redirect url @@ -100,10 +100,10 @@ public function switch(StoreInterface $fromStore, StoreInterface $targetStore, s * Validates one time token * * @param string $signature - * @param DataObject $hashData + * @param HashData $hashData * @return bool */ - public function validateHash(string $signature, DataObject $hashData): bool + public function validateHash(string $signature, HashData $hashData): bool { if (!empty($signature) && !empty($hashData)) { $timeStamp = $hashData->getTimestamp(); diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php index 988b889b2d9ba..2d0068efbdd92 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator/HashData.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Store\Model\StoreSwitcher\HashGenerator; use Magento\Framework\DataObject; @@ -17,9 +19,9 @@ class HashData extends DataObject * * @return int */ - public function getCustomerId() + public function getCustomerId(): int { - return $this->getData('customer_id'); + return (int)$this->getData('customer_id'); } /** @@ -27,9 +29,9 @@ public function getCustomerId() * * @return int */ - public function getTimestamp() + public function getTimestamp(): int { - return $this->getData('time_stamp'); + return (int)$this->getData('time_stamp'); } /** @@ -37,8 +39,8 @@ public function getTimestamp() * * @return string */ - public function getFromStoreCode() + public function getFromStoreCode(): string { - return $this->getData('___from_store'); + return (string)$this->getData('___from_store'); } } diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 0ff9f40e4c9cb..226cacd71195e 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -121,7 +121,6 @@ public function testSwitch(): void $fromStore = $this->createPartialMock(Store::class, ['getCode']); $toStore = $this->createPartialMock(Store::class, ['getCode']); $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); - $timeStamp = time(); $targetUrl=$this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); // phpcs:ignore $urlParts=parse_url($targetUrl, PHP_URL_QUERY); @@ -132,32 +131,16 @@ public function testSwitch(): void if (isset($params['signature'])) { $signature=$params['signature']; } + $this->assertEquals($params['customer_id'], $this->customerId); + $this->assertEquals($params['___from_store'], $fromStoreCode); + $this->assertTrue($this->hashGenerator->validateHash($signature, new HashData([ "customer_id" => $this->customerId, - "time_stamp" => $timeStamp, + "time_stamp" => $params['time_stamp'], "___from_store" => $fromStoreCode ]))); } - /** - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer.php - * @return void - */ - public function testValidateHashWithCorrectData(): void - { - $timeStamp = time(); - $customerId = $this->customerId; - $fromStoreCode = 'test'; - $data = new HashData([ - "customer_id" => $customerId, - "time_stamp" => $timeStamp, - "___from_store" => $fromStoreCode - ]); - $signature = hash_hmac('sha256', implode(',', [$this->customerId, $timeStamp, $fromStoreCode]), $this->key); - $this->assertTrue($this->hashGenerator->validateHash($signature, $data)); - } - /** * @magentoAppIsolation enabled * @magentoDataFixture Magento/Customer/_files/customer.php @@ -167,13 +150,26 @@ public function testValidateHashWithInCorrectData(): void { $timeStamp = 0; $customerId = 8; - $fromStoreCode = 'test'; + $fromStoreCode = 'store1'; $data = new HashData([ "customer_id" => $customerId, "time_stamp" => $timeStamp, "___from_store" => $fromStoreCode ]); - $signature = hash_hmac('sha256', implode(',', [$this->customerId, $timeStamp, $fromStoreCode]), $this->key); + $redirectUrl = "http://domain.com/"; + $fromStore = $this->createPartialMock(Store::class, ['getCode']); + $toStore = $this->createPartialMock(Store::class, ['getCode']); + $fromStore->expects($this->once())->method('getCode')->willReturn($fromStoreCode); + $targetUrl = $this->hashGenerator->switch($fromStore, $toStore, $redirectUrl); + // phpcs:ignore + $urlParts = parse_url($targetUrl,PHP_URL_QUERY); + $signature = ''; + // phpcs:ignore + parse_str($urlParts, $params); + + if (isset($params['signature'])) { + $signature = $params['signature']; + } $this->assertFalse($this->hashGenerator->validateHash($signature, $data)); } } From d4c0c0d755119e87e7208f2949d8ac9dca8ba81d Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Thu, 16 May 2019 15:27:23 -0500 Subject: [PATCH 0062/2437] MC-15811: Incorrect Url Fix annotation. --- lib/internal/Magento/Framework/Session/SidResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Session/SidResolver.php b/lib/internal/Magento/Framework/Session/SidResolver.php index 117c46f3443a7..041995d20fcd2 100644 --- a/lib/internal/Magento/Framework/Session/SidResolver.php +++ b/lib/internal/Magento/Framework/Session/SidResolver.php @@ -11,7 +11,7 @@ /** * Class SidResolver - * @deprecated 2.3.2 + * @deprecated 2.3.3 SIDs in URLs are no longer used */ class SidResolver implements SidResolverInterface { From adce9d933f26541b906b2161e9b9e1551586f94a Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Thu, 23 May 2019 14:01:07 +0300 Subject: [PATCH 0063/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Added API-functional test --- .../Magento/Sales/Service/V1/ShipmentAddTrackTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php index e170cd96ea82e..c25956a4858ec 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php @@ -81,13 +81,15 @@ public function testShipmentAddTrack() * Shipment Tracking throw an error if order doesn't exist. * * @magentoApiDataFixture Magento/Sales/_files/shipment.php + * @magentoApiDataFixture Magento/Sales/_files/order_list.php */ public function testShipmentTrackWithFailedOrderId() { /** @var \Magento\Sales\Model\Order $order */ $orderCollection = $this->objectManager->get(\Magento\Sales\Model\ResourceModel\Order\Collection::class); $order = $orderCollection->getLastItem(); - $failedOrderId = $order->getId() + 999999; + // Order ID from Magento/Sales/_files/order_list.php + $failedOrderId = $order->getId(); $shipmentCollection = $this->objectManager->get(Collection::class); /** @var \Magento\Sales\Model\Order\Shipment $shipment */ $shipment = $shipmentCollection->getFirstItem(); @@ -102,7 +104,7 @@ public function testShipmentTrackWithFailedOrderId() ShipmentTrackInterface::TITLE => 'Shipment title', ShipmentTrackInterface::CARRIER_CODE => Track::CUSTOM_CARRIER_CODE, ]; - $expectedMessage = 'The entity that was requested doesn\'t exist. Verify the entity and try again.'; + $expectedMessage = 'The shipment doesn\'t belong to the order.'; try { $this->_webApiCall($this->getServiceInfo(), ['entity' => $trackData]); From 66c4798fb31a0999358d7e19d3dce073665e304e Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Thu, 23 May 2019 18:32:12 +0300 Subject: [PATCH 0064/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - The translate for exception was added --- .../Model/Order/Shipment/TrackRepository.php | 24 +++++++++++++------ app/code/Magento/Sales/i18n/en_US.csv | 1 + 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index b9911518ad58c..c8fc9e5fc5600 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -16,8 +16,9 @@ use Magento\Sales\Api\Data\ShipmentTrackSearchResultInterfaceFactory; use Magento\Sales\Api\ShipmentTrackRepositoryInterface; use Magento\Sales\Model\Spi\ShipmentTrackResourceInterface; -use \Magento\Sales\Model\OrderRepository; +use \Magento\Sales\Api\OrderRepositoryInterface; use \Magento\Framework\App\ObjectManager; +use Psr\Log\LoggerInterface; /** * Repository of shipment tracking information @@ -45,31 +46,39 @@ class TrackRepository implements ShipmentTrackRepositoryInterface private $collectionProcessor; /** - * @var OrderRepository + * @var OrderRepositoryInterface */ private $orderRepository; + /** + * @var LoggerInterface + */ + private $logger; + /** * @param ShipmentTrackResourceInterface $trackResource * @param ShipmentTrackInterfaceFactory $trackFactory * @param ShipmentTrackSearchResultInterfaceFactory $searchResultFactory * @param CollectionProcessorInterface $collectionProcessor - * @param OrderRepository $orderRepository + * @param OrderRepositoryInterface|null $orderRepository + * @param LoggerInterface|null $logger */ public function __construct( ShipmentTrackResourceInterface $trackResource, ShipmentTrackInterfaceFactory $trackFactory, ShipmentTrackSearchResultInterfaceFactory $searchResultFactory, CollectionProcessorInterface $collectionProcessor, - OrderRepository $orderRepository = null + OrderRepositoryInterface $orderRepository = null, + LoggerInterface $logger = null ) { - $this->trackResource = $trackResource; $this->trackFactory = $trackFactory; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor; $this->orderRepository = $orderRepository ?: - ObjectManager::getInstance()->get(OrderRepository::class); + ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->logger = $logger ?: + ObjectManager::getInstance()->get(LoggerInterface::class); } /** @@ -118,7 +127,8 @@ public function save(ShipmentTrackInterface $entity) } if (array_search($entity['parent_id'], $shipmentId) === false) { - throw new CouldNotSaveException(__('The shipment doesn\'t belong to the order.')); + $this->logger->error('The shipment doesn\'t belong to the order.'); + throw new CouldNotSaveException(__('Could not save the shipment tracking.')); } try { diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index c5657f3a309f7..662781204f78b 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -797,3 +797,4 @@ Created,Created Refunds,Refunds "Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" "Allow Zero GrandTotal","Allow Zero GrandTotal" +"Could not save the shipment tracking" \ No newline at end of file From 40e805e8535eefcfcb7a3364fb0ff6e16a66a313 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Thu, 23 May 2019 15:59:52 -0500 Subject: [PATCH 0065/2437] MC-16865: Outdated js libraries - Updated js library --- lib/web/knockoutjs/knockout.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/web/knockoutjs/knockout.js b/lib/web/knockoutjs/knockout.js index cd11f4d0c0d14..93ccdc5415111 100644 --- a/lib/web/knockoutjs/knockout.js +++ b/lib/web/knockoutjs/knockout.js @@ -502,16 +502,6 @@ ko.utils = (function () { setElementName: function(element, name) { element.name = name; - - // Workaround IE 6/7 issue - // - https://github.com/SteveSanderson/knockout/issues/197 - // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/ - if (ieVersion <= 7) { - try { - element.mergeAttributes(document.createElement("<input name='" + element.name + "'/>"), false); - } - catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View" - } }, forceRefresh: function(node) { From ec7f7b8708e1014e5e742bc1715ca3bf77cf1637 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 17 May 2019 13:20:51 -0500 Subject: [PATCH 0066/2437] MC-15733: firefox method update - Updated method for firefox consumption - Updated test for the change --- lib/internal/Magento/Framework/Escaper.php | 2 +- lib/internal/Magento/Framework/Test/Unit/EscaperTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index b12548f27bc8b..b7daadb04daba 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -135,7 +135,7 @@ private function removeNotAllowedTags(\DOMDocument $domDocument, array $allowedT . '\']' ); foreach ($nodes as $node) { - if ($node->nodeName != '#text' && $node->nodeName != '#comment') { + if ($node->nodeName != '#text') { $node->parentNode->replaceChild($domDocument->createTextNode($node->textContent), $node); } } diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index 7b45765fdefe8..a1d03f023a20c 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -224,7 +224,7 @@ public function escapeHtmlDataProvider() ], 'text with html comment' => [ 'data' => 'Only <span><b>2</b></span> in stock <!-- HTML COMMENT -->', - 'expected' => 'Only <span><b>2</b></span> in stock <!-- HTML COMMENT -->', + 'expected' => 'Only <span><b>2</b></span> in stock HTML COMMENT ', 'allowedTags' => ['span', 'b'], ], 'text with non ascii characters' => [ From 4b0823380c32a29aa3f1fec12aa2dabd4a608b97 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 17 May 2019 14:32:22 -0500 Subject: [PATCH 0067/2437] MC-15733: firefox method update - Resolved static failures --- lib/internal/Magento/Framework/Escaper.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index b7daadb04daba..29e285114fa05 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -6,6 +6,8 @@ namespace Magento\Framework; +use Magento\Framework\Exception\LocalizedException; + /** * Magento escape methods * @@ -74,7 +76,7 @@ public function escapeHtml($data, $allowedTags = null) $domDocument = new \DOMDocument('1.0', 'UTF-8'); set_error_handler( function ($errorNumber, $errorString) { - throw new \Exception($errorString, $errorNumber); + throw new LocalizedException($errorString, $errorNumber); } ); $data = $this->prepareUnescapedCharacters($data); From ebc9622a6d09f505275ee8992233c83ceaed1184 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 17 May 2019 15:31:09 -0500 Subject: [PATCH 0068/2437] MC-15733: firefox method update - Resolved static failures --- lib/internal/Magento/Framework/Escaper.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index 29e285114fa05..fac4d9445c052 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -6,8 +6,6 @@ namespace Magento\Framework; -use Magento\Framework\Exception\LocalizedException; - /** * Magento escape methods * @@ -76,7 +74,7 @@ public function escapeHtml($data, $allowedTags = null) $domDocument = new \DOMDocument('1.0', 'UTF-8'); set_error_handler( function ($errorNumber, $errorString) { - throw new LocalizedException($errorString, $errorNumber); + throw new \InvalidArgumentException($errorString, $errorNumber); } ); $data = $this->prepareUnescapedCharacters($data); From 73381b2f4bddf5949d798fef4b98017e7fe22c36 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Mon, 20 May 2019 11:29:33 -0500 Subject: [PATCH 0069/2437] MC-15733: firefox method update - Added method for firefox consumption - Updated test for the change --- lib/internal/Magento/Framework/Escaper.php | 16 ++++++++++++++++ .../Magento/Framework/Test/Unit/EscaperTest.php | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index fac4d9445c052..9eec5aec530a7 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -89,6 +89,7 @@ function ($errorNumber, $errorString) { } restore_error_handler(); + $this->removeComments($domDocument); $this->removeNotAllowedTags($domDocument, $allowedTags); $this->removeNotAllowedAttributes($domDocument); $this->escapeText($domDocument); @@ -158,6 +159,21 @@ private function removeNotAllowedAttributes(\DOMDocument $domDocument) } } + /** + * Remove comments + * + * @param \DOMDocument $domDocument + * @return void + */ + private function removeComments(\DOMDocument $domDocument) + { + $xpath = new \DOMXPath($domDocument); + $nodes = $xpath->query('//comment()'); + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + /** * Escape text * diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index a1d03f023a20c..77ad480a53bd0 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -224,7 +224,12 @@ public function escapeHtmlDataProvider() ], 'text with html comment' => [ 'data' => 'Only <span><b>2</b></span> in stock <!-- HTML COMMENT -->', - 'expected' => 'Only <span><b>2</b></span> in stock HTML COMMENT ', + 'expected' => 'Only <span><b>2</b></span> in stock ', + 'allowedTags' => ['span', 'b'], + ], + 'text with multi-line html comment' => [ + 'data' => 'Only <span><b>2</b></span> in stock <!-- --!\n\n><img src=#>-->', + 'expected' => 'Only <span><b>2</b></span> in stock ', 'allowedTags' => ['span', 'b'], ], 'text with non ascii characters' => [ From 191b33243a8bd2005d29e37155d29951bb2f3b90 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Tue, 21 May 2019 10:35:08 -0500 Subject: [PATCH 0070/2437] MC-15733: firefox method update - Resolved incorrect test quotes --- lib/internal/Magento/Framework/Test/Unit/EscaperTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index 77ad480a53bd0..bc12b0bd1227c 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -228,7 +228,7 @@ public function escapeHtmlDataProvider() 'allowedTags' => ['span', 'b'], ], 'text with multi-line html comment' => [ - 'data' => 'Only <span><b>2</b></span> in stock <!-- --!\n\n><img src=#>-->', + 'data' => "Only <span><b>2</b></span> in stock <!-- --!\n\n><img src=#>-->", 'expected' => 'Only <span><b>2</b></span> in stock ', 'allowedTags' => ['span', 'b'], ], From 7dc3ef8ecef02e9e036cff69f27987605e473e57 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Fri, 24 May 2019 11:22:34 -0500 Subject: [PATCH 0071/2437] MC-15811: Incorrect Url Fix multi line function static error. Refactor data object in integration test. --- .../Store/Controller/Store/SwitchRequest.php | 12 +++++---- .../Magento/Store/Model/HashGeneratorTest.php | 25 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Store/Controller/Store/SwitchRequest.php b/app/code/Magento/Store/Controller/Store/SwitchRequest.php index 24e07137497b1..9ce151bdab094 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchRequest.php +++ b/app/code/Magento/Store/Controller/Store/SwitchRequest.php @@ -80,11 +80,13 @@ public function execute() $encodedUrl = (string)$this->_request->getParam(ActionInterface::PARAM_NAME_URL_ENCODED); $targetUrl = $this->urlDecoder->decode($encodedUrl); - $data = new HashData([ - "customer_id" => $customerId, - "time_stamp" => $timeStamp, - "___from_store" => $fromStoreCode - ]); + $data = new HashData( + [ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ] + ); if ($targetUrl && $this->hashGenerator->validateHash($signature, $data)) { try { diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php index 226cacd71195e..1bacd79b74f49 100644 --- a/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/Model/HashGeneratorTest.php @@ -134,11 +134,14 @@ public function testSwitch(): void $this->assertEquals($params['customer_id'], $this->customerId); $this->assertEquals($params['___from_store'], $fromStoreCode); - $this->assertTrue($this->hashGenerator->validateHash($signature, new HashData([ - "customer_id" => $this->customerId, - "time_stamp" => $params['time_stamp'], - "___from_store" => $fromStoreCode - ]))); + $data = new HashData( + [ + "customer_id" => $this->customerId, + "time_stamp" => $params['time_stamp'], + "___from_store" => $fromStoreCode + ] + ); + $this->assertTrue($this->hashGenerator->validateHash($signature, $data)); } /** @@ -151,11 +154,13 @@ public function testValidateHashWithInCorrectData(): void $timeStamp = 0; $customerId = 8; $fromStoreCode = 'store1'; - $data = new HashData([ - "customer_id" => $customerId, - "time_stamp" => $timeStamp, - "___from_store" => $fromStoreCode - ]); + $data = new HashData( + [ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ] + ); $redirectUrl = "http://domain.com/"; $fromStore = $this->createPartialMock(Store::class, ['getCode']); $toStore = $this->createPartialMock(Store::class, ['getCode']); From a22b9234a9f05512c5a47a9e64b0070b996c453b Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 24 May 2019 16:05:16 -0500 Subject: [PATCH 0072/2437] MC-16977: Broken MFTF Test: MC-14208: Clear all products from the 'Compare Products' list --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index f5c3171a3fe90..ae6a55bdd112f 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -6,6 +6,7 @@ */ namespace Magento\Catalog\Controller\Product\Compare; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; @@ -18,6 +19,7 @@ class Add extends \Magento\Catalog\Controller\Product\Compare implements HttpPos * Add item to compare list. * * @return \Magento\Framework\Controller\ResultInterface + * @throws NoSuchEntityException */ public function execute() { @@ -36,7 +38,7 @@ public function execute() $product = null; } - if ($product && $product->isSalable()) { + if ($product && $product->getStatus() === Status::STATUS_ENABLED) { $this->_catalogProductCompareList->addProduct($product); $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class From cd67eab797cac72813a688cd7115d620164145a1 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 24 May 2019 16:56:58 -0500 Subject: [PATCH 0073/2437] MC-16977: Broken MFTF Test: MC-14208: Clear all products from the 'Compare Products' list --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index ae6a55bdd112f..3793a3858406e 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -38,7 +38,7 @@ public function execute() $product = null; } - if ($product && $product->getStatus() === Status::STATUS_ENABLED) { + if ($product && $product->getStatus() !== Status::STATUS_DISABLED) { $this->_catalogProductCompareList->addProduct($product); $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class From 6daa19b40fdd586664de9193c4a880d5202dda75 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 28 May 2019 10:15:28 +0300 Subject: [PATCH 0074/2437] Small adjustments --- ...PagerShoppingCartWithMoreThan20ProductsTest.xml | 14 +++++++------- ...tMissingPagerShoppingCartWith20ProductsTest.xml | 8 ++++---- ...gCartPagerForOneItemPerPageAnd2ProductsTest.xml | 8 ++++---- .../Test/TestCase/ShoppingCartPagerTest.xml | 12 ++++-------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml index 89d6d654f1444..06893679a5948 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -31,6 +31,13 @@ <argument name="product" value="$$simpleProduct21CartItems$$"/> </actionGroup> </before> + <after> + <!--Set back the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + + <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> + <deleteData createDataKey="simpleProduct21CartItems" stepKey="deleteCartItem21"/> + </after> <!-- Go to the shopping cart and check if the pager is visible--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerText"> @@ -42,12 +49,5 @@ <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText2" > <argument name="text" value="Items 1 to 20"/> </actionGroup> - <after> - <!--Set back the default number of items on cart which is 20--> - <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> - - <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> - <deleteData createDataKey="simpleProduct21CartItems" stepKey="deleteCartItem21"/> - </after> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml index e9f83bb9cc167..57a45112a668b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml @@ -24,14 +24,14 @@ <!--Create and add to cart 20 products--> <actionGroup ref="CreateAndAddToCart20ProductsActionGroup" stepKey="CartItems" /> </before> + <after> + <!--Delete the test products--> + <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> + </after> <!-- Go to the shopping cart and check if the pager is missing--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText" > <argument name="text" value="Items 1 to 20"/> </actionGroup> - <after> - <!--Delete the test products--> - <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> - </after> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml index 4e3bd22ccde51..fd2169a9dbfd7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -31,10 +31,6 @@ <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> </before> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> - <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> - <argument name="text" value="Items 1 to 1 of 2 total"/> - </actionGroup> <after> <!--Set back the default number of items on cart which is 20--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> @@ -42,5 +38,9 @@ <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> </after> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> + <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> + <argument name="text" value="Items 1 to 1 of 2 total"/> + </actionGroup> </test> </tests> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml index 27e9583db5fd2..e03351de7a2b1 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml @@ -8,29 +8,25 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\ShoppingCartPagerTest" summary="Verify pager on Shopping Cart" ticketId="MAGETWO-63337"> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd20Products" summary="Verify pager is NOT presented on Shopping Cart page if qty of products = 20, by default system configuration" ticketId="MAGETWO-63337"> - <data name="tag" xsi:type="string">severity:S2</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd21Products" summary="Verify pager is presented on Shopping Cart page if items qty=21, by default system configuration" ticketId="MAGETWO-63338"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="config/dataset" xsi:type="string">default_number_of_items_per_page_on_shopping_cart</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart"/> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText"/> </variation> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAndRemovingOneProduct" summary="Verify pager is disapeared from Shopping Cart page if change qty from 21 to 20, by default system configuration" ticketId="MAGETWO-63339"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> <data name="itemsToRemove" xsi:type="string">1</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestForOneItemPerPageAnd20Products" summary="Verify Pager is presented on Shopping Cart page with non-default system configuration" ticketId="MAGETWO-63340"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="configData" xsi:type="string">one_item_per_page_on_shopping_cart</data> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart" /> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText" /> </variation> From 70d8f5dead89f801be7b1af2d5f6782b93308803 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 28 May 2019 07:51:22 -0500 Subject: [PATCH 0075/2437] MC-16977: Broken MFTF Test: MC-14208: Clear all products from the 'Compare Products' list --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index 3793a3858406e..ac162652cf897 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -19,7 +19,6 @@ class Add extends \Magento\Catalog\Controller\Product\Compare implements HttpPos * Add item to compare list. * * @return \Magento\Framework\Controller\ResultInterface - * @throws NoSuchEntityException */ public function execute() { From 26513db3d98d65fa3471f7ee1706affcb4fc1878 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 28 May 2019 09:26:03 -0500 Subject: [PATCH 0076/2437] MC-16977: Broken MFTF Test: MC-14208: Clear all products from the 'Compare Products' list --- app/code/Magento/Catalog/Controller/Product/Compare/Add.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index ac162652cf897..2b989dde9809f 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -37,7 +37,7 @@ public function execute() $product = null; } - if ($product && $product->getStatus() !== Status::STATUS_DISABLED) { + if ($product && (int)$product->getStatus() !== Status::STATUS_DISABLED) { $this->_catalogProductCompareList->addProduct($product); $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class From 7447cc947e0a551ac45ab1fdb62dcfbe7d8f12aa Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Tue, 28 May 2019 15:35:16 -0500 Subject: [PATCH 0077/2437] MC-15977: Email template preview --- .../Block/Adminhtml/Template/PreviewTest.php | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php index 8143995f3b2af..83eda7e39a810 100644 --- a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php @@ -54,14 +54,16 @@ protected function setUp() $designConfigData = []; $this->template = $this->getMockBuilder(\Magento\Email\Model\Template::class) - ->setMethods([ - 'setDesignConfig', - 'getDesignConfig', - '__wakeup', - 'getProcessedTemplate', - 'getAppState', - 'revertDesign' - ]) + ->setMethods( + [ + 'setDesignConfig', + 'getDesignConfig', + '__wakeup', + 'getProcessedTemplate', + 'getAppState', + 'revertDesign' + ] + ) ->disableOriginalConstructor() ->getMock(); @@ -82,9 +84,7 @@ protected function setUp() ->willReturn(self::MALICIOUS_TEXT); $this->template->method('getDesignConfig') - ->willReturn(new \Magento\Framework\DataObject( - $designConfigData - )); + ->willReturn(new \Magento\Framework\DataObject($designConfigData)); $emailFactory = $this->createPartialMock(\Magento\Email\Model\TemplateFactory::class, ['create']); $emailFactory->expects($this->any()) @@ -106,9 +106,11 @@ protected function setUp() $this->storeManager->expects($this->any())->method('getDefaultStoreView')->willReturn(null); $this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]); $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) - ->setConstructorArgs([ - $scopeConfig - ]) + ->setConstructorArgs( + [ + $scopeConfig + ] + ) ->setMethods(['emulateAreaCode']) ->disableOriginalConstructor() ->getMock(); From 4b62b6524eee31f4dbdb579bf51405c2a8579e43 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 29 May 2019 10:29:58 +0300 Subject: [PATCH 0078/2437] Refactoring the Action Group naming --- ...onGroup.xml => AssertTextIsVisibleOnPageActionGroup.xml} | 6 +++--- ...rontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml | 2 +- ...ntShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/code/Magento/Checkout/Test/Mftf/ActionGroup/{AssertIsVisibleCartPagerTextActionGroup.xml => AssertTextIsVisibleOnPageActionGroup.xml} (70%) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml similarity index 70% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml index f1793ecd640b4..0eb584d4c2242 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsVisibleCartPagerTextActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml @@ -7,11 +7,11 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertPagerTextIsVisibleActionGroup"> + <actionGroup name="AssertTextIsVisibleOnPageActionGroup"> <arguments> <argument name="text" type="string"/> </arguments> - <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> - <see userInput="{{text}}" stepKey="VerifyPagerText"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="{{text}}" stepKey="VerifyPageText"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml index 06893679a5948..0c9411db4f5ca 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -40,7 +40,7 @@ </after> <!-- Go to the shopping cart and check if the pager is visible--> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> - <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerText"> + <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerText"> <argument name="text" value="Items 1 to 20 of 21 total"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml index fd2169a9dbfd7..5ba1b0122c6e6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -39,7 +39,7 @@ <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> </after> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> - <actionGroup ref="AssertPagerTextIsVisibleActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> + <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> <argument name="text" value="Items 1 to 1 of 2 total"/> </actionGroup> </test> From 9b50d1eae804b70e85fd4fa65be82613823aac01 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 29 May 2019 12:03:18 -0500 Subject: [PATCH 0079/2437] MC-15573: Prevent invalid UPS configurations --- .../Ups/Model/Config/Backend/UpsUrl.php | 32 ++++++++ .../Unit/Model/Config/Backend/UpsUrlTest.php | 78 +++++++++++++++++++ app/code/Magento/Ups/etc/adminhtml/system.xml | 3 + app/code/Magento/Ups/i18n/en_US.csv | 1 + 4 files changed, 114 insertions(+) create mode 100644 app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php create mode 100644 app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php diff --git a/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php b/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php new file mode 100644 index 0000000000000..155ab7c87be34 --- /dev/null +++ b/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Ups\Model\Config\Backend; + +use Magento\Framework\App\Config\Value; +use Magento\Framework\Exception\ValidatorException; + +/** + * Represents a config URL that may point to a UPS endpoint + */ +class UpsUrl extends Value +{ + /** + * @inheritdoc + */ + public function beforeSave() + { + $host = parse_url((string)$this->getValue(), \PHP_URL_HOST); + + if (!empty($host) && !preg_match('/(?:.+\.|^)ups\.com$/i', $host)) { + throw new ValidatorException(__('UPS API endpoint URL\'s must use ups.com')); + } + + return parent::beforeSave(); + } +} diff --git a/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php b/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php new file mode 100644 index 0000000000000..8a480983b1bdc --- /dev/null +++ b/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Ups\Test\Unit\Model\Config\Backend; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Ups\Model\Config\Backend\UpsUrl; +use PHPUnit\Framework\TestCase; + +/** + * Verify behavior of UpsUrl backend type + */ +class UpsUrlTest extends TestCase +{ + + /** + * @var UpsUrl + */ + private $config; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + /** @var UpsUrl $upsUrl */ + $this->config = $objectManager->getObject(UpsUrl::class); + } + + /** + * @dataProvider validDataProvider + * @param string $data The valid data + */ + public function testBeforeSave($data=null) + { + $this->config->setValue($data); + $this->config->beforeSave(); + } + + /** + * @dataProvider invalidDataProvider + * @param string $data The invalid data + * @expectedException \Magento\Framework\Exception\ValidatorException + * @expectedExceptionMessage UPS API endpoint URL's must use ups.com + */ + public function testBeforeSaveErrors($data) + { + $this->config->setValue($data); + $this->config->beforeSave(); + } + + public function validDataProvider() + { + return [ + [], + [null], + [''], + ['http://ups.com'], + ['https://foo.ups.com'], + ['http://foo.ups.com/foo/bar?baz=bash&fizz=buzz'], + ]; + } + + public function invalidDataProvider() + { + return [ + ['http://upsfoo.com'], + ['https://fooups.com'], + ['https://ups.com.fake.com'], + ['https://ups.info'], + ['http://ups.com.foo.com/foo/bar?baz=bash&fizz=buzz'], + ['http://fooups.com/foo/bar?baz=bash&fizz=buzz'], + ]; + } +} diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index 8b9dc30a0188b..91eed08f31db7 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -56,9 +56,11 @@ </field> <field id="gateway_url" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Gateway URL</label> + <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> <field id="gateway_xml_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Gateway XML URL</label> + <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> <field id="handling_type" translate="label" type="select" sortOrder="110" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Calculate Handling Fee</label> @@ -103,6 +105,7 @@ </field> <field id="tracking_xml_url" translate="label" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Tracking XML URL</label> + <backend_model>Magento\Ups\Model\Config\Backend\UpsUrl</backend_model> </field> <field id="type" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>UPS Type</label> diff --git a/app/code/Magento/Ups/i18n/en_US.csv b/app/code/Magento/Ups/i18n/en_US.csv index baf8ecc855440..68dd34a313bd9 100644 --- a/app/code/Magento/Ups/i18n/en_US.csv +++ b/app/code/Magento/Ups/i18n/en_US.csv @@ -114,3 +114,4 @@ Title,Title Mode,Mode "This enables or disables SSL verification of the Magento server by UPS.","This enables or disables SSL verification of the Magento server by UPS." Debug,Debug +"UPS API endpoint URL's must use ups.com","UPS API endpoint URL's must use ups.com" From 5215bbad2c99f1c0d96a62c4276b08107c2e9b2d Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 29 May 2019 13:11:06 -0500 Subject: [PATCH 0080/2437] MC-15573: Prevent invalid UPS configurations --- .../Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php b/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php index 8a480983b1bdc..149f9378889f8 100644 --- a/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php +++ b/app/code/Magento/Ups/Test/Unit/Model/Config/Backend/UpsUrlTest.php @@ -34,7 +34,7 @@ protected function setUp() * @dataProvider validDataProvider * @param string $data The valid data */ - public function testBeforeSave($data=null) + public function testBeforeSave($data = null) { $this->config->setValue($data); $this->config->beforeSave(); From 1b80e1b84d22b1fdf341c2f3654b8b833dde9a2b Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 29 May 2019 14:17:32 -0500 Subject: [PATCH 0081/2437] MC-16114: Update Email Template Filter --- .../Magento/Framework/Filter/Template.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 38198bb9a717b..c32df1b175d8f 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -85,6 +85,16 @@ class Template implements \Zend_Filter_Interface 'getdatausingmethod' ]; + /** + * @var array[] + */ + private $restrictedMethodsByInstanceType = [ + \Magento\Framework\DB\Adapter\AdapterInterface::class => [ + 'rawquery', + 'rawfetchrow' + ] + ]; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param array $variables @@ -405,6 +415,12 @@ private function validateVariableMethodCall($object, string $method): void if (in_array(mb_strtolower($method), $this->restrictedMethods)) { throw new \InvalidArgumentException("Method $method cannot be called from template."); } + } else { + foreach ($this->restrictedMethodsByInstanceType as $instanceType => $restrictedMethods) { + if ($object instanceof $instanceType && in_array(mb_strtolower($method), $restrictedMethods)) { + throw new \InvalidArgumentException("Method $method cannot be called from template."); + } + } } } From 95601282c252cde1ce14a6e26871cc6393071e6c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 29 May 2019 14:20:52 -0500 Subject: [PATCH 0082/2437] MC-15573: Prevent invalid UPS configuration --- app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php b/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php index 155ab7c87be34..9db35b6a42dcc 100644 --- a/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php +++ b/app/code/Magento/Ups/Model/Config/Backend/UpsUrl.php @@ -21,6 +21,7 @@ class UpsUrl extends Value */ public function beforeSave() { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $host = parse_url((string)$this->getValue(), \PHP_URL_HOST); if (!empty($host) && !preg_match('/(?:.+\.|^)ups\.com$/i', $host)) { From b320ba646413422b8f379b16908d065ebfb40b03 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 30 May 2019 10:57:28 +0300 Subject: [PATCH 0083/2437] MC-13886: Downloadable Product controller|API save changes --- .../Unit/Model/Link/ContentValidatorTest.php | 51 +++++++------------ .../Test/Unit/Model/LinkRepositoryTest.php | 38 +++++++++----- .../Model/Sample/ContentValidatorTest.php | 33 ++++++------ .../Test/Unit/Model/SampleRepositoryTest.php | 36 +++++++------ 4 files changed, 79 insertions(+), 79 deletions(-) diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php index 5484e39bd57fc..771fbc37e5e13 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php @@ -246,45 +246,29 @@ protected function getLinkMock(array $linkData) 'isShareable', 'getNumberOfDownloads', 'getLinkType', - 'getLinkFile' + 'getLinkFile', ] ) ->getMockForAbstractClass(); - $linkMock->expects($this->any())->method('getTitle')->will($this->returnValue( - $linkData['title'] - )); - $linkMock->expects($this->any())->method('getPrice')->will($this->returnValue( - $linkData['price'] - )); - $linkMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( - $linkData['sort_order'] - )); - $linkMock->expects($this->any())->method('isShareable')->will($this->returnValue( - $linkData['shareable'] - )); - $linkMock->expects($this->any())->method('getNumberOfDownloads')->will($this->returnValue( - $linkData['number_of_downloads'] - )); - $linkMock->expects($this->any())->method('getLinkType')->will($this->returnValue( - $linkData['link_type'] - )); - $linkMock->expects($this->any())->method('getLinkFile')->will($this->returnValue( - $this->linkFileMock - )); + $linkMock->expects($this->any())->method('getTitle')->will($this->returnValue($linkData['title'])); + $linkMock->expects($this->any())->method('getPrice')->will($this->returnValue($linkData['price'])); + $linkMock->expects($this->any())->method('getSortOrder')->will($this->returnValue($linkData['sort_order'])); + $linkMock->expects($this->any())->method('isShareable')->will($this->returnValue($linkData['shareable'])); + $linkMock->expects($this->any())->method('getNumberOfDownloads')->will( + $this->returnValue($linkData['number_of_downloads']) + ); + $linkMock->expects($this->any())->method('getLinkType')->will($this->returnValue($linkData['link_type'])); + $linkMock->expects($this->any())->method('getLinkFile')->will($this->returnValue($this->linkFileMock)); if (isset($linkData['link_url'])) { - $linkMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue( - $linkData['link_url'] - )); + $linkMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue($linkData['link_url'])); } if (isset($linkData['sample_url'])) { - $linkMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( - $linkData['sample_url'] - )); + $linkMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue($linkData['sample_url'])); } if (isset($linkData['sample_type'])) { - $linkMock->expects($this->any())->method('getSampleType')->will($this->returnValue( - $linkData['sample_type'] - )); + $linkMock->expects($this->any())->method('getSampleType')->will( + $this->returnValue($linkData['sample_type']) + ); } if (isset($linkData['link_file_content'])) { $linkMock->expects($this->any())->method('getLinkFileContent')->willReturn($linkData['link_file_content']); @@ -293,9 +277,8 @@ protected function getLinkMock(array $linkData) $linkMock->expects($this->any())->method('getSampleFileContent') ->willReturn($linkData['sample_file_content']); } - $linkMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( - $this->sampleFileMock - )); + $linkMock->expects($this->any())->method('getSampleFile')->will($this->returnValue($this->sampleFileMock)); + return $linkMock; } } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php index 4494877b70f6c..25f720f27150c 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php @@ -98,7 +98,9 @@ protected function setUp() \Magento\Framework\Json\EncoderInterface::class ); $this->linkFactoryMock = $this->createPartialMock(\Magento\Downloadable\Model\LinkFactory::class, ['create']); - $this->productMock = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ + $this->productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ '__wakeup', 'getTypeId', 'setDownloadableData', @@ -107,8 +109,9 @@ protected function setUp() 'getStoreId', 'getStore', 'getWebsiteIds', - 'getData' - ]); + 'getData', + ] + ); $this->service = new \Magento\Downloadable\Model\LinkRepository( $this->repositoryMock, $this->productTypeMock, @@ -310,12 +313,15 @@ public function testUpdate() $storeMock = $this->createMock(\Magento\Store\Model\Store::class); $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId)); $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); - $existingLinkMock = $this->createPartialMock(\Magento\Downloadable\Model\Link::class, [ + $existingLinkMock = $this->createPartialMock( + \Magento\Downloadable\Model\Link::class, + [ '__wakeup', 'getId', 'load', - 'getProductId' - ]); + 'getProductId', + ] + ); $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock)); $linkMock = $this->getLinkMock($linkData); $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) @@ -372,12 +378,15 @@ public function testUpdateWithExistingFile() $storeMock = $this->createMock(\Magento\Store\Model\Store::class); $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId)); $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); - $existingLinkMock = $this->createPartialMock(\Magento\Downloadable\Model\Link::class, [ + $existingLinkMock = $this->createPartialMock( + \Magento\Downloadable\Model\Link::class, + [ '__wakeup', 'getId', 'load', - 'getProductId' - ]); + 'getProductId', + ] + ); $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock)); $linkMock = $this->getLinkMock($linkData); $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock) @@ -504,10 +513,12 @@ public function testGetList() 'sample_file' => '/r/o/rock.melody.ogg', 'link_type' => 'url', 'link_url' => 'http://link.url', - 'link_file' => null + 'link_file' => null, ]; - $linkMock = $this->createPartialMock(\Magento\Downloadable\Model\Link::class, [ + $linkMock = $this->createPartialMock( + \Magento\Downloadable\Model\Link::class, + [ 'getId', 'getStoreTitle', 'getTitle', @@ -522,8 +533,9 @@ public function testGetList() 'getSampleUrl', 'getLinkType', 'getLinkFile', - 'getLinkUrl' - ]); + 'getLinkUrl', + ] + ); $linkInterfaceMock = $this->createMock(\Magento\Downloadable\Api\Data\LinkInterface::class); diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php index 90bcfa2e39bef..4a32a45859cec 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php @@ -117,28 +117,29 @@ public function getInvalidSortOrder() protected function getSampleContentMock(array $sampleContentData) { $contentMock = $this->createMock(\Magento\Downloadable\Api\Data\SampleInterface::class); - $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue( - $sampleContentData['title'] - )); - - $contentMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( - $sampleContentData['sort_order'] - )); - $contentMock->expects($this->any())->method('getSampleType')->will($this->returnValue( - $sampleContentData['sample_type'] - )); + $contentMock->expects($this->any())->method('getTitle')->will( + $this->returnValue($sampleContentData['title']) + ); + + $contentMock->expects($this->any())->method('getSortOrder')->will( + $this->returnValue($sampleContentData['sort_order']) + ); + $contentMock->expects($this->any())->method('getSampleType')->will( + $this->returnValue($sampleContentData['sample_type']) + ); if (isset($sampleContentData['sample_url'])) { - $contentMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( - $sampleContentData['sample_url'] - )); + $contentMock->expects($this->any())->method('getSampleUrl')->will( + $this->returnValue($sampleContentData['sample_url']) + ); } if (isset($sampleContentData['sample_file_content'])) { $contentMock->expects($this->any())->method('getSampleFileContent') ->willReturn($sampleContentData['sample_file_content']); } - $contentMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( - $this->sampleFileMock - )); + $contentMock->expects($this->any())->method('getSampleFile')->will( + $this->returnValue($this->sampleFileMock) + ); + return $contentMock; } } diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php index f1ca30bd7dd36..f1674c6838a2d 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php @@ -149,25 +149,26 @@ protected function getSampleMock(array $sampleData) $sampleMock->expects($this->any())->method('getId')->willReturn($sampleData['id']); } $sampleMock->expects($this->any())->method('getTitle')->will($this->returnValue($sampleData['title'])); - $sampleMock->expects($this->any())->method('getSortOrder')->will($this->returnValue( - $sampleData['sort_order'] - )); + $sampleMock->expects($this->any())->method('getSortOrder')->will( + $this->returnValue($sampleData['sort_order']) + ); if (isset($sampleData['sample_type'])) { - $sampleMock->expects($this->any())->method('getSampleType')->will($this->returnValue( - $sampleData['sample_type'] - )); + $sampleMock->expects($this->any())->method('getSampleType')->will( + $this->returnValue($sampleData['sample_type']) + ); } if (isset($sampleData['sample_url'])) { - $sampleMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue( - $sampleData['sample_url'] - )); + $sampleMock->expects($this->any())->method('getSampleUrl')->will( + $this->returnValue($sampleData['sample_url']) + ); } if (isset($sampleData['sample_file'])) { - $sampleMock->expects($this->any())->method('getSampleFile')->will($this->returnValue( - $sampleData['sample_file'] - )); + $sampleMock->expects($this->any())->method('getSampleFile')->will( + $this->returnValue($sampleData['sample_file']) + ); } + return $sampleMock; } @@ -416,10 +417,12 @@ public function testGetList() 'sort_order' => 21, 'sample_type' => 'file', 'sample_url' => null, - 'sample_file' => '/r/o/rock.melody.ogg' + 'sample_file' => '/r/o/rock.melody.ogg', ]; - $sampleMock = $this->createPartialMock(\Magento\Downloadable\Model\Sample::class, [ + $sampleMock = $this->createPartialMock( + \Magento\Downloadable\Model\Sample::class, + [ 'getId', 'getStoreTitle', 'getTitle', @@ -428,8 +431,9 @@ public function testGetList() 'getSampleUrl', 'getSortOrder', 'getData', - '__wakeup' - ]); + '__wakeup', + ] + ); $sampleInterfaceMock = $this->createMock(\Magento\Downloadable\Api\Data\SampleInterface::class); From b39ae28f96dd1d12d7494c888c378a45d7457fb9 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Thu, 30 May 2019 10:53:14 +0000 Subject: [PATCH 0084/2437] MAGETWO-98703: CSV file is not exported - Change message --- .../ImportExport/Controller/Adminhtml/Export/Export.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php index 13c22a976e798..c111a9dd22948 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ImportExport\Controller\Adminhtml\Export; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; @@ -85,7 +87,10 @@ public function execute() $this->messagePublisher->publish('import_export.export', $dataObject); $this->messageManager->addSuccessMessage( - __('Message is added to queue, wait to get your file soon') + __( + 'Message is added to queue, wait to get your file soon.' + . ' Make sure your cron job is running to export the file' + ) ); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); From f7f8ceb43f2348257ae38498197d2dc3d1641266 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Thu, 30 May 2019 11:14:49 -0500 Subject: [PATCH 0085/2437] MC-17067: Update config table schema - Updated config table schema --- app/code/Magento/Config/etc/db_schema.xml | 2 ++ app/code/Magento/Config/etc/db_schema_whitelist.json | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/etc/db_schema.xml b/app/code/Magento/Config/etc/db_schema.xml index 8aeac802fbd91..1b2f7a0519411 100644 --- a/app/code/Magento/Config/etc/db_schema.xml +++ b/app/code/Magento/Config/etc/db_schema.xml @@ -15,6 +15,8 @@ default="0" comment="Config Scope Id"/> <column xsi:type="varchar" name="path" nullable="false" length="255" default="general" comment="Config Path"/> <column xsi:type="text" name="value" nullable="true" comment="Config Value"/> + <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" + comment="Updated At"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="config_id"/> </constraint> diff --git a/app/code/Magento/Config/etc/db_schema_whitelist.json b/app/code/Magento/Config/etc/db_schema_whitelist.json index 850e160bc732f..51ca41d8e6af8 100644 --- a/app/code/Magento/Config/etc/db_schema_whitelist.json +++ b/app/code/Magento/Config/etc/db_schema_whitelist.json @@ -5,11 +5,12 @@ "scope": true, "scope_id": true, "path": true, - "value": true + "value": true, + "updated_at": true }, "constraint": { "PRIMARY": true, "CORE_CONFIG_DATA_SCOPE_SCOPE_ID_PATH": true } } -} \ No newline at end of file +} From c7676c54342926a3d40878ceb6042899f0ec49fe Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Thu, 30 May 2019 15:36:53 -0500 Subject: [PATCH 0086/2437] MC-16940: Prevent errors from incorrect configurations --- .../Interpreter/ConfigurableObject.php | 70 +++++++++++++++---- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 2810db6569421..654359ec30be7 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -3,10 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\View\Element\UiComponent\Argument\Interpreter; +use Magento\Framework\Code\Reader\ClassReader; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Framework\ObjectManager\ConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Data\Argument\InterpreterInterface; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; /** * Class ConfigurableObject @@ -16,8 +22,9 @@ class ConfigurableObject implements InterpreterInterface /** * @var array */ - private $classBlacklist = [ - \Zend\Code\Reflection\FileReflection::class + private $classWhitelist = [ + DataProviderInterface::class, + OptionSourceInterface::class ]; /** @@ -29,17 +36,33 @@ class ConfigurableObject implements InterpreterInterface * @var InterpreterInterface */ protected $argumentInterpreter; + /** + * @var ClassReader|null + */ + private $classReader; + /** + * @var ConfigInterface + */ + private $objectManagerConfig; /** * Constructor * * @param ObjectManagerInterface $objectManager * @param InterpreterInterface $argumentInterpreter + * @param ClassReader|null $classReader + * @param ConfigInterface $objectManagerConfig */ - public function __construct(ObjectManagerInterface $objectManager, InterpreterInterface $argumentInterpreter) - { + public function __construct( + ObjectManagerInterface $objectManager, + InterpreterInterface $argumentInterpreter, + ClassReader $classReader = null, + ConfigInterface $objectManagerConfig = null + ) { $this->objectManager = $objectManager; $this->argumentInterpreter = $argumentInterpreter; + $this->classReader = $classReader ?? $objectManager->get(ClassReader::class); + $this->objectManagerConfig = $objectManagerConfig ?? $objectManager->get(ConfigInterface::class); } /** @@ -61,14 +84,18 @@ public function evaluate(array $data) throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); } - if (in_array( - ltrim(strtolower($arguments['class']), '\\'), - array_map('strtolower', $this->classBlacklist) - )) { - throw new \InvalidArgumentException(sprintf( - 'Class argument is invalid: %s', - $arguments['class'] - )); + $type = $this->objectManagerConfig->getInstanceType( + $this->objectManagerConfig->getPreference($arguments['class']) + ); + + $classParents = $this->getParents($type); + + $whitelistIntersection = array_intersect($classParents, $this->classWhitelist); + + if (empty($whitelistIntersection)) { + throw new \InvalidArgumentException( + sprintf('Class argument is invalid: %s', $arguments['class']) + ); } $className = $arguments['class']; @@ -77,4 +104,23 @@ public function evaluate(array $data) return $this->objectManager->create($className, $arguments); } + + /** + * Retrieves all the parent classes and interfaces for a class including the ones implemented by the class itself + * + * @param string $type + * @return string[] + */ + private function getParents(string $type) + { + $classParents = $this->classReader->getParents($type); + foreach ($classParents as $parent) { + if (empty($parent)) { + continue; + } + $classParents = array_merge($classParents, $this->getParents($parent)); + } + + return $classParents; + } } From 84bf99b4d098b4dde120546c7b727e153bdc94ae Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 31 May 2019 12:13:06 -0500 Subject: [PATCH 0087/2437] MC-16074: Sitemap generator update - Updated magento store config file --- app/code/Magento/Store/etc/config.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Store/etc/config.xml b/app/code/Magento/Store/etc/config.xml index 23182c8d76470..07e4c8b0b6529 100644 --- a/app/code/Magento/Store/etc/config.xml +++ b/app/code/Magento/Store/etc/config.xml @@ -135,14 +135,14 @@ </protected_extensions> <public_files_valid_paths> <protected> - <app>/app/*/*</app> - <bin>/bin/*/*</bin> - <dev>/dev/*/*</dev> - <generated>/generated/*/*</generated> - <lib>/lib/*/*</lib> - <setup>/setup/*/*</setup> - <update>/update/*/*</update> - <vendor>/vendor/*/*</vendor> + <app>*/app/*/*</app> + <bin>*/bin/*/*</bin> + <dev>*/dev/*/*</dev> + <generated>*/generated/*/*</generated> + <lib>*/lib/*/*</lib> + <setup>*/setup/*/*</setup> + <update>*/update/*/*</update> + <vendor>*/vendor/*/*</vendor> </protected> </public_files_valid_paths> </file> From e6e7665d54074c55fcd821d6682554c77f1b9f7b Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 31 May 2019 12:21:26 -0500 Subject: [PATCH 0088/2437] MC-16940: Prevent errors from incorrect configurations --- app/etc/di.xml | 8 ++++ .../Interpreter/ConfigurableObject.php | 47 +++++++------------ 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/app/etc/di.xml b/app/etc/di.xml index 200a56201239d..cccae25b467eb 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -662,6 +662,14 @@ <argument name="argumentInterpreter" xsi:type="object">layoutArgumentGeneratorInterpreter</argument> </arguments> </type> + <type name="Magento\Framework\View\Element\UiComponent\Argument\Interpreter\ConfigurableObject"> + <arguments> + <argument name="classWhitelist" xsi:type="array"> + <item name="0" xsi:type="string">Magento\Framework\Data\OptionSourceInterface</item> + <item name="1" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface</item> + </argument> + </arguments> + </type> <type name="Magento\Framework\Mview\View"> <arguments> <argument name="state" xsi:type="object" shared="false">Magento\Indexer\Model\Mview\View\State</argument> diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 654359ec30be7..4e7caaf4c5655 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -8,11 +8,9 @@ namespace Magento\Framework\View\Element\UiComponent\Argument\Interpreter; use Magento\Framework\Code\Reader\ClassReader; -use Magento\Framework\Data\OptionSourceInterface; use Magento\Framework\ObjectManager\ConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Data\Argument\InterpreterInterface; -use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; /** * Class ConfigurableObject @@ -22,10 +20,7 @@ class ConfigurableObject implements InterpreterInterface /** * @var array */ - private $classWhitelist = [ - DataProviderInterface::class, - OptionSourceInterface::class - ]; + private $classWhitelist = []; /** * @var ObjectManagerInterface @@ -36,10 +31,12 @@ class ConfigurableObject implements InterpreterInterface * @var InterpreterInterface */ protected $argumentInterpreter; + /** - * @var ClassReader|null + * @var ClassReader */ private $classReader; + /** * @var ConfigInterface */ @@ -50,17 +47,20 @@ class ConfigurableObject implements InterpreterInterface * * @param ObjectManagerInterface $objectManager * @param InterpreterInterface $argumentInterpreter + * @param array $classWhitelist * @param ClassReader|null $classReader - * @param ConfigInterface $objectManagerConfig + * @param ConfigInterface|null $objectManagerConfig */ public function __construct( ObjectManagerInterface $objectManager, InterpreterInterface $argumentInterpreter, + array $classWhitelist = [], ClassReader $classReader = null, ConfigInterface $objectManagerConfig = null ) { $this->objectManager = $objectManager; $this->argumentInterpreter = $argumentInterpreter; + $this->classWhitelist = $classWhitelist; $this->classReader = $classReader ?? $objectManager->get(ClassReader::class); $this->objectManagerConfig = $objectManagerConfig ?? $objectManager->get(ConfigInterface::class); } @@ -88,11 +88,15 @@ public function evaluate(array $data) $this->objectManagerConfig->getPreference($arguments['class']) ); - $classParents = $this->getParents($type); - - $whitelistIntersection = array_intersect($classParents, $this->classWhitelist); + $classIsAllowed = false; + foreach ($this->classWhitelist as $allowedClass) { + if (is_subclass_of($type, $allowedClass, true)) { + $classIsAllowed = true; + break; + } + } - if (empty($whitelistIntersection)) { + if (!$classIsAllowed) { throw new \InvalidArgumentException( sprintf('Class argument is invalid: %s', $arguments['class']) ); @@ -104,23 +108,4 @@ public function evaluate(array $data) return $this->objectManager->create($className, $arguments); } - - /** - * Retrieves all the parent classes and interfaces for a class including the ones implemented by the class itself - * - * @param string $type - * @return string[] - */ - private function getParents(string $type) - { - $classParents = $this->classReader->getParents($type); - foreach ($classParents as $parent) { - if (empty($parent)) { - continue; - } - $classParents = array_merge($classParents, $this->getParents($parent)); - } - - return $classParents; - } } From e06b4e596da2147f487413325528623da32eaac6 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 31 May 2019 12:26:13 -0500 Subject: [PATCH 0089/2437] MC-16940: Prevent errors from incorrect configurations --- .../Argument/Interpreter/ConfigurableObject.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 4e7caaf4c5655..3098504ebfc53 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -7,7 +7,6 @@ namespace Magento\Framework\View\Element\UiComponent\Argument\Interpreter; -use Magento\Framework\Code\Reader\ClassReader; use Magento\Framework\ObjectManager\ConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Data\Argument\InterpreterInterface; @@ -32,11 +31,6 @@ class ConfigurableObject implements InterpreterInterface */ protected $argumentInterpreter; - /** - * @var ClassReader - */ - private $classReader; - /** * @var ConfigInterface */ @@ -48,20 +42,17 @@ class ConfigurableObject implements InterpreterInterface * @param ObjectManagerInterface $objectManager * @param InterpreterInterface $argumentInterpreter * @param array $classWhitelist - * @param ClassReader|null $classReader * @param ConfigInterface|null $objectManagerConfig */ public function __construct( ObjectManagerInterface $objectManager, InterpreterInterface $argumentInterpreter, array $classWhitelist = [], - ClassReader $classReader = null, ConfigInterface $objectManagerConfig = null ) { $this->objectManager = $objectManager; $this->argumentInterpreter = $argumentInterpreter; $this->classWhitelist = $classWhitelist; - $this->classReader = $classReader ?? $objectManager->get(ClassReader::class); $this->objectManagerConfig = $objectManagerConfig ?? $objectManager->get(ConfigInterface::class); } From edb45bd6b45a5de87a35498f0340238fcb80ad6a Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 31 May 2019 16:02:24 -0500 Subject: [PATCH 0090/2437] MC-16940: Prevent errors from incorrect configurations --- .../Interpreter/ConfigurableObject.php | 60 ++-- .../Interpreter/ConfigurableObjectTest.php | 286 ++++++++++++++++++ 2 files changed, 329 insertions(+), 17 deletions(-) create mode 100644 lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 3098504ebfc53..2ea2856cc0f13 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -7,9 +7,12 @@ namespace Magento\Framework\View\Element\UiComponent\Argument\Interpreter; +use Magento\Framework\Code\Reader\ClassReader; +use Magento\Framework\Data\OptionSourceInterface; use Magento\Framework\ObjectManager\ConfigInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Data\Argument\InterpreterInterface; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; /** * Class ConfigurableObject @@ -31,6 +34,11 @@ class ConfigurableObject implements InterpreterInterface */ protected $argumentInterpreter; + /** + * @var ClassReader|null + */ + private $classReader; + /** * @var ConfigInterface */ @@ -42,17 +50,20 @@ class ConfigurableObject implements InterpreterInterface * @param ObjectManagerInterface $objectManager * @param InterpreterInterface $argumentInterpreter * @param array $classWhitelist + * @param ClassReader|null $classReader * @param ConfigInterface|null $objectManagerConfig */ public function __construct( ObjectManagerInterface $objectManager, InterpreterInterface $argumentInterpreter, array $classWhitelist = [], + ClassReader $classReader = null, ConfigInterface $objectManagerConfig = null ) { $this->objectManager = $objectManager; $this->argumentInterpreter = $argumentInterpreter; $this->classWhitelist = $classWhitelist; + $this->classReader = $classReader ?? $objectManager->get(ClassReader::class); $this->objectManagerConfig = $objectManagerConfig ?? $objectManager->get(ConfigInterface::class); } @@ -75,28 +86,43 @@ public function evaluate(array $data) throw new \InvalidArgumentException('Node "argument" with name "class" is required for this type.'); } - $type = $this->objectManagerConfig->getInstanceType( - $this->objectManagerConfig->getPreference($arguments['class']) - ); + $className = $arguments['class']; + unset($arguments['class']); + } - $classIsAllowed = false; - foreach ($this->classWhitelist as $allowedClass) { - if (is_subclass_of($type, $allowedClass, true)) { - $classIsAllowed = true; - break; - } - } + $type = $this->objectManagerConfig->getInstanceType( + $this->objectManagerConfig->getPreference($className) + ); - if (!$classIsAllowed) { - throw new \InvalidArgumentException( - sprintf('Class argument is invalid: %s', $arguments['class']) - ); - } + $classParents = $this->getParents($type); - $className = $arguments['class']; - unset($arguments['class']); + $whitelistIntersection = array_intersect($classParents, $this->classWhitelist); + + if (empty($whitelistIntersection)) { + throw new \InvalidArgumentException( + sprintf('Class argument is invalid: %s', $className) + ); } return $this->objectManager->create($className, $arguments); } + + /** + * Retrieves all the parent classes and interfaces for a class including the ones implemented by the class itself + * + * @param string $type + * @return string[] + */ + private function getParents(string $type) + { + $classParents = $this->classReader->getParents($type); + foreach ($classParents as $parent) { + if (empty($parent)) { + continue; + } + $classParents = array_merge($classParents, $this->getParents($parent)); + } + + return $classParents; + } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php new file mode 100644 index 0000000000000..8b4f2a35c0802 --- /dev/null +++ b/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php @@ -0,0 +1,286 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\View\Test\Unit\UiComponent\Argument\Interpreter; + +use Magento\Framework\Code\Reader\ClassReader; +use Magento\Framework\Data\Argument\InterpreterInterface; +use Magento\Framework\ObjectManager\ConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\UiComponent\Argument\Interpreter\ConfigurableObject; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Unit tests for ConfigurableObject + */ +class ConfigurableObjectTest extends TestCase +{ + /** + * @var ConfigurableObject + */ + private $configurableObject; + + /** + * @var ObjectManagerInterface|MockObject + */ + private $objectManager; + + /** + * @var InterpreterInterface|MockObject + */ + private $interpreter; + + /** + * @var ClassReader|MockObject + */ + private $classReader; + + /** + * @var ConfigInterface|MockObject + */ + private $objectManagerConfig; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->objectManager = $this->createMock(ObjectManagerInterface::class); + $this->interpreter = $this->createMock(InterpreterInterface::class); + $this->classReader = $this->createMock(ClassReader::class); + $this->objectManagerConfig = $this->createMock(ConfigInterface::class); + $this->configurableObject = $objectManager->getObject( + ConfigurableObject::class, + [ + 'objectManager' => $this->objectManager, + 'argumentInterpreter' => $this->interpreter, + 'classWhitelist' => [ + \Foo\Bar\ClassA::class, + \Foo\Bar\InterfaceA::class, + ], + 'classReader' => $this->classReader, + 'objectManagerConfig' => $this->objectManagerConfig, + ] + ); + } + + /** + * @dataProvider validDataProvider + */ + public function testValidCombinations( + $data, + $expectedClass, + $classParentsValueMap, + $expectedArguments + ) { + $this->objectManagerConfig + ->method('getPreference') + ->with($expectedClass) + ->WillReturn('bar'); + $this->objectManagerConfig + ->method('getInstanceType') + ->with('bar') + ->willReturn($expectedClass); + + $this->classReader + ->method('getParents') + ->willReturnMap($classParentsValueMap); + + $this->objectManager + ->expects($this->once()) + ->method('create') + ->with($expectedClass, $expectedArguments) + ->willReturn('an object yay!'); + + $this->interpreter + ->method('evaluate') + ->will( + $this->returnCallback( + function (array $arg) { + return $arg['value']; + } + ) + ); + + $actualResult = $this->configurableObject->evaluate($data); + self::assertSame('an object yay!', $actualResult); + } + + /** + * @dataProvider invalidDataProvider + */ + public function testInvalidCombinations( + $data, + $expectedClass, + $classParentsValueMap, + $expectedException, + $expectedExceptionMessage + ) { + + $this->expectException($expectedException); + $this->expectExceptionMessage($expectedExceptionMessage); + + $this->objectManagerConfig + ->method('getPreference') + ->with($expectedClass) + ->WillReturn('bar'); + $this->objectManagerConfig + ->method('getInstanceType') + ->with('bar') + ->willReturn($expectedClass); + + $this->classReader + ->method('getParents') + ->willReturnMap($classParentsValueMap); + + $this->objectManager + ->expects($this->never()) + ->method('create'); + + $this->interpreter + ->method('evaluate') + ->will( + $this->returnCallback( + function (array $arg) { + return $arg['value']; + } + ) + ); + + $actualResult = $this->configurableObject->evaluate($data); + self::assertSame('an object yay!', $actualResult); + } + + public function validDataProvider() + { + return [ + // Test most basic syntax + [ + [ + 'value' => 'MyFooClass', + ], + 'MyFooClass', + [ + ['MyFooClass', ['Something', 'skipme']], + ['Something', ['dontcare', 'SomethingElse']], + ['SomethingElse', [\Foo\Bar\ClassA::class, 'unrelated']], + ['skipme', []], + ['dontcare', []], + ['unrelated', []], + [\Foo\Bar\ClassA::class, []] + ], + [] + ], + // Test alternative data syntax + [ + [ + 'argument' => [ + 'class' => ['value' => 'MyFooClass'] + ] + ], + 'MyFooClass', + [ + ['MyFooClass', ['Something', 'skipme']], + ['Something', ['dontcare', 'SomethingElse']], + ['SomethingElse', [\Foo\Bar\ClassA::class, 'unrelated']], + ['skipme', []], + ['dontcare', []], + ['unrelated', []], + [\Foo\Bar\ClassA::class, []] + ], + [] + ], + // Test arguments + [ + [ + 'argument' => [ + 'class' => ['value' => 'MyFooClass'], + 'myarg' => ['value' => 'bar'], + ] + ], + 'MyFooClass', + [ + ['MyFooClass', ['Something', 'skipme']], + ['Something', ['dontcare', 'SomethingElse']], + ['SomethingElse', [\Foo\Bar\ClassA::class, 'unrelated']], + ['skipme', []], + ['dontcare', []], + ['unrelated', []], + [\Foo\Bar\ClassA::class, []] + ], + ['myarg' => 'bar'] + ], + // Test multiple matching whitelisted classes + [ + [ + 'argument' => [ + 'class' => ['value' => 'MyFooClass'], + 'myarg' => ['value' => 'bar'], + ] + ], + 'MyFooClass', + [ + ['MyFooClass', ['Something', 'skipme']], + ['Something', ['dontcare', 'SomethingElse']], + ['SomethingElse', [\Foo\Bar\ClassA::class, 'unrelated']], + ['skipme', []], + ['dontcare', []], + ['unrelated', [\Foo\Bar\InterfaceA::class]], + [\Foo\Bar\ClassA::class, []], + [\Foo\Bar\InterfaceA::class, []] + ], + ['myarg' => 'bar'] + ], + ]; + } + + public function invalidDataProvider() + { + return [ + [ + [ + 'notvalid' => 'sup' + ], + '', + [], + \InvalidArgumentException::class, + 'Node "argument" required for this type.' + ], + [ + [ + 'argument' => [ + 'notclass' => ['value' => 'doesntmatter'] + ] + ], + '', + [], + \InvalidArgumentException::class, + 'Node "argument" with name "class" is required for this type.' + ], + [ + [ + 'argument' => [ + 'class' => ['value' => 'MyFooClass'], + 'myarg' => ['value' => 'bar'], + ] + ], + 'MyFooClass', + [ + ['MyFooClass', ['Something', 'skipme']], + ['Something', ['dontcare', 'SomethingElse']], + ['SomethingElse', ['unrelated']], + ['skipme', []], + ['dontcare', []], + ['unrelated', []], + ], + \InvalidArgumentException::class, + 'Class argument is invalid: MyFooClass' + ], + ]; + } +} From 839095db2229adb41e0b226b6b8737fb67f00096 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 3 Jun 2019 08:21:59 -0500 Subject: [PATCH 0091/2437] MC-16940: Prevent errors from incorrect configurations --- .../Interpreter/ConfigurableObject.php | 20 +++++++++---------- .../Interpreter/ConfigurableObjectTest.php | 16 ++++----------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php index 2ea2856cc0f13..2691bc21357b0 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Argument/Interpreter/ConfigurableObject.php @@ -88,20 +88,20 @@ public function evaluate(array $data) $className = $arguments['class']; unset($arguments['class']); - } - $type = $this->objectManagerConfig->getInstanceType( - $this->objectManagerConfig->getPreference($className) - ); + $type = $this->objectManagerConfig->getInstanceType( + $this->objectManagerConfig->getPreference($className) + ); - $classParents = $this->getParents($type); + $classParents = $this->getParents($type); - $whitelistIntersection = array_intersect($classParents, $this->classWhitelist); + $whitelistIntersection = array_intersect($classParents, $this->classWhitelist); - if (empty($whitelistIntersection)) { - throw new \InvalidArgumentException( - sprintf('Class argument is invalid: %s', $className) - ); + if (empty($whitelistIntersection)) { + throw new \InvalidArgumentException( + sprintf('Class argument is invalid: %s', $className) + ); + } } return $this->objectManager->create($className, $arguments); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php b/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php index 8b4f2a35c0802..0d4be68a4c1bb 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/UiComponent/Argument/Interpreter/ConfigurableObjectTest.php @@ -159,21 +159,13 @@ function (array $arg) { public function validDataProvider() { return [ - // Test most basic syntax + // Test most basic syntax with no arguments [ [ - 'value' => 'MyFooClass', - ], - 'MyFooClass', - [ - ['MyFooClass', ['Something', 'skipme']], - ['Something', ['dontcare', 'SomethingElse']], - ['SomethingElse', [\Foo\Bar\ClassA::class, 'unrelated']], - ['skipme', []], - ['dontcare', []], - ['unrelated', []], - [\Foo\Bar\ClassA::class, []] + 'value' => 'MyObject', ], + 'MyObject', + [], [] ], // Test alternative data syntax From cd0618ae7885208e8cab4fac92b8d8bac87dd749 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Mon, 3 Jun 2019 17:26:13 +0300 Subject: [PATCH 0092/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Added API-functional test --- .../testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php index c25956a4858ec..5b7b1bf606932 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php @@ -104,7 +104,7 @@ public function testShipmentTrackWithFailedOrderId() ShipmentTrackInterface::TITLE => 'Shipment title', ShipmentTrackInterface::CARRIER_CODE => Track::CUSTOM_CARRIER_CODE, ]; - $expectedMessage = 'The shipment doesn\'t belong to the order.'; + $expectedMessage = 'Could not save the shipment tracking.'; try { $this->_webApiCall($this->getServiceInfo(), ['entity' => $trackData]); From 1c0879c90dda4050fecb02e743d676e9b7036072 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Mon, 3 Jun 2019 12:11:42 -0500 Subject: [PATCH 0093/2437] MC-15134: Currency rate config update - Updated currency matrix template --- .../view/adminhtml/templates/system/currency/rate/matrix.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/system/currency/rate/matrix.phtml b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/system/currency/rate/matrix.phtml index 8a16eb71e0853..9701fc928e861 100644 --- a/app/code/Magento/CurrencySymbol/view/adminhtml/templates/system/currency/rate/matrix.phtml +++ b/app/code/Magento/CurrencySymbol/view/adminhtml/templates/system/currency/rate/matrix.phtml @@ -28,7 +28,7 @@ $_rates = ($_newRates) ? $_newRates : $_oldRates; <tr> <th> </th> <?php $_i = 0; foreach ($block->getAllowedCurrencies() as $_currencyCode): ?> - <th><span><?= /* @escapeNotVerified */ $_currencyCode ?></span></th> + <th><span><?= $block->escapeHtml($_currencyCode) ?></span></th> <?php endforeach; ?> </tr> </thead> From ceb5524a85fa6cbfe5d63ff4d7912cf26cf6b926 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 4 Jun 2019 13:31:10 -0500 Subject: [PATCH 0094/2437] MC-16114: Update Email Template Filter --- lib/internal/Magento/Framework/Filter/Template.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index c32df1b175d8f..1bb063fb279a6 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -90,8 +90,7 @@ class Template implements \Zend_Filter_Interface */ private $restrictedMethodsByInstanceType = [ \Magento\Framework\DB\Adapter\AdapterInterface::class => [ - 'rawquery', - 'rawfetchrow' + '*' ] ]; @@ -417,7 +416,9 @@ private function validateVariableMethodCall($object, string $method): void } } else { foreach ($this->restrictedMethodsByInstanceType as $instanceType => $restrictedMethods) { - if ($object instanceof $instanceType && in_array(mb_strtolower($method), $restrictedMethods)) { + if ($object instanceof $instanceType && + ($restrictedMethods === '*' || in_array(mb_strtolower($method), $restrictedMethods)) + ) { throw new \InvalidArgumentException("Method $method cannot be called from template."); } } From 9a9b6e4733ec3dab4c0bf133bf9d4211e9789749 Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Tue, 4 Jun 2019 14:58:11 -0500 Subject: [PATCH 0095/2437] MC-16056: Template engine optimizations --- .../Magento/Framework/Filter/Template.php | 108 ++++++++---------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 6a04e8e8c6953..6654135b7f6c6 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -9,9 +9,6 @@ */ namespace Magento\Framework\Filter; -use Magento\Framework\Model\AbstractExtensibleModel; -use Magento\Framework\Model\AbstractModel; - /** * Template filter * @@ -400,7 +397,7 @@ protected function getParameters($value) */ private function validateVariableMethodCall($object, string $method): void { - if ($object === $this) { + if ($object instanceof self || $object instanceof \Magento\Framework\DataObject) { if (in_array(mb_strtolower($method), $this->restrictedMethods)) { throw new \InvalidArgumentException("Method $method cannot be called from template."); } @@ -408,28 +405,7 @@ private function validateVariableMethodCall($object, string $method): void } /** - * Check allowed methods for data objects. - * - * Deny calls for methods that may disrupt template processing. - * - * @param object $object - * @param string $method - * @return bool - * @throws \InvalidArgumentException - */ - private function isAllowedDataObjectMethod($object, string $method): bool - { - if ($object instanceof AbstractExtensibleModel || $object instanceof AbstractModel) { - if (in_array(mb_strtolower($method), $this->restrictedMethods)) { - throw new \InvalidArgumentException("Method $method cannot be called from template."); - } - } - - return true; - } - - /** - * Return variable value for var construction + * Return variable value for var construction. * * @param string $value raw parameters * @param string $default default value @@ -448,45 +424,18 @@ protected function getVariable($value, $default = '{no_value_defined}') if ($i == 0 && isset($this->templateVars[$stackVars[$i]['name']])) { // Getting of template value $stackVars[$i]['variable'] = & $this->templateVars[$stackVars[$i]['name']]; - } elseif (isset($stackVars[$i - 1]['variable']) - && $stackVars[$i - 1]['variable'] instanceof \Magento\Framework\DataObject - ) { - // If data object calling methods or getting properties + } elseif (isset($stackVars[$i - 1]['variable']) && is_object($stackVars[$i - 1]['variable'])) { if ($stackVars[$i]['type'] == 'property') { - $caller = 'get' . $this->string->upperCaseWords($stackVars[$i]['name'], '_', ''); - $stackVars[$i]['variable'] = method_exists( + $stackVars[$i]['variable'] = $this->evaluateObjectPropertyAccess( $stackVars[$i - 1]['variable'], - $caller - ) ? $stackVars[$i - 1]['variable']->{$caller}() : $stackVars[$i - 1]['variable']->getData( $stackVars[$i]['name'] ); } elseif ($stackVars[$i]['type'] == 'method') { - // Calling of data object method - if (method_exists($stackVars[$i - 1]['variable'], $stackVars[$i]['name']) - || substr($stackVars[$i]['name'], 0, 3) == 'get' - ) { - $stackVars[$i]['args'] = $this->getStackArgs($stackVars[$i]['args']); - - if ($this->isAllowedDataObjectMethod($stackVars[$i - 1]['variable'], $stackVars[$i]['name'])) { - $stackVars[$i]['variable'] = call_user_func_array( - [$stackVars[$i - 1]['variable'], $stackVars[$i]['name']], - $stackVars[$i]['args'] - ); - } - } - } - $last = $i; - } elseif (isset($stackVars[$i - 1]['variable']) - && is_object($stackVars[$i - 1]['variable']) - && $stackVars[$i]['type'] == 'method' - ) { - // Calling object methods - $object = $stackVars[$i - 1]['variable']; - $method = $stackVars[$i]['name']; - if (method_exists($object, $method)) { - $args = $this->getStackArgs($stackVars[$i]['args']); - $this->validateVariableMethodCall($object, $method); - $stackVars[$i]['variable'] = call_user_func_array([$object, $method], $args); + $stackVars[$i]['variable'] = $this->evaluateObjectMethodCall( + $stackVars[$i - 1]['variable'], + $stackVars[$i]['name'], + $stackVars[$i]['args'] + ); } $last = $i; } @@ -500,6 +449,45 @@ protected function getVariable($value, $default = '{no_value_defined}') return $result; } + /** + * Evaluate object property access. + * + * @param object $object + * @param string $property + * @return null + */ + private function evaluateObjectPropertyAccess($object, $property) + { + $method = 'get' . $this->string->upperCaseWords($property, '_', ''); + $this->validateVariableMethodCall($object, $method); + return method_exists($object, $method) + ? $object->{$method}() + : (($object instanceof \Magento\Framework\DataObject) ? $object->getData($property) : null); + } + + /** + * Evaluate object method call. + * + * @param object $object + * @param string $method + * @param array $arguments + * @return mixed|null + */ + private function evaluateObjectMethodCall($object, $method, $arguments) + { + if (method_exists($object, $method) + || ($object instanceof \Magento\Framework\DataObject && substr($method, 0, 3) == 'get') + ) { + $arguments = $this->getStackArgs($arguments); + $this->validateVariableMethodCall($object, $method); + return call_user_func_array( + [$object, $method], + $arguments + ); + } + return null; + } + /** * Loops over a set of stack args to process variables into array argument values * From e4062ebe6fb4ed4fd0b9253be543cfbe6e585b7b Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 5 Jun 2019 18:14:51 +0300 Subject: [PATCH 0096/2437] Refactoring the Tests, Action Groups by removing the not needed Action Groups --- ...rtIsNotVisibleCartPagerTextActionGroup.xml | 2 +- .../AssertTextIsVisibleOnPageActionGroup.xml | 3 +- ...orefrontAdd20ProductsToCartActionGroup.xml | 112 ---------------- .../StorefrontAddProductToCartActionGroup.xml | 13 ++ .../StorefrontDelete20ProductsActionGroup.xml | 32 ----- .../StorefrontOpenCartPageActionGroup.xml | 14 ++ .../StorefrontRemoveCartItemActionGroup.xml | 13 ++ .../Test/Mftf/Page/CheckoutCartPage.xml | 1 + .../Section/StorefrontCartToolbarSection.xml | 17 +++ ...ShoppingCartWithMoreThan20ProductsTest.xml | 121 +++++++++++++++--- ...ingPagerShoppingCartWith20ProductsTest.xml | 106 ++++++++++++++- ...PagerForOneItemPerPageAnd2ProductsTest.xml | 5 +- 12 files changed, 270 insertions(+), 169 deletions(-) delete mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml delete mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml index d08a6f3cf5053..2072cb6df1dc1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml @@ -12,6 +12,6 @@ <argument name="text" type="string"/> </arguments> <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> - <dontSee userInput="{{text}}" stepKey="VerifyMissingPagerText"/> + <dontSee userInput="{{text}}" selector="{{StorefrontCartToolbarSection.toolbarNumber}}" stepKey="VerifyMissingPagerText"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml index 0eb584d4c2242..8f1920a28d9f1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml @@ -10,8 +10,9 @@ <actionGroup name="AssertTextIsVisibleOnPageActionGroup"> <arguments> <argument name="text" type="string"/> + <argument name="selector" type="string"/> </arguments> <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="{{text}}" stepKey="VerifyPageText"/> + <see userInput="{{text}}" selector="{{selector}}" stepKey="VerifyPageText"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml deleted file mode 100644 index b4662afffec69..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAdd20ProductsToCartActionGroup.xml +++ /dev/null @@ -1,112 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateAndAddToCart20ProductsActionGroup"> - <createData entity="SimpleTwo" stepKey="simpleProduct1"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct1CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage1"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart1"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct2"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct2CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage2"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart2"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct3"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct3CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage3"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart3"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct4"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct4CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage4"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart4"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct5"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct5CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage5"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart5"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct6"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct6CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage6"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart6"/> - <waitForPageLoad stepKey="waitForPageLoad6"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct7"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct7CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage7"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart7"/> - <waitForPageLoad stepKey="waitForPageLoad7"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct8"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct8CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage8"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart8"/> - <waitForPageLoad stepKey="waitForPageLoad8"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct9"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct9CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage9"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart9"/> - <waitForPageLoad stepKey="waitForPageLoad9"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct10"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct10CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage10"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart10"/> - <waitForPageLoad stepKey="waitForPageLoad10"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct11"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct11CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage11"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart11"/> - <waitForPageLoad stepKey="waitForPageLoad11"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct12"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct12CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage12"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart12"/> - <waitForPageLoad stepKey="waitForPageLoad12"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct13"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct13CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage13"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart13"/> - <waitForPageLoad stepKey="waitForPageLoad13"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct14"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct14CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage14"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart14"/> - <waitForPageLoad stepKey="waitForPageLoad14"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct15"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct15CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage15"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart15"/> - <waitForPageLoad stepKey="waitForPageLoad15"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct16"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct16CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage16"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart16"/> - <waitForPageLoad stepKey="waitForPageLoad16"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct17"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct17CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage17"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart17"/> - <waitForPageLoad stepKey="waitForPageLoad17"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct18"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct18CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage18"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart18"/> - <waitForPageLoad stepKey="waitForPageLoad18"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct19"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct19CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage19"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart19"/> - <waitForPageLoad stepKey="waitForPageLoad19"/> - - <createData entity="SimpleTwo" stepKey="simpleProduct20"/> - <amOnPage url="{{StorefrontProductPage.url($$simpleProduct20CartItems.custom_attributes[url_key]$$)}}" stepKey="goToProductPage20"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart20"/> - <waitForPageLoad stepKey="waitForPageLoad20"/> - - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml new file mode 100644 index 0000000000000..f2d4088370a2b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontRemoveCartItemActionGroup"> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml deleted file mode 100644 index f3db7f989d31d..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontDelete20ProductsActionGroup.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="Delete20ProductsActionGroup"> - <deleteData createDataKey="simpleProduct1CartItems" stepKey="deleteCartItem1"/> - <deleteData createDataKey="simpleProduct2CartItems" stepKey="deleteCartItem2"/> - <deleteData createDataKey="simpleProduct3CartItems" stepKey="deleteCartItem3"/> - <deleteData createDataKey="simpleProduct4CartItems" stepKey="deleteCartItem4"/> - <deleteData createDataKey="simpleProduct5CartItems" stepKey="deleteCartItem5"/> - <deleteData createDataKey="simpleProduct6CartItems" stepKey="deleteCartItem6"/> - <deleteData createDataKey="simpleProduct7CartItems" stepKey="deleteCartItem7"/> - <deleteData createDataKey="simpleProduct8CartItems" stepKey="deleteCartItem8"/> - <deleteData createDataKey="simpleProduct9CartItems" stepKey="deleteCartItem9"/> - <deleteData createDataKey="simpleProduct10CartItems" stepKey="deleteCartItem10"/> - <deleteData createDataKey="simpleProduct11CartItems" stepKey="deleteCartItem11"/> - <deleteData createDataKey="simpleProduct12CartItems" stepKey="deleteCartItem12"/> - <deleteData createDataKey="simpleProduct13CartItems" stepKey="deleteCartItem13"/> - <deleteData createDataKey="simpleProduct14CartItems" stepKey="deleteCartItem14"/> - <deleteData createDataKey="simpleProduct15CartItems" stepKey="deleteCartItem15"/> - <deleteData createDataKey="simpleProduct16CartItems" stepKey="deleteCartItem16"/> - <deleteData createDataKey="simpleProduct17CartItems" stepKey="deleteCartItem17"/> - <deleteData createDataKey="simpleProduct18CartItems" stepKey="deleteCartItem18"/> - <deleteData createDataKey="simpleProduct19CartItems" stepKey="deleteCartItem19"/> - <deleteData createDataKey="simpleProduct20CartItems" stepKey="deleteCartItem20"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml new file mode 100644 index 0000000000000..fe1e48e00c5bb --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenCartPageActionGroup"> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="openCartPage" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml new file mode 100644 index 0000000000000..f2d4088370a2b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontRemoveCartItemActionGroup"> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml index a77b07a129dce..cf7f2baeb4b26 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml @@ -9,6 +9,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> + <section name="StorefrontCartToolbarSection"/> <section name="CheckoutCartProductSection"/> <section name="CheckoutCartSummarySection"/> <section name="CheckoutCartCrossSellSection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml new file mode 100644 index 0000000000000..ff40449369530 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCartToolbarSection"> + <element name="toolbarNumber" type="text" selector="div.toolbar > .pager > .toolbar-amount > .toolbar-number" /> + <element name="toolbarPager" type="text" selector="div.toolbar > .pager > .pages" /> + <element name="toolbarNextPage" type="text" selector="div.toolbar > .pager > .pages > .pages-item-next" /> + <element name="toolbarPreviousPage" type="text" selector="div.toolbar > .pager > .pages > .pages-item-previous" /> + </section> +</sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml index 0c9411db4f5ca..41554db6d3a4f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -22,30 +22,121 @@ <!--Set the default number of items on cart which is 20--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> - <!--Create and add to cart 20 products--> - <actionGroup ref="CreateAndAddToCart20ProductsActionGroup" stepKey="CartItems" /> - - <!-- Create and Add the 21th product to the shopping cart --> - <createData entity="SimpleTwo" stepKey="simpleProduct21CartItems"/> + <createData entity="SimpleTwo" stepKey="simpleProduct1"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct2"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem2"> + <argument name="product" value="$simpleProduct2$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct3"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem3"> + <argument name="product" value="$simpleProduct3$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct4"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem4"> + <argument name="product" value="$simpleProduct4$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct5"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem5"> + <argument name="product" value="$simpleProduct5$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct6"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem6"> + <argument name="product" value="$simpleProduct6$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct7"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem7"> + <argument name="product" value="$simpleProduct7$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct8"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem8"> + <argument name="product" value="$simpleProduct8$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct9"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem9"> + <argument name="product" value="$simpleProduct9$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct10"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem10"> + <argument name="product" value="$$simpleProduct10$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct11"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem11"> + <argument name="product" value="$$simpleProduct11$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct12"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem12"> + <argument name="product" value="$$simpleProduct12$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct13"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem13"> + <argument name="product" value="$$simpleProduct13$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct14"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem14"> + <argument name="product" value="$$simpleProduct14$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct15"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem15"> + <argument name="product" value="$$simpleProduct15$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct16"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem16"> + <argument name="product" value="$$simpleProduct16$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct17"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem17"> + <argument name="product" value="$$simpleProduct17$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct18"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem18"> + <argument name="product" value="$$simpleProduct18$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct19"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem19"> + <argument name="product" value="$$simpleProduct19$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct20"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem20"> + <argument name="product" value="$$simpleProduct20$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct21"/> <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem21"> - <argument name="product" value="$$simpleProduct21CartItems$$"/> + <argument name="product" value="$$simpleProduct21$$"/> </actionGroup> </before> <after> - <!--Set back the default number of items on cart which is 20--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> - - <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> - <deleteData createDataKey="simpleProduct21CartItems" stepKey="deleteCartItem21"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteCartItem1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteCartItem2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteCartItem3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteCartItem4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteCartItem5"/> + <deleteData createDataKey="simpleProduct6" stepKey="deleteCartItem6"/> + <deleteData createDataKey="simpleProduct7" stepKey="deleteCartItem7"/> + <deleteData createDataKey="simpleProduct8" stepKey="deleteCartItem8"/> + <deleteData createDataKey="simpleProduct9" stepKey="deleteCartItem9"/> + <deleteData createDataKey="simpleProduct10" stepKey="deleteCartItem10"/> + <deleteData createDataKey="simpleProduct11" stepKey="deleteCartItem11"/> + <deleteData createDataKey="simpleProduct12" stepKey="deleteCartItem12"/> + <deleteData createDataKey="simpleProduct13" stepKey="deleteCartItem13"/> + <deleteData createDataKey="simpleProduct14" stepKey="deleteCartItem14"/> + <deleteData createDataKey="simpleProduct15" stepKey="deleteCartItem15"/> + <deleteData createDataKey="simpleProduct16" stepKey="deleteCartItem16"/> + <deleteData createDataKey="simpleProduct17" stepKey="deleteCartItem17"/> + <deleteData createDataKey="simpleProduct18" stepKey="deleteCartItem18"/> + <deleteData createDataKey="simpleProduct19" stepKey="deleteCartItem19"/> + <deleteData createDataKey="simpleProduct20" stepKey="deleteCartItem20"/> + <deleteData createDataKey="simpleProduct21" stepKey="deleteCartItem21"/> </after> - <!-- Go to the shopping cart and check if the pager is visible--> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerText"> <argument name="text" value="Items 1 to 20 of 21 total"/> + <argument name="selector" value="{{StorefrontCartToolbarSection.toolbarNumber}}"/> </actionGroup> - - <!-- Remove a product from cart and check if the pager is not visible--> - <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCheckoutCart"/> + <actionGroup ref="StorefrontRemoveCartItemActionGroup" stepKey="removeCartItem" /> <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText2" > <argument name="text" value="Items 1 to 20"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml index 57a45112a668b..aeeb8a0d718e8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml @@ -21,15 +21,111 @@ <before> <!--Set the default number of items on cart which is 20--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> - <!--Create and add to cart 20 products--> - <actionGroup ref="CreateAndAddToCart20ProductsActionGroup" stepKey="CartItems" /> + <createData entity="SimpleTwo" stepKey="simpleProduct1"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct2"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct3"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem3"> + <argument name="product" value="$$simpleProduct3$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct4"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem4"> + <argument name="product" value="$$simpleProduct4$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct5"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem5"> + <argument name="product" value="$$simpleProduct5$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct6"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem6"> + <argument name="product" value="$$simpleProduct6$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct7"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem7"> + <argument name="product" value="$$simpleProduct7$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct8"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem8"> + <argument name="product" value="$$simpleProduct8$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct9"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem9"> + <argument name="product" value="$$simpleProduct9$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct10"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem10"> + <argument name="product" value="$$simpleProduct10$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct11"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem11"> + <argument name="product" value="$$simpleProduct11$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct12"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem12"> + <argument name="product" value="$$simpleProduct12$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct13"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem13"> + <argument name="product" value="$$simpleProduct13$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct14"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem14"> + <argument name="product" value="$$simpleProduct14$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct15"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem15"> + <argument name="product" value="$$simpleProduct15$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct16"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem16"> + <argument name="product" value="$$simpleProduct16$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct17"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem17"> + <argument name="product" value="$$simpleProduct17$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct18"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem18"> + <argument name="product" value="$$simpleProduct18$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct19"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem19"> + <argument name="product" value="$$simpleProduct19$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct20"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="CartItem20"> + <argument name="product" value="$$simpleProduct20$$"/> + </actionGroup> </before> <after> - <!--Delete the test products--> - <actionGroup ref="Delete20ProductsActionGroup" stepKey="Delete20CreatedProducts" /> + <deleteData createDataKey="simpleProduct1" stepKey="deleteCartItem1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteCartItem2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteCartItem3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteCartItem4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteCartItem5"/> + <deleteData createDataKey="simpleProduct6" stepKey="deleteCartItem6"/> + <deleteData createDataKey="simpleProduct7" stepKey="deleteCartItem7"/> + <deleteData createDataKey="simpleProduct8" stepKey="deleteCartItem8"/> + <deleteData createDataKey="simpleProduct9" stepKey="deleteCartItem9"/> + <deleteData createDataKey="simpleProduct10" stepKey="deleteCartItem10"/> + <deleteData createDataKey="simpleProduct11" stepKey="deleteCartItem11"/> + <deleteData createDataKey="simpleProduct12" stepKey="deleteCartItem12"/> + <deleteData createDataKey="simpleProduct13" stepKey="deleteCartItem13"/> + <deleteData createDataKey="simpleProduct14" stepKey="deleteCartItem14"/> + <deleteData createDataKey="simpleProduct15" stepKey="deleteCartItem15"/> + <deleteData createDataKey="simpleProduct16" stepKey="deleteCartItem16"/> + <deleteData createDataKey="simpleProduct17" stepKey="deleteCartItem17"/> + <deleteData createDataKey="simpleProduct18" stepKey="deleteCartItem18"/> + <deleteData createDataKey="simpleProduct19" stepKey="deleteCartItem19"/> + <deleteData createDataKey="simpleProduct20" stepKey="deleteCartItem20"/> </after> <!-- Go to the shopping cart and check if the pager is missing--> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText" > <argument name="text" value="Items 1 to 20"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml index 5ba1b0122c6e6..2b6c31a941efe 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -21,7 +21,6 @@ <before> <!-- Changing the number of items to display in cart--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 1" /> - <createData entity="SimpleTwo" stepKey="createSimpleProduct1"/> <createData entity="SimpleTwo" stepKey="createSimpleProduct2"/> <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage1"> @@ -34,13 +33,13 @@ <after> <!--Set back the default number of items on cart which is 20--> <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> - <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> </after> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> <argument name="text" value="Items 1 to 1 of 2 total"/> + <argument name="selector" value="{{StorefrontCartToolbarSection.toolbarNumber}}"/> </actionGroup> </test> </tests> From 86ac6e56e190a4407189352bbbdbdd8b850ad921 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 5 Jun 2019 18:50:49 +0300 Subject: [PATCH 0097/2437] Removing not needed ActionGroup. Adjusting the Test --- .../StorefrontAddProductToCartActionGroup.xml | 13 ------------- ...kPagerShoppingCartWithMoreThan20ProductsTest.xml | 1 - 2 files changed, 14 deletions(-) delete mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml deleted file mode 100644 index f2d4088370a2b..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontRemoveCartItemActionGroup"> - <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCart"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml index 41554db6d3a4f..08922c019bdcd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -108,7 +108,6 @@ </actionGroup> </before> <after> - <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> <deleteData createDataKey="simpleProduct1" stepKey="deleteCartItem1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteCartItem2"/> <deleteData createDataKey="simpleProduct3" stepKey="deleteCartItem3"/> From 142e5fd71839d3f28a99fedf6a3ce11ad9cfdff1 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 5 Jun 2019 12:57:50 -0500 Subject: [PATCH 0098/2437] MC-15919: Customer attribute label update - Updated meta data resolver object --- app/code/Magento/Customer/Model/AttributeMetadataResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 979730eb1c9c2..84162bb8dae09 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -144,7 +144,7 @@ public function getAttributesMeta( $attribute, $meta['arguments']['data']['config'] ); - + $meta['arguments']['data']['config']['__disableTmpl'] = true; return $meta; } From 52dbfdf1905825b947265bc1fc44b159973e3790 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 5 Jun 2019 08:47:44 -0500 Subject: [PATCH 0099/2437] MC-16963: Admin panel Design Configuration changes logs are missing --- .../Model/Design/BackendModelFactory.php | 22 ++++++++++++++----- .../Model/Design/Config/ValueChecker.php | 6 ++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/BackendModelFactory.php b/app/code/Magento/Theme/Model/Design/BackendModelFactory.php index 155afa89c4173..2896e9f101298 100644 --- a/app/code/Magento/Theme/Model/Design/BackendModelFactory.php +++ b/app/code/Magento/Theme/Model/Design/BackendModelFactory.php @@ -11,6 +11,9 @@ use Magento\Theme\Model\Design\Config\MetadataProvider; use Magento\Theme\Model\ResourceModel\Design\Config\CollectionFactory; +/** + * Class BackendModelFactory + */ class BackendModelFactory extends ValueFactory { /** @@ -58,13 +61,15 @@ public function __construct( */ public function create(array $data = []) { + $storedData = $this->getStoredData($data['scope'], $data['scopeId'], $data['config']['path']); + $backendModelData = array_replace_recursive( - $this->getStoredData($data['scope'], $data['scopeId'], $data['config']['path']), + $storedData, [ 'path' => $data['config']['path'], 'scope' => $data['scope'], 'scope_id' => $data['scopeId'], - 'field_config' => $data['config'], + 'field_config' => $data['config'] ] ); @@ -76,6 +81,10 @@ public function create(array $data = []) $backendModel = $this->getNewBackendModel($backendType, $backendModelData); $backendModel->setValue($data['value']); + foreach ($storedData as $key => $value) { + $backendModel->setOrigData($key, $value); + } + return $backendModel; } @@ -166,9 +175,12 @@ protected function getMetadata() { if (!$this->metadata) { $this->metadata = $this->metadataProvider->get(); - array_walk($this->metadata, function (&$value) { - $value = $value['path']; - }); + array_walk( + $this->metadata, + function (&$value) { + $value = $value['path']; + } + ); } return $this->metadata; } diff --git a/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php b/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php index 42801f57c8822..00f30e9d7976c 100644 --- a/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php +++ b/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php @@ -8,6 +8,9 @@ use Magento\Framework\App\Config as AppConfig; use Magento\Framework\App\ScopeFallbackResolverInterface; +/** + * Class ValueChecker + */ class ValueChecker { /** @@ -61,7 +64,7 @@ public function isDifferentFromDefault($value, $scope, $scopeId, array $fieldCon $fieldConfig ), $this->valueProcessor->process( - $this->appConfig->getValue($fieldConfig['path'], $scope, $scopeId), + ($this->appConfig->getValue($fieldConfig['path'], $scope, $scopeId) ?? ""), $scope, $scopeId, $fieldConfig @@ -80,6 +83,7 @@ public function isDifferentFromDefault($value, $scope, $scopeId, array $fieldCon */ protected function isEqual($value, $defaultValue) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction switch (gettype($value)) { case 'array': return $this->isEqualArrays($value, $defaultValue); From 5dad282326cc4c7aff794ad1db1df283be28540c Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 5 Jun 2019 14:38:26 +0300 Subject: [PATCH 0100/2437] MAGETWO-99862: wishlist_item_option table contains unrelated data --- .../Wishlist/Controller/Index/Plugin.php | 8 ++- .../Test/Unit/Controller/Index/PluginTest.php | 37 ++++++---- .../Wishlist/Controller/Index/PluginTest.php | 71 +++++++++++++++++++ 3 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php diff --git a/app/code/Magento/Wishlist/Controller/Index/Plugin.php b/app/code/Magento/Wishlist/Controller/Index/Plugin.php index 60d6859613d5e..150e4de72b40d 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Plugin.php +++ b/app/code/Magento/Wishlist/Controller/Index/Plugin.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -12,6 +11,9 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\App\Response\RedirectInterface; +/** + * Wishlist plugin before dispatch + */ class Plugin { /** @@ -75,7 +77,9 @@ public function beforeDispatch(\Magento\Framework\App\ActionInterface $subject, if (!$this->customerSession->getBeforeWishlistUrl()) { $this->customerSession->setBeforeWishlistUrl($this->redirector->getRefererUrl()); } - $this->customerSession->setBeforeWishlistRequest($request->getParams()); + $data = $request->getParams(); + unset($data['login']); + $this->customerSession->setBeforeWishlistRequest($data); $this->customerSession->setBeforeRequestParams($this->customerSession->getBeforeWishlistRequest()); $this->customerSession->setBeforeModuleName('wishlist'); $this->customerSession->setBeforeControllerName('index'); diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php index 399b48073b339..2b583f9101516 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/PluginTest.php @@ -6,6 +6,9 @@ namespace Magento\Wishlist\Test\Unit\Controller\Index; +/** + * Test for wishlist plugin before dispatch + */ class PluginTest extends \PHPUnit\Framework\TestCase { /** @@ -38,22 +41,26 @@ class PluginTest extends \PHPUnit\Framework\TestCase */ protected $request; + /** + * @inheritdoc + */ protected function setUp() { $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'authenticate', - 'getBeforeWishlistUrl', - 'setBeforeWishlistUrl', - 'setBeforeWishlistRequest', - 'getBeforeWishlistRequest', - 'setBeforeRequestParams', - 'setBeforeModuleName', - 'setBeforeControllerName', - 'setBeforeAction', - ]) - ->getMock(); + ->setMethods( + [ + 'authenticate', + 'getBeforeWishlistUrl', + 'setBeforeWishlistUrl', + 'setBeforeWishlistRequest', + 'getBeforeWishlistRequest', + 'setBeforeRequestParams', + 'setBeforeModuleName', + 'setBeforeControllerName', + 'setBeforeAction', + ] + )->getMock(); $this->authenticationState = $this->createMock(\Magento\Wishlist\Model\AuthenticationState::class); $this->config = $this->createMock(\Magento\Framework\App\Config::class); @@ -62,6 +69,9 @@ protected function setUp() $this->request = $this->createMock(\Magento\Framework\App\Request\Http::class); } + /** + * @inheritdoc + */ protected function tearDown() { unset( @@ -96,6 +106,7 @@ public function testBeforeDispatch() $refererUrl = 'http://referer-url.com'; $params = [ 'product' => 1, + 'login' => [], ]; $actionFlag = $this->createMock(\Magento\Framework\App\ActionFlag::class); @@ -139,7 +150,7 @@ public function testBeforeDispatch() ->willReturnSelf(); $this->customerSession->expects($this->once()) ->method('setBeforeWishlistRequest') - ->with($params) + ->with(['product' => 1]) ->willReturnSelf(); $this->customerSession->expects($this->once()) ->method('getBeforeWishlistRequest') diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php new file mode 100644 index 0000000000000..4c8937e1c6e9a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Controller\Index; + +use Magento\TestFramework\TestCase\AbstractController; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Catalog\Api\ProductRepositoryInterface; + +/** + * Test for wishlist plugin before dispatch + */ +class PluginTest extends AbstractController +{ + /** + * @var CustomerSession + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->customerSession = $this->_objectManager->get(CustomerSession::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerSession->logout(); + $this->customerSession = null; + parent::tearDown(); + } + + /** + * Test for adding product to wishlist with invalidate credentials + * + * @return void + * @magentoDataFixture Magento/Catalog/_files/product_simple_xss.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppArea frontend + */ + public function testAddActionProductWithInvalidCredentials(): void + { + $this->getRequest()->setPostValue( + [ + 'login' => [ + 'username' => 'invalidCustomer@example.com', + 'password' => 'invalidPassword', + ], + ] + ); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + + $product = $productRepository->get('product-with-xss'); + + $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); + + $this->assertArrayNotHasKey('login', $this->customerSession->getBeforeWishlistRequest()); + } +} From cc3ce3d72904007b5d6b251f526f587cf0d4e83a Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 6 Jun 2019 10:40:36 -0500 Subject: [PATCH 0101/2437] MC-16114: Update Email Template Filter --- lib/internal/Magento/Framework/Filter/Template.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 1bb063fb279a6..0515b60cc3165 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -417,7 +417,7 @@ private function validateVariableMethodCall($object, string $method): void } else { foreach ($this->restrictedMethodsByInstanceType as $instanceType => $restrictedMethods) { if ($object instanceof $instanceType && - ($restrictedMethods === '*' || in_array(mb_strtolower($method), $restrictedMethods)) + (in_array('*', $restrictedMethods) || in_array(mb_strtolower($method), $restrictedMethods)) ) { throw new \InvalidArgumentException("Method $method cannot be called from template."); } From 8b623dbea0ffeeca7ce698e5b5e7d76b95f0916f Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Thu, 6 Jun 2019 11:28:55 -0500 Subject: [PATCH 0102/2437] MC-15919: Customer attribute label update - Updated meta data resolver object --- .../Magento/Customer/Model/AttributeMetadataResolver.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 84162bb8dae09..a41d52cdc45a4 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -113,7 +113,12 @@ public function getAttributesMeta( // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value foreach (self::$metaProperties as $metaName => $origName) { $value = $attribute->getDataUsingMethod($origName); - $meta['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ($metaName === 'label') { + $meta['arguments']['data']['config'][$metaName] = __($value); + $meta['arguments']['data']['config']['__disableTmpl'] = [$metaName => true]; + } else { + $meta['arguments']['data']['config'][$metaName] = $value; + } if ('frontend_input' === $origName) { $meta['arguments']['data']['config']['formElement'] = self::$formElement[$value] ?? $value; } @@ -144,7 +149,6 @@ public function getAttributesMeta( $attribute, $meta['arguments']['data']['config'] ); - $meta['arguments']['data']['config']['__disableTmpl'] = true; return $meta; } From 3d19d0f53aa7ed2903ae9170bac92046ec061348 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Thu, 6 Jun 2019 15:41:17 -0500 Subject: [PATCH 0103/2437] MC-16963: Admin panel Design Configuration changes logs are missing --- .../Magento/Theme/Model/Design/BackendModelFactory.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/BackendModelFactory.php b/app/code/Magento/Theme/Model/Design/BackendModelFactory.php index 2896e9f101298..df4ad381ca0d8 100644 --- a/app/code/Magento/Theme/Model/Design/BackendModelFactory.php +++ b/app/code/Magento/Theme/Model/Design/BackendModelFactory.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Theme\Model\Design; use Magento\Framework\App\Config\Value; @@ -81,8 +84,11 @@ public function create(array $data = []) $backendModel = $this->getNewBackendModel($backendType, $backendModelData); $backendModel->setValue($data['value']); - foreach ($storedData as $key => $value) { - $backendModel->setOrigData($key, $value); + if ($storedData) { + foreach ($storedData as $key => $value) { + $backendModel->setOrigData($key, $value); + } + $backendModel->setOrigData('field_config', $data['config']); } return $backendModel; From 89dd87d5a150edccb542085b51c3b5bfdf6592f7 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 7 Jun 2019 18:04:29 +0300 Subject: [PATCH 0104/2437] Convert NewCustomerPasswordComplexityTest --- .../StorefrontFillRegistryFormActionGroup.xml | 31 +++++++++++++++++ .../StorefrontSeeHeaderLinksActionGroup.xml | 19 +++++++++++ .../Mftf/Section/StorefrontHeaderSection.xml | 16 +++++++++ .../NewCustomerPasswordComplexityTest.xml | 33 +++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml create mode 100644 app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml new file mode 100644 index 0000000000000..0a1440937a554 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillRegistryFormActionGroup"> + + <!--Fill Registry Form with password length is below 8 characters --> + <fillField stepKey="fillFirstName1stVariation" userInput="John" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> + <fillField stepKey="fillLastName1stVariation" userInput="Doe" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> + <fillField stepKey="fillEmail1stVariation" userInput="johndoe@domain.com" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> + <fillField stepKey="fillPassword1stVariation" userInput="123123" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> + <fillField stepKey="fillConfirmPassword1stVariation" userInput="123123" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> + <click stepKey="clickCreateAccountButton1stVariation" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <see userInput="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored." stepKey="seeTheErrorMessageIsDisplayed1"/> + + <!--Fill Registry Form with not secure enough password --> + <fillField stepKey="fillFirstName2ndVariation" userInput="John" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> + <fillField stepKey="fillLastName2ndVariation" userInput="Doe" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> + <fillField stepKey="fillEmail2ndVariation" userInput="johndoe@domain.com" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> + <fillField stepKey="fillPassword2ndVariation" userInput="123123qa" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> + <fillField stepKey="fillConfirmPassword2ndVariation" userInput="123123qa" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> + <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> + <see userInput="Minimum of different classes of characters in password is 3. Classes of characters: Lower Case, Upper Case, Digits, Special Characters." stepKey="seeTheErrorMessageIsDisplayed2"/> + + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml new file mode 100644 index 0000000000000..b70560ad2cd86 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSeeHeaderLinksActionGroup"> + <arguments> + <argument name="LinkName" type="string" defaultValue="Create an Account"/> + </arguments> + + <see stepKey="SeeElement" selector="{{StorefrontHeaderSection.headerlinks}}" userInput="{{LinkName}}"/> + <click stepKey="ClickLink" selector="{{StorefrontHeaderSection.HeaderLinkByText(LinkName)}}"/> + <waitForPageLoad stepKey="Wait"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml new file mode 100644 index 0000000000000..fefde1e6035f3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontHeaderSection"> + <element name="headerlinks" type="text" selector="ul.header.links"/> + <element name="HeaderLinkByText" type="text" selector="//ul[contains(@class, 'header') and contains(@class, 'links')]/li/a[contains(text(),'{{LinkName}}')]" + parameterized="true" /> + </section> +</sections> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml new file mode 100644 index 0000000000000..a414878997021 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewCustomerPasswordComplexityTest"> + <annotations> + <features value="Security"/> + <stories value="Checking customer's password length and password complexity"/> + <title value="Notify the customer if password length or complexity is not match to requirements"/> + <description value="Show notifies to the customer if password length or complexity is not match to requirements"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <!-- TEST BODY --> + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- Click the Registration Link --> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="ClickTheLink"> + <argument name="LinkName" value="Create an Account" /> + </actionGroup> + <!-- Fill Registry Form with Incorrect Password (Two variations) --> + <actionGroup ref="StorefrontFillRegistryFormActionGroup" stepKey="FillRegistryForm"/> + <!--Test Body END--> + + </test> +</tests> From 100a81eb41648eabb41c2d77059368750916758e Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 7 Jun 2019 18:32:45 +0300 Subject: [PATCH 0105/2437] Refactoring --- .../Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml index a414878997021..29135332d6125 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -23,7 +23,7 @@ <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> <!-- Click the Registration Link --> <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="ClickTheLink"> - <argument name="LinkName" value="Create an Account" /> + <argument name="LinkName" value="Create an Account" /> </actionGroup> <!-- Fill Registry Form with Incorrect Password (Two variations) --> <actionGroup ref="StorefrontFillRegistryFormActionGroup" stepKey="FillRegistryForm"/> From bd051c49790daa6756c1d9645d5d58f93811b714 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 7 Jun 2019 14:41:46 -0500 Subject: [PATCH 0106/2437] MC-15973: Incorrect ImportExport file handling --- app/code/Magento/ImportExport/Model/Import.php | 8 +++++++- .../Controller/Adminhtml/Import/ValidateTest.php | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index c8a5921b41b12..d29bf29dd50af 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -568,9 +568,15 @@ public function uploadSource() $entity = $this->getEntity(); /** @var $uploader Uploader */ $uploader = $this->_uploaderFactory->create(['fileId' => self::FIELD_NAME_SOURCE_FILE]); + $uploader->setAllowedExtensions(['csv', 'zip']); $uploader->skipDbProcessing(true); $fileName = $this->random->getRandomString(32) . '.' . $uploader->getFileExtension(); - $result = $uploader->save($this->getWorkingDir(), $fileName); + try { + $result = $uploader->save($this->getWorkingDir(), $fileName); + } catch (\Exception $e) { + throw new LocalizedException(__('The file cannot be uploaded.')); + } + // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged $extension = pathinfo($result['file'], PATHINFO_EXTENSION); diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php index a3cf42b48489f..d9a758b549667 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Controller/Adminhtml/Import/ValidateTest.php @@ -93,7 +93,7 @@ public function validationDataProvider(): array [ 'file_name' => 'test.txt', 'mime-type' => 'text/csv', - 'message' => '\'txt\' file extension is not supported', + 'message' => 'The file cannot be uploaded.', 'delimiter' => ',', ], [ From 57645ff1df01e3ddb467d30750d3c6957b26fc7f Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Mon, 10 Jun 2019 11:15:29 -0500 Subject: [PATCH 0107/2437] MC-17305: Product Form Update. --- .../Catalog/view/frontend/templates/product/view/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml index 8d298aec9f1cb..e201877b47cce 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml @@ -22,7 +22,7 @@ <input type="hidden" name="product" value="<?= (int)$_product->getId() ?>" /> <input type="hidden" name="selected_configurable_option" value="" /> <input type="hidden" name="related_product" id="related-products-field" value="" /> - <input type="hidden" name="item" value="<?= $block->escapeHtmlAttr($block->getRequest()->getParam('id')) ?>" /> + <input type="hidden" name="item" value="<?= $block->escapeJs($block->escapeHtmlAttr($block->getRequest()->getParam('id'))) ?>" /> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_top') ?> <?php if (!$block->hasOptions()) :?> From 58836f072898c63edb8fa4afa9670570aca3beb8 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 11 Jun 2019 10:03:33 -0500 Subject: [PATCH 0108/2437] MC-15972: File uploads --- .../Model/Product/Gallery/Processor.php | 18 +++--- .../Model/ResourceModel/File/Storage/File.php | 39 +++++++++--- .../ResourceModel/File/Storage/FileTest.php | 34 +++++++++- .../Framework/File/Test/Unit/UploaderTest.php | 63 +++++++++++++++++++ .../Magento/Framework/File/Uploader.php | 56 ++++++++++------- 5 files changed, 169 insertions(+), 41 deletions(-) create mode 100644 lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php index e1b788bc3941b..f1d27c38e9456 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/Processor.php @@ -167,10 +167,10 @@ public function addImage( } $fileName = \Magento\MediaStorage\Model\File\Uploader::getCorrectFileName($pathinfo['basename']); - $dispretionPath = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName); - $fileName = $dispretionPath . '/' . $fileName; + $dispersionPath = \Magento\MediaStorage\Model\File\Uploader::getDispersionPath($fileName); + $fileName = $dispersionPath . '/' . $fileName; - $fileName = $this->getNotDuplicatedFilename($fileName, $dispretionPath); + $fileName = $this->getNotDuplicatedFilename($fileName, $dispersionPath); $destinationFile = $this->mediaConfig->getTmpMediaPath($fileName); @@ -465,27 +465,27 @@ protected function getUniqueFileName($file, $forTmp = false) * Get filename which is not duplicated with other files in media temporary and media directories * * @param string $fileName - * @param string $dispretionPath + * @param string $dispersionPath * @return string * @since 101.0.0 */ - protected function getNotDuplicatedFilename($fileName, $dispretionPath) + protected function getNotDuplicatedFilename($fileName, $dispersionPath) { - $fileMediaName = $dispretionPath . '/' + $fileMediaName = $dispersionPath . '/' . \Magento\MediaStorage\Model\File\Uploader::getNewFileName($this->mediaConfig->getMediaPath($fileName)); - $fileTmpMediaName = $dispretionPath . '/' + $fileTmpMediaName = $dispersionPath . '/' . \Magento\MediaStorage\Model\File\Uploader::getNewFileName($this->mediaConfig->getTmpMediaPath($fileName)); if ($fileMediaName != $fileTmpMediaName) { if ($fileMediaName != $fileName) { return $this->getNotDuplicatedFilename( $fileMediaName, - $dispretionPath + $dispersionPath ); } elseif ($fileTmpMediaName != $fileName) { return $this->getNotDuplicatedFilename( $fileTmpMediaName, - $dispretionPath + $dispersionPath ); } } diff --git a/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php b/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php index 8dfce40419b4a..b847b4c13adfc 100644 --- a/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php +++ b/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php @@ -6,12 +6,20 @@ namespace Magento\MediaStorage\Model\ResourceModel\File\Storage; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem\Io\File as FileIo; +use Magento\Framework\App\ObjectManager; /** * Class File */ class File { + /** + * @var FileIo + */ + private $fileIo; + /** * @var \Magento\Framework\Filesystem */ @@ -25,11 +33,16 @@ class File /** * @param \Magento\Framework\Filesystem $filesystem * @param \Psr\Log\LoggerInterface $log + * @param FileIo $fileIo */ - public function __construct(\Magento\Framework\Filesystem $filesystem, \Psr\Log\LoggerInterface $log) - { + public function __construct( + \Magento\Framework\Filesystem $filesystem, + \Psr\Log\LoggerInterface $log, + FileIo $fileIo = null + ) { $this->_logger = $log; $this->_filesystem = $filesystem; + $this->fileIo = $fileIo ?? ObjectManager::getInstance()->get(FileIo::class); } /** @@ -45,14 +58,15 @@ public function getStorageData($dir = '/') $directoryInstance = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA); if ($directoryInstance->isDirectory($dir)) { foreach ($directoryInstance->readRecursively($dir) as $path) { - $itemName = basename($path); + $pathInfo = $this->fileIo->getPathInfo($path); + $itemName = $pathInfo['basename']; if ($itemName == '.svn' || $itemName == '.htaccess') { continue; } if ($directoryInstance->isDirectory($path)) { $directories[] = [ 'name' => $itemName, - 'path' => dirname($path) == '.' ? '/' : dirname($path), + 'path' => $pathInfo['dirname'] === '.' ? '/' : $pathInfo['dirname'], ]; } else { $files[] = $path; @@ -64,7 +78,7 @@ public function getStorageData($dir = '/') } /** - * Clear files and directories in storage + * Clear all files in storage $dir * * @param string $dir * @return $this @@ -73,8 +87,17 @@ public function clear($dir = '') { $directoryInstance = $this->_filesystem->getDirectoryWrite(DirectoryList::MEDIA); if ($directoryInstance->isDirectory($dir)) { - foreach ($directoryInstance->read($dir) as $path) { - $directoryInstance->delete($path); + $paths = $directoryInstance->readRecursively($dir); + foreach ($paths as $path) { + if ($directoryInstance->isDirectory()) { + continue; + } + + $pathInfo = $this->fileIo->getPathInfo($path); + + if ($pathInfo['basename'] !== '.htaccess') { + $directoryInstance->delete($path); + } } } @@ -127,7 +150,7 @@ public function saveFile($filePath, $content, $overwrite = false) } } catch (\Magento\Framework\Exception\FileSystemException $e) { $this->_logger->info($e->getMessage()); - throw new \Magento\Framework\Exception\LocalizedException(__('Unable to save file: %1', $filePath)); + throw new LocalizedException(__('Unable to save file: %1', $filePath)); } return false; diff --git a/app/code/Magento/MediaStorage/Test/Unit/Model/ResourceModel/File/Storage/FileTest.php b/app/code/Magento/MediaStorage/Test/Unit/Model/ResourceModel/File/Storage/FileTest.php index 97dffbe0e39a9..adc045cd0bed5 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/Model/ResourceModel/File/Storage/FileTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/Model/ResourceModel/File/Storage/FileTest.php @@ -6,12 +6,18 @@ namespace Magento\MediaStorage\Test\Unit\Model\ResourceModel\File\Storage; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class FileTest */ class FileTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Framework\Filesystem\Io\File + */ + private $fileIoMock; + /** * @var \Magento\MediaStorage\Model\ResourceModel\File\Storage\File */ @@ -44,9 +50,17 @@ protected function setUp() ['isDirectory', 'readRecursively'] ); - $this->storageFile = new \Magento\MediaStorage\Model\ResourceModel\File\Storage\File( - $this->filesystemMock, - $this->loggerMock + $this->fileIoMock = $this->createPartialMock(\Magento\Framework\Filesystem\Io\File::class, ['getPathInfo']); + + $objectManager = new ObjectManager($this); + + $this->storageFile = $objectManager->getObject( + \Magento\MediaStorage\Model\ResourceModel\File\Storage\File::class, + [ + 'filesystem' => $this->filesystemMock, + 'log' => $this->loggerMock, + 'fileIo' => $this->fileIoMock + ] ); } @@ -98,6 +112,20 @@ public function testGetStorageData() 'folder_one/folder_two/.htaccess', 'folder_one/folder_two/file_two.txt', ]; + + $pathInfos = array_map( + function ($path) { + return [$path, pathinfo($path)]; + }, + $paths + ); + + $this->fileIoMock->expects( + $this->any() + )->method( + 'getPathInfo' + )->will($this->returnValueMap($pathInfos)); + sort($paths); $this->directoryReadMock->expects( $this->once() diff --git a/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php b/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php new file mode 100644 index 0000000000000..ac19f4dc36ed9 --- /dev/null +++ b/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\File\Test\Unit; + +/** + * Unit Test class for \Magento\Framework\File\Uploader + */ +class UploaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @param string $fileName + * @param string|bool $expectedCorrectedFileName + * + * @dataProvider getCorrectFileNameProvider + */ + public function testGetCorrectFileName($fileName, $expectedCorrectedFileName) + { + $isExceptionExpected = $expectedCorrectedFileName === true; + + if ($isExceptionExpected) { + $this->expectException(\InvalidArgumentException::class); + } + + $this->assertEquals( + $expectedCorrectedFileName, + \Magento\Framework\File\Uploader::getCorrectFileName($fileName) + ); + } + + /** + * @return array + */ + public function getCorrectFileNameProvider() + { + return [ + [ + '^&*&^&*^$$$$()', + 'file.' + ], + [ + '^&*&^&*^$$$$().png', + 'file.png' + ], + [ + '_', + 'file.' + ], + [ + '_.jpg', + 'file.jpg' + ], + [ + 'a.' . str_repeat('b', 100), + true + ] + ]; + } +} diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index 328a748cfd5df..5396862195f88 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\File; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Validation\ValidationException; + /** * File upload class * @@ -75,7 +78,7 @@ class Uploader protected $_allowRenameFiles = false; /** - * If this variable is set to TRUE, files dispertion will be supported. + * If this variable is set to TRUE, files dispersion will be supported. * * @var bool * @access protected @@ -162,7 +165,7 @@ class Uploader * * @param string|array $fileId * @param \Magento\Framework\File\Mime|null $fileMime - * @throws \Exception + * @throws \DomainException */ public function __construct( $fileId, @@ -171,7 +174,7 @@ public function __construct( $this->_setUploadFileId($fileId); if (!file_exists($this->_file['tmp_name'])) { $code = empty($this->_file['tmp_name']) ? self::TMP_NAME_EMPTY : 0; - throw new \Exception('The file was not uploaded.', $code); + throw new \DomainException('The file was not uploaded.', $code); } else { $this->_fileExists = true; } @@ -256,7 +259,7 @@ public function save($destinationFolder, $newFileName = null) * * @param string $destinationFolder * @return void - * @throws \Exception + * @throws FileSystemException */ private function validateDestination($destinationFolder) { @@ -265,7 +268,7 @@ private function validateDestination($destinationFolder) } if (!is_writable($destinationFolder)) { - throw new \Exception('Destination folder is not writable or does not exists.'); + throw new FileSystemException(__('Destination folder is not writable or does not exists.')); } } @@ -302,7 +305,7 @@ protected function _moveFile($tmpPath, $destPath) * Validate file before save * * @return void - * @throws \Exception + * @throws ValidationException */ protected function _validateFile() { @@ -312,7 +315,7 @@ protected function _validateFile() //is file extension allowed if (!$this->checkAllowedExtension($this->getFileExtension())) { - throw new \Exception('Disallowed file type.'); + throw new ValidationException(__('Disallowed file type.')); } //run validate callbacks foreach ($this->_validateCallbacks as $params) { @@ -366,19 +369,27 @@ public function removeValidateCallback($callbackName) } /** - * Correct filename with special chars and spaces + * Correct filename with special chars and spaces; also trim excessively long filenames * * @param string $fileName * @return string + * @throws \InvalidArgumentException */ public static function getCorrectFileName($fileName) { $fileName = preg_replace('/[^a-z0-9_\\-\\.]+/i', '_', $fileName); $fileInfo = pathinfo($fileName); + $fileInfo['extension'] = $fileInfo['extension'] ?? ''; + + // account for excessively long filenames that cannot be stored completely in database + if (strlen($fileInfo['basename']) > 100) { + throw new \InvalidArgumentException('Filename is too long; must be 100 characters or less'); + } if (preg_match('/^_+$/', $fileInfo['filename'])) { $fileName = 'file.' . $fileInfo['extension']; } + return $fileName; } @@ -533,7 +544,8 @@ private function _getMimeType() * * @param string|array $fileId * @return void - * @throws \Exception + * @throws \DomainException + * @throws \InvalidArgumentException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function _setUploadFileId($fileId) @@ -543,7 +555,7 @@ private function _setUploadFileId($fileId) $this->_file = $fileId; } else { if (empty($_FILES)) { - throw new \Exception('$_FILES array is empty'); + throw new \DomainException('$_FILES array is empty'); } preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file); @@ -565,7 +577,9 @@ private function _setUploadFileId($fileId) $this->_uploadType = self::SINGLE_STYLE; $this->_file = $_FILES[$fileId]; } elseif ($fileId == '') { - throw new \Exception('Invalid parameter given. A valid $_FILES[] identifier is expected.'); + throw new \InvalidArgumentException( + 'Invalid parameter given. A valid $_FILES[] identifier is expected.' + ); } } } @@ -575,7 +589,7 @@ private function _setUploadFileId($fileId) * * @param string $destinationFolder * @return \Magento\Framework\File\Uploader - * @throws \Exception + * @throws FileSystemException */ private function _createDestinationFolder($destinationFolder) { @@ -590,7 +604,7 @@ private function _createDestinationFolder($destinationFolder) if (!(@is_dir($destinationFolder) || @mkdir($destinationFolder, 0777, true) )) { - throw new \Exception("Unable to create directory '{$destinationFolder}'."); + throw new FileSystemException(__('Unable to create directory %1.', $destinationFolder)); } return $this; } @@ -620,7 +634,7 @@ public static function getNewFileName($destinationFile) } /** - * Get dispertion path + * Get dispersion path * * @param string $fileName * @return string @@ -632,7 +646,7 @@ public static function getDispretionPath($fileName) } /** - * Get dispertion path + * Get dispersion path * * @param string $fileName * @return string @@ -640,17 +654,17 @@ public static function getDispretionPath($fileName) public static function getDispersionPath($fileName) { $char = 0; - $dispertionPath = ''; + $dispersionPath = ''; while ($char < 2 && $char < strlen($fileName)) { - if (empty($dispertionPath)) { - $dispertionPath = '/' . ('.' == $fileName[$char] ? '_' : $fileName[$char]); + if (empty($dispersionPath)) { + $dispersionPath = '/' . ('.' == $fileName[$char] ? '_' : $fileName[$char]); } else { - $dispertionPath = self::_addDirSeparator( - $dispertionPath + $dispersionPath = self::_addDirSeparator( + $dispersionPath ) . ('.' == $fileName[$char] ? '_' : $fileName[$char]); } $char++; } - return $dispertionPath; + return $dispersionPath; } } From 1322bd3c5c2be9f2ed06b7fbeebd167735cf6016 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Tue, 11 Jun 2019 11:16:54 -0500 Subject: [PATCH 0109/2437] MC-16963: Admin panel Design Configuration changes logs are missing --- .../Magento/Theme/Model/Design/Config/ValueChecker.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php b/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php index 00f30e9d7976c..11d45616e387d 100644 --- a/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php +++ b/app/code/Magento/Theme/Model/Design/Config/ValueChecker.php @@ -83,13 +83,11 @@ public function isDifferentFromDefault($value, $scope, $scopeId, array $fieldCon */ protected function isEqual($value, $defaultValue) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction - switch (gettype($value)) { - case 'array': - return $this->isEqualArrays($value, $defaultValue); - default: - return $value === $defaultValue; + if (is_array($value)) { + return $this->isEqualArrays($value, $defaultValue); } + + return $value === $defaultValue; } /** From dc8786a27f9f2a789f926ce0757c663f6633bf53 Mon Sep 17 00:00:00 2001 From: Sachin Admane <sadmane@magento.com> Date: Tue, 11 Jun 2019 11:22:48 -0500 Subject: [PATCH 0110/2437] MC-17305: Product Form Update. Add int typecast. --- .../Catalog/view/frontend/templates/product/view/form.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml index e201877b47cce..7d59e9831e947 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml @@ -22,7 +22,7 @@ <input type="hidden" name="product" value="<?= (int)$_product->getId() ?>" /> <input type="hidden" name="selected_configurable_option" value="" /> <input type="hidden" name="related_product" id="related-products-field" value="" /> - <input type="hidden" name="item" value="<?= $block->escapeJs($block->escapeHtmlAttr($block->getRequest()->getParam('id'))) ?>" /> + <input type="hidden" name="item" value="<?= (int)$block->getRequest()->getParam('id') ?>" /> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_top') ?> <?php if (!$block->hasOptions()) :?> From 0a0f3943777aa64c48a76ac37a64aa1657c3e88b Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Tue, 11 Jun 2019 13:20:07 -0500 Subject: [PATCH 0111/2437] MC-17065: Email error message - Resolved error message issue for inline editing customer --- .../Customer/Controller/Adminhtml/Index/InlineEdit.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 7220de0356817..599415bc7bb7c 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -70,8 +70,14 @@ class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionIn */ private $addressRegistry; + /** + * @var \Magento\Framework\Escaper + */ + private $escaper; + /** * @param Action\Context $context + * @param \Magento\Framework\Escaper $escaper * @param CustomerRepositoryInterface $customerRepository * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory * @param \Magento\Customer\Model\Customer\Mapper $customerMapper @@ -86,6 +92,7 @@ public function __construct( \Magento\Customer\Model\Customer\Mapper $customerMapper, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Escaper $escaper, AddressRegistry $addressRegistry = null ) { $this->customerRepository = $customerRepository; @@ -93,6 +100,7 @@ public function __construct( $this->customerMapper = $customerMapper; $this->dataObjectHelper = $dataObjectHelper; $this->logger = $logger; + $this->escaper = $escaper; $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); parent::__construct($context); } @@ -315,7 +323,7 @@ protected function getCustomer() */ protected function getErrorWithCustomerId($errorText) { - return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . __($errorText); + return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . $this->escaper->escapeHtml(__($errorText)); } /** From 61c3ea0eda1e2b7cc4aa80a7f844c399b3a705cf Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Tue, 11 Jun 2019 15:40:42 -0500 Subject: [PATCH 0112/2437] MC-17065: Email error message - Resolved unit test failures --- .../Unit/Controller/Adminhtml/Index/InlineEditTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 45e64f6557d51..7d2db1cc16b01 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Model\EmailNotificationInterface; use Magento\Framework\DataObject; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Escaper; /** * Unit tests for Inline customer edit @@ -78,6 +79,9 @@ class InlineEditTest extends \PHPUnit\Framework\TestCase /** @var array */ private $items; + /** @var \Magento\Framework\Escaper */ + private $escaper; + /** * Sets up mocks * @@ -86,7 +90,7 @@ class InlineEditTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - + $this->escaper = new Escaper(); $this->request = $this->getMockForAbstractClass( \Magento\Framework\App\RequestInterface::class, [], @@ -172,7 +176,8 @@ protected function setUp() 'addressDataFactory' => $this->addressDataFactory, 'addressRepository' => $this->addressRepository, 'logger' => $this->logger, - 'addressRegistry' => $this->addressRegistry + 'escaper' => $this->escaper, + 'addressRegistry' => $this->addressRegistry, ] ); $reflection = new \ReflectionClass(get_class($this->controller)); @@ -365,6 +370,7 @@ public function testExecuteLocalizedException() ->method('save') ->with($this->customerData) ->willThrowException($exception); + $this->messageManager->expects($this->once()) ->method('addError') ->with('[Customer ID: 12] Exception message'); From 75180b57877fe3efdbce95722444c8c16a59f317 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Tue, 11 Jun 2019 15:42:38 -0500 Subject: [PATCH 0113/2437] MC-17065: Email error message - Updated implementation --- .../Customer/Controller/Adminhtml/Index/InlineEdit.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 599415bc7bb7c..7405269f8451d 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -242,10 +242,10 @@ protected function saveCustomer(CustomerInterface $customer) $this->disableAddressValidation($customer); $this->customerRepository->save($customer); } catch (\Magento\Framework\Exception\InputException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); $this->logger->critical($e); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); $this->logger->critical($e); } catch (\Exception $e) { $this->getMessageManager()->addError($this->getErrorWithCustomerId('We can\'t save the customer.')); @@ -323,7 +323,7 @@ protected function getCustomer() */ protected function getErrorWithCustomerId($errorText) { - return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . $this->escaper->escapeHtml(__($errorText)); + return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . __($errorText); } /** From e3c99770eab38dc7da5041760b4156871ef4f92b Mon Sep 17 00:00:00 2001 From: Mark Berube <berube@adobe.com> Date: Wed, 12 Jun 2019 00:29:44 -0500 Subject: [PATCH 0114/2437] MC-15974: Fixing payment title. --- .../view/frontend/web/js/action/select-payment-method.js | 4 ++++ .../Ui/Component/Listing/Column/Method/Options.php | 9 +++++++++ .../Magento/Ui/view/base/web/js/grid/filters/filters.js | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js index 702df47526715..6bda10fceb7b9 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js @@ -12,6 +12,10 @@ define([ 'use strict'; return function (paymentMethod) { + paymentMethod.__disableTmpl = { + title: true + }; + quote.paymentMethod(paymentMethod); }; }); diff --git a/app/code/Magento/Payment/Ui/Component/Listing/Column/Method/Options.php b/app/code/Magento/Payment/Ui/Component/Listing/Column/Method/Options.php index fbf80de519f9f..71e0384c72f79 100644 --- a/app/code/Magento/Payment/Ui/Component/Listing/Column/Method/Options.php +++ b/app/code/Magento/Payment/Ui/Component/Listing/Column/Method/Options.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Payment\Ui\Component\Listing\Column\Method; /** @@ -41,6 +42,14 @@ public function toOptionArray() if ($this->options === null) { $this->options = $this->paymentHelper->getPaymentMethodList(true, true); } + + array_walk( + $this->options, + function (&$item) { + $item['__disableTmpl'] = true; + } + ); + return $this->options; } } diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index c608400a6f174..78016ee489a11 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -274,7 +274,8 @@ define([ filter = utils.extend({}, filters.base, filter); //Accepting labels as is. filter.__disableTmpl = { - label: 1 + label: 1, + options: 1 }; filter = utils.template(filter, { From 3c9064feec8d18cc17503a0f1f3726afb8ac45b9 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 12 Jun 2019 10:16:33 -0500 Subject: [PATCH 0115/2437] MC-15972: File uploads --- .../MediaStorage/Model/ResourceModel/File/Storage/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php b/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php index b847b4c13adfc..8c9fe7b848fad 100644 --- a/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php +++ b/app/code/Magento/MediaStorage/Model/ResourceModel/File/Storage/File.php @@ -89,7 +89,7 @@ public function clear($dir = '') if ($directoryInstance->isDirectory($dir)) { $paths = $directoryInstance->readRecursively($dir); foreach ($paths as $path) { - if ($directoryInstance->isDirectory()) { + if ($directoryInstance->isDirectory($path)) { continue; } From fcc01ed7486aba33bc6380d3a3664cc30504682d Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 12 Jun 2019 10:44:43 -0500 Subject: [PATCH 0116/2437] MC-17065: Email error message - Resolved static test failures --- .../Controller/Adminhtml/Index/InlineEdit.php | 33 ++++++++++++------- .../Adminhtml/Index/InlineEditTest.php | 24 +++++++++----- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 7405269f8451d..77c9cbad07a1c 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -77,12 +77,12 @@ class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionIn /** * @param Action\Context $context - * @param \Magento\Framework\Escaper $escaper * @param CustomerRepositoryInterface $customerRepository * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory * @param \Magento\Customer\Model\Customer\Mapper $customerMapper * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Escaper $escaper * @param AddressRegistry|null $addressRegistry */ public function __construct( @@ -136,10 +136,14 @@ public function execute() $postItems = $this->getRequest()->getParam('items', []); if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { - return $resultJson->setData([ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ]); + return $resultJson->setData( + [ + 'messages' => [ + __('Please correct the data sent.') + ], + 'error' => true, + ] + ); } foreach (array_keys($postItems) as $customerId) { @@ -155,10 +159,12 @@ public function execute() $this->getEmailNotification()->credentialsChanged($this->getCustomer(), $currentCustomer->getEmail()); } - return $resultJson->setData([ - 'messages' => $this->getErrorMessages(), - 'error' => $this->isErrorExists() - ]); + return $resultJson->setData( + [ + 'messages' => $this->getErrorMessages(), + 'error' => $this->isErrorExists() + ] + ); } /** @@ -242,13 +248,16 @@ protected function saveCustomer(CustomerInterface $customer) $this->disableAddressValidation($customer); $this->customerRepository->save($customer); } catch (\Magento\Framework\Exception\InputException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); + $this->getMessageManager() + ->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); $this->logger->critical($e); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); + $this->getMessageManager() + ->addError($this->getErrorWithCustomerId($this->escaper->escapeHtml($e->getMessage()))); $this->logger->critical($e); } catch (\Exception $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId('We can\'t save the customer.')); + $this->getMessageManager() + ->addError($this->getErrorWithCustomerId('We can\'t save the customer.')); $this->logger->critical($e); } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 7d2db1cc16b01..7fca556fc2cda 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -296,10 +296,14 @@ protected function prepareMocksForErrorMessagesProcessing() ->willReturn('Error text'); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => ['Error text'], - 'error' => true, - ]) + ->with( + [ + 'messages' => [ + 'Error text', + ], + 'error' => true, + ] + ) ->willReturnSelf(); } @@ -345,10 +349,14 @@ public function testExecuteWithoutItems() $this->resultJson ->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ]) + ->with( + [ + 'messages' => [ + __('Please correct the data sent.'), + ], + 'error' => true, + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); } From bfaab3775c93767d8b58dca6c27481d89c0822a8 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 12 Jun 2019 11:17:27 -0500 Subject: [PATCH 0117/2437] MC-17447: Update wishlist controller - Updated wishlist controller - Updated integration test for wishlist controller --- app/code/Magento/Wishlist/Controller/Index/Add.php | 5 ++++- .../Magento/Wishlist/Controller/Index/PluginTest.php | 1 + .../testsuite/Magento/Wishlist/Controller/IndexTest.php | 9 ++++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Wishlist/Controller/Index/Add.php b/app/code/Magento/Wishlist/Controller/Index/Add.php index 5cb60905aea48..ad7ff670a9c5b 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Add.php +++ b/app/code/Magento/Wishlist/Controller/Index/Add.php @@ -7,15 +7,18 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\App\Action; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\NotFoundException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Controller\ResultFactory; /** + * Wish list Add controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Add extends \Magento\Wishlist\Controller\AbstractIndex +class Add extends \Magento\Wishlist\Controller\AbstractIndex implements HttpPostActionInterface { /** * @var \Magento\Wishlist\Controller\WishlistProviderInterface diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php index 4c8937e1c6e9a..5303c9f352b87 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/Index/PluginTest.php @@ -50,6 +50,7 @@ protected function tearDown() */ public function testAddActionProductWithInvalidCredentials(): void { + $this->getRequest()->setMethod('POST'); $this->getRequest()->setPostValue( [ 'login' => [ diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php index e020d31838f06..f43133c92fc3d 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php @@ -98,9 +98,12 @@ public function testAddActionProductNameXss() { /** @var \Magento\Framework\Data\Form\FormKey $formKey */ $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class); - $this->getRequest()->setPostValue([ - 'form_key' => $formKey->getFormKey(), - ]); + $this->getRequest()->setMethod('POST'); + $this->getRequest()->setPostValue( + [ + 'form_key' => $formKey->getFormKey(), + ] + ); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() From f290df20014d522fcc395cc78e707743cb944d6a Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 12 Jun 2019 12:43:10 -0500 Subject: [PATCH 0118/2437] MC-17447: Update wishlist controller - Resolved static test failure --- app/code/Magento/Wishlist/Controller/Index/Add.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Wishlist/Controller/Index/Add.php b/app/code/Magento/Wishlist/Controller/Index/Add.php index ad7ff670a9c5b..3ed152cb84125 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Add.php +++ b/app/code/Magento/Wishlist/Controller/Index/Add.php @@ -141,6 +141,7 @@ public function execute() 'referer' => $referer ] ); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addErrorMessage( __('We can\'t add the item to Wish List right now: %1.', $e->getMessage()) From b573a7a447287403cab785c6656345f95302a8f0 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 12 Jun 2019 14:21:34 -0500 Subject: [PATCH 0119/2437] MC-17065: Email error message - Resolved backward incompatibility issue --- .../Customer/Controller/Adminhtml/Index/InlineEdit.php | 8 ++++---- .../Unit/Controller/Adminhtml/Index/InlineEditTest.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 77c9cbad07a1c..eff812a65a3bb 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -82,8 +82,8 @@ class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionIn * @param \Magento\Customer\Model\Customer\Mapper $customerMapper * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Escaper $escaper * @param AddressRegistry|null $addressRegistry + * @param \Magento\Framework\Escaper $escaper */ public function __construct( Action\Context $context, @@ -92,16 +92,16 @@ public function __construct( \Magento\Customer\Model\Customer\Mapper $customerMapper, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Escaper $escaper, - AddressRegistry $addressRegistry = null + AddressRegistry $addressRegistry = null, + \Magento\Framework\Escaper $escaper = null ) { $this->customerRepository = $customerRepository; $this->resultJsonFactory = $resultJsonFactory; $this->customerMapper = $customerMapper; $this->dataObjectHelper = $dataObjectHelper; $this->logger = $logger; - $this->escaper = $escaper; $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); + $this->escaper = $escaper ?: ObjectManager::getInstance()->get(\Magento\Framework\Escaper::class); parent::__construct($context); } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 7fca556fc2cda..8267624f7b006 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -176,8 +176,8 @@ protected function setUp() 'addressDataFactory' => $this->addressDataFactory, 'addressRepository' => $this->addressRepository, 'logger' => $this->logger, - 'escaper' => $this->escaper, 'addressRegistry' => $this->addressRegistry, + 'escaper' => $this->escaper, ] ); $reflection = new \ReflectionClass(get_class($this->controller)); From 592d49b5de426c89a819818fedf0b7f6f5f1e13f Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 12 Jun 2019 16:26:16 -0500 Subject: [PATCH 0120/2437] MC-16284: Visible message alert --- .../view/base/web/js/form/element/file-uploader.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index f28569caa0053..f4b267758cf1d 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -427,7 +427,8 @@ define([ _.each(this.aggregatedErrors, function (error) { notification().add({ error: true, - message: '%s' + error.message, // %s to be used as placeholder for html injection + // %s to be used as placeholders for html injection + message: '%s' + error.filename + '%s ' + $t('was not uploaded.') + '%s' + error.message, /** * Adds constructed error notification to aggregatedErrorMessages @@ -435,12 +436,11 @@ define([ * @param {String} constructedMessage */ insertMethod: function (constructedMessage) { - var errorMsgBodyHtml = '<strong>%s</strong> %s.<br>' - .replace('%s', error.filename) - .replace('%s', $t('was not uploaded')); - // html is escaped in message body for notification widget; prepend unescaped html here - constructedMessage = constructedMessage.replace('%s', errorMsgBodyHtml); + constructedMessage = constructedMessage + .replace('%s', '<strong>') + .replace('%s', '</strong>') + .replace('%s', '<br>'); aggregatedErrorMessages.push(constructedMessage); } From 61b0be7ba66cfff980022246039767782f033b19 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 13 Jun 2019 15:07:31 -0500 Subject: [PATCH 0121/2437] MC-15972: File uploads --- .../Magento/Framework/File/Test/Unit/UploaderTest.php | 6 +++++- lib/internal/Magento/Framework/File/Uploader.php | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php b/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php index ac19f4dc36ed9..d0aa658184457 100644 --- a/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php +++ b/lib/internal/Magento/Framework/File/Test/Unit/UploaderTest.php @@ -55,7 +55,11 @@ public function getCorrectFileNameProvider() 'file.jpg' ], [ - 'a.' . str_repeat('b', 100), + 'a.' . str_repeat('b', 88), + 'a.' . str_repeat('b', 88) + ], + [ + 'a.' . str_repeat('b', 89), true ] ]; diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index 5396862195f88..af19c619ae68f 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -382,8 +382,8 @@ public static function getCorrectFileName($fileName) $fileInfo['extension'] = $fileInfo['extension'] ?? ''; // account for excessively long filenames that cannot be stored completely in database - if (strlen($fileInfo['basename']) > 100) { - throw new \InvalidArgumentException('Filename is too long; must be 100 characters or less'); + if (strlen($fileInfo['basename']) > 90) { + throw new \InvalidArgumentException('Filename is too long; must be 90 characters or less'); } if (preg_match('/^_+$/', $fileInfo['filename'])) { From 6786439ab125a564c2a8ba50b0f8387c26f7c486 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 14 Jun 2019 09:45:11 +0300 Subject: [PATCH 0122/2437] MC-16042: Zip archive validation --- lib/internal/Magento/Framework/Archive/Zip.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/internal/Magento/Framework/Archive/Zip.php b/lib/internal/Magento/Framework/Archive/Zip.php index c41f8b28ce348..925adda48504c 100644 --- a/lib/internal/Magento/Framework/Archive/Zip.php +++ b/lib/internal/Magento/Framework/Archive/Zip.php @@ -54,6 +54,7 @@ public function unpack($source, $destination) $zip = new \ZipArchive(); if ($zip->open($source) === true) { $filename = $this->filterRelativePaths($zip->getNameIndex(0) ?: ''); + $filename = $this->filterExcludedFiles($filename); if ($filename) { $zip->extractTo(dirname($destination), $filename); rename(dirname($destination).'/'.$filename, $destination); @@ -82,4 +83,19 @@ private function filterRelativePaths(string $path): string return $path; } + + /** + * Filter excluded files. + * + * @param string $file + * @return string + */ + private function filterExcludedFiles(string $file): string + { + if ($file && preg_match('/^\.htaccess$/', $file)) { + $file = ''; + } + + return $file; + } } From 102cc0fa543b0fcba9faa1fc4c610bd28ad53775 Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Thu, 13 Jun 2019 14:47:01 +0300 Subject: [PATCH 0123/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Implemented validation URL key during category creation --- .../CategoryUrlPathAutogeneratorObserver.php | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 95ddceed6ec8f..346804db8a18b 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -48,22 +48,31 @@ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface */ private $categoryRepository; + /** + * @var \Magento\Backend\App\Area\FrontNameResolver + */ + private $frontNameResolver; + /** * @param CategoryUrlPathGenerator $categoryUrlPathGenerator * @param ChildrenCategoriesProvider $childrenCategoriesProvider * @param \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService * @param CategoryRepositoryInterface $categoryRepository + * @param \Magento\Backend\App\Area\FrontNameResolver $frontNameResolver */ public function __construct( CategoryUrlPathGenerator $categoryUrlPathGenerator, ChildrenCategoriesProvider $childrenCategoriesProvider, StoreViewService $storeViewService, - CategoryRepositoryInterface $categoryRepository + CategoryRepositoryInterface $categoryRepository, + \Magento\Backend\App\Area\FrontNameResolver $frontNameResolver = null ) { $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; $this->childrenCategoriesProvider = $childrenCategoriesProvider; $this->storeViewService = $storeViewService; $this->categoryRepository = $categoryRepository; + $this->frontNameResolver = $frontNameResolver ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Backend\App\Area\FrontNameResolver::class); } /** @@ -102,12 +111,12 @@ private function updateUrlKey($category, $urlKey) throw new \Magento\Framework\Exception\LocalizedException(__('Invalid URL key')); } - if (in_array($urlKey, $this->invalidValues)) { + if (in_array($urlKey, $this->getInvalidValues())) { throw new \Magento\Framework\Exception\LocalizedException( __( 'URL key "%1" conflicts with reserved endpoint names: %2. Try another url key.', $urlKey, - implode(', ', $this->invalidValues) + implode(', ', $this->getInvalidValues()) ) ); } @@ -122,6 +131,16 @@ private function updateUrlKey($category, $urlKey) } } + /** + * Get reserved endpoint names. + * + * @return array + */ + private function getInvalidValues() + { + return array_unique(array_merge($this->invalidValues, [$this->frontNameResolver->getFrontName()])); + } + /** * Update url path for children category. * From 6c7c45103cc2aeb4f6c51cb5b266c85cb024d80c Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 14 Jun 2019 15:56:52 +0300 Subject: [PATCH 0124/2437] MC-17303: Customer login fix --- app/code/Magento/Customer/Controller/Account/LoginPost.php | 2 +- app/code/Magento/Customer/Controller/Ajax/Login.php | 2 +- app/code/Magento/Customer/Model/Plugin/CustomerNotification.php | 2 +- app/code/Magento/Customer/Model/Session.php | 2 +- app/code/Magento/Rss/Controller/Feed.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 04051fbbf366b..9afaab4121644 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -182,8 +182,8 @@ public function execute() if (!empty($login['username']) && !empty($login['password'])) { try { $customer = $this->customerAccountManagement->authenticate($login['username'], $login['password']); - $this->session->setCustomerDataAsLoggedIn($customer); $this->session->regenerateId(); + $this->session->setCustomerDataAsLoggedIn($customer); if ($this->getCookieManager()->getCookie('mage-cache-sessid')) { $metadata = $this->getCookieMetadataFactory()->createCookieMetadata(); $metadata->setPath('/'); diff --git a/app/code/Magento/Customer/Controller/Ajax/Login.php b/app/code/Magento/Customer/Controller/Ajax/Login.php index 5049c83e60f35..d5425bfc27332 100644 --- a/app/code/Magento/Customer/Controller/Ajax/Login.php +++ b/app/code/Magento/Customer/Controller/Ajax/Login.php @@ -190,8 +190,8 @@ public function execute() $credentials['username'], $credentials['password'] ); - $this->customerSession->setCustomerDataAsLoggedIn($customer); $this->customerSession->regenerateId(); + $this->customerSession->setCustomerDataAsLoggedIn($customer); $redirectRoute = $this->getAccountRedirect()->getRedirectCookie(); if ($this->cookieManager->getCookie('mage-cache-sessid')) { $metadata = $this->cookieMetadataFactory->createCookieMetadata(); diff --git a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php index 517aef5690ee6..9b655fc843529 100644 --- a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php +++ b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php @@ -82,10 +82,10 @@ public function beforeDispatch(AbstractAction $subject, RequestInterface $reques ) ) { try { + $this->session->regenerateId(); $customer = $this->customerRepository->getById($customerId); $this->session->setCustomerData($customer); $this->session->setCustomerGroupId($customer->getGroupId()); - $this->session->regenerateId(); $this->notificationStorage->remove(NotificationStorage::UPDATE_CUSTOMER_SESSION, $customer->getId()); } catch (NoSuchEntityException $e) { $this->logger->error($e); diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index 5900fed218edf..c37a28d04fc52 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -412,10 +412,10 @@ public function checkCustomerId($customerId) */ public function setCustomerAsLoggedIn($customer) { + $this->regenerateId(); $this->setCustomer($customer); $this->_eventManager->dispatch('customer_login', ['customer' => $customer]); $this->_eventManager->dispatch('customer_data_object_login', ['customer' => $this->getCustomerDataObject()]); - $this->regenerateId(); return $this; } diff --git a/app/code/Magento/Rss/Controller/Feed.php b/app/code/Magento/Rss/Controller/Feed.php index 8fbe7addb560d..634540dde5d96 100644 --- a/app/code/Magento/Rss/Controller/Feed.php +++ b/app/code/Magento/Rss/Controller/Feed.php @@ -84,8 +84,8 @@ protected function auth() list($login, $password) = $this->httpAuthentication->getCredentials(); try { $customer = $this->customerAccountManagement->authenticate($login, $password); - $this->customerSession->setCustomerDataAsLoggedIn($customer); $this->customerSession->regenerateId(); + $this->customerSession->setCustomerDataAsLoggedIn($customer); } catch (\Exception $e) { $this->logger->critical($e); } From 5e4f83b39c41d32416886a7a794a8c2315a5810c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 7 Jun 2019 13:18:57 -0500 Subject: [PATCH 0125/2437] MC-16724: Prevent errors from incorrect import data --- .../Model/Import/Product.php | 3 + .../Import/Product/Validator/LayoutUpdate.php | 85 +++++++++++++++ .../Validator/LayoutUpdatePermissions.php | 81 ++++++++++++++ .../Validator/LayoutUpdatePermissionsTest.php | 101 ++++++++++++++++++ .../Product/Validator/LayoutUpdateTest.php | 97 +++++++++++++++++ .../Magento/CatalogImportExport/composer.json | 3 +- .../Magento/CatalogImportExport/etc/di.xml | 7 ++ .../LayoutUpdate/PermissionsValidatorTest.php | 101 ++++++++++++++++++ app/etc/di.xml | 10 ++ .../Model/Import/ProductTest.php | 21 ++++ .../Unit/ValidationState/ConfigurableTest.php | 30 ++++++ .../Config/ValidationState/Configurable.php | 38 +++++++ 12 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdate.php create mode 100644 app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php create mode 100644 app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php create mode 100644 app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdateTest.php create mode 100644 app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php create mode 100644 lib/internal/Magento/Framework/Config/Test/Unit/ValidationState/ConfigurableTest.php create mode 100644 lib/internal/Magento/Framework/Config/ValidationState/Configurable.php diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 0b7fbaf86826b..036bd5de0fb27 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -301,6 +301,8 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually', ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => 'Value for multiselect attribute %s contains duplicated values', 'invalidNewToDateValue' => 'Make sure new_to_date is later than or the same as new_from_date', + 'invalidLayoutUpdate' => 'Invalid layout update', + 'insufficientPermissions' => 'You do not have permissions to update "%s"', ]; //@codingStandardsIgnoreEnd @@ -1508,6 +1510,7 @@ public function getImagesFromRow(array $rowData) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @throws LocalizedException + * phpcs:disable Generic.Metrics.NestingLevel.TooHigh */ protected function _saveProducts() { diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdate.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdate.php new file mode 100644 index 0000000000000..99919628518c6 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdate.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Import\Product\Validator; + +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; + +/** + * Validates layout and custom layout update fields + */ +class LayoutUpdate extends AbstractImportValidator +{ + private const ERROR_INVALID_LAYOUT_UPDATE = 'invalidLayoutUpdate'; + + /** + * @var ValidatorFactory + */ + private $layoutValidatorFactory; + + /** + * @var ValidationStateInterface + */ + private $validationState; + + /** + * @param ValidatorFactory $layoutValidatorFactory + * @param ValidationStateInterface $validationState + */ + public function __construct( + ValidatorFactory $layoutValidatorFactory, + ValidationStateInterface $validationState + ) { + $this->layoutValidatorFactory = $layoutValidatorFactory; + $this->validationState = $validationState; + } + + /** + * @inheritdoc + */ + public function isValid($value): bool + { + if (!empty($value['custom_layout_update']) && !$this->validateXml($value['custom_layout_update'])) { + $this->_addMessages( + [ + $this->context->retrieveMessageTemplate(self::ERROR_INVALID_LAYOUT_UPDATE) + ] + ); + return false; + } + + return true; + } + + /** + * Validate XML layout update + * + * @param string $xml + * @return bool + */ + private function validateXml(string $xml): bool + { + /** @var $layoutXmlValidator \Magento\Framework\View\Model\Layout\Update\Validator */ + $layoutXmlValidator = $this->layoutValidatorFactory->create( + [ + 'validationState' => $this->validationState, + ] + ); + + try { + if (!$layoutXmlValidator->isValid($xml)) { + return false; + } + } catch (\Exception $e) { + return false; + } + + return true; + } +} diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php new file mode 100644 index 0000000000000..d1df04b8e95c8 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Model\Import\Product\Validator; + +use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractImportValidator; + +/** + * Validator to assert that the current user is allowed to make design updates if a layout is provided in the import + */ +class LayoutUpdatePermissions extends AbstractImportValidator +{ + private const ERROR_INSUFFICIENT_PERMISSIONS = 'insufficientPermissions'; + + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var array + */ + private $allowedUserTypes = [ + UserContextInterface::USER_TYPE_ADMIN, + UserContextInterface::USER_TYPE_INTEGRATION + ]; + + /** + * @param UserContextInterface $userContext + * @param AuthorizationInterface $authorization + */ + public function __construct( + UserContextInterface $userContext, + AuthorizationInterface $authorization + ) { + $this->userContext = $userContext; + $this->authorization = $authorization; + } + + /** + * Validate that the current user is allowed to make design updates + * + * @param array $data + * @return boolean + */ + public function isValid($data): bool + { + if (empty($data['custom_layout_update'])) { + return true; + } + + $userType = $this->userContext->getUserType(); + $isValid = in_array($userType, $this->allowedUserTypes) + && $this->authorization->isAllowed('Magento_Catalog::edit_product_design'); + + if (!$isValid) { + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate(self::ERROR_INSUFFICIENT_PERMISSIONS), + 'custom_layout_update' + ) + ] + ); + } + + return $isValid; + } +} diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php new file mode 100644 index 0000000000000..2f883b5f4146b --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product\Validator; + +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Authorization\Model\UserContextInterface; +use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions; +use Magento\Framework\AuthorizationInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test validation for layout update permissions + */ +class LayoutUpdatePermissionsTest extends TestCase +{ + /** + * @var LayoutUpdatePermissions|MockObject + */ + private $validator; + + /** + * @var UserContextInterface|MockObject + */ + private $userContext; + + /** + * @var AuthorizationInterface|MockObject + */ + private $authorization; + + /** + * @var Product + */ + private $context; + + protected function setUp() + { + $this->userContext = $this->createMock(UserContextInterface::class); + $this->authorization = $this->createMock(AuthorizationInterface::class); + $this->context = $this->createMock(Product::class); + $this->context + ->method('retrieveMessageTemplate') + ->with('insufficientPermissions') + ->willReturn('oh no "%s"'); + $this->validator = new LayoutUpdatePermissions( + $this->userContext, + $this->authorization + ); + $this->validator->init($this->context); + } + + /** + * @param $value + * @param $userContext + * @param $isAllowed + * @param $isValid + * @dataProvider configurationsProvider + */ + public function testValidationConfiguration($value, $userContext, $isAllowed, $isValid) + { + $this->userContext + ->method('getUserType') + ->willReturn($userContext); + + $this->authorization + ->method('isAllowed') + ->with('Magento_Catalog::edit_product_design') + ->willReturn($isAllowed); + + $result = $this->validator->isValid(['custom_layout_update' => $value]); + $messages = $this->validator->getMessages(); + + self::assertSame($isValid, $result); + + if ($isValid) { + self::assertSame([], $messages); + } else { + self::assertSame(['oh no "custom_layout_update"'], $messages); + } + } + + public function configurationsProvider() + { + return [ + ['', null, null, true], + [null, null, null, true], + ['foo', UserContextInterface::USER_TYPE_ADMIN, true, true], + ['foo', UserContextInterface::USER_TYPE_INTEGRATION, true, true], + ['foo', UserContextInterface::USER_TYPE_ADMIN, false, false], + ['foo', UserContextInterface::USER_TYPE_INTEGRATION, false, false], + ['foo', 'something', null, false], + ]; + } +} diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdateTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdateTest.php new file mode 100644 index 0000000000000..d1e8b879f6a08 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdateTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product\Validator; + +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate; +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\View\Model\Layout\Update\Validator; +use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test validation for layout update + */ +class LayoutUpdateTest extends TestCase +{ + /** + * @var LayoutUpdate|MockObject + */ + private $validator; + + /** + * @var Validator|MockObject + */ + private $layoutValidator; + + protected function setUp() + { + $validatorFactory = $this->createMock(ValidatorFactory::class); + $validationState = $this->createMock(ValidationStateInterface::class); + $this->layoutValidator = $this->createMock(Validator::class); + $validatorFactory->method('create') + ->with(['validationState' => $validationState]) + ->willReturn($this->layoutValidator); + + $this->validator = new LayoutUpdate( + $validatorFactory, + $validationState + ); + } + + public function testValidationIsSkippedWithDataNotPresent() + { + $this->layoutValidator + ->expects($this->never()) + ->method('isValid'); + + $result = $this->validator->isValid([]); + self::assertTrue($result); + } + + public function testValidationFailsProperly() + { + $this->layoutValidator + ->method('isValid') + ->with('foo') + ->willReturn(false); + + $contextMock = $this->createMock(Product::class); + $contextMock + ->method('retrieveMessageTemplate') + ->with('invalidLayoutUpdate') + ->willReturn('oh no'); + $this->validator->init($contextMock); + + $result = $this->validator->isValid(['custom_layout_update' => 'foo']); + $messages = $this->validator->getMessages(); + self::assertFalse($result); + self::assertSame(['oh no'], $messages); + } + + public function testInvalidDataException() + { + $this->layoutValidator + ->method('isValid') + ->willThrowException(new \Exception('foo')); + + $contextMock = $this->createMock(Product::class); + $contextMock + ->method('retrieveMessageTemplate') + ->with('invalidLayoutUpdate') + ->willReturn('oh no'); + $this->validator->init($contextMock); + + $result = $this->validator->isValid(['custom_layout_update' => 'foo']); + $messages = $this->validator->getMessages(); + self::assertFalse($result); + self::assertSame(['oh no'], $messages); + } +} diff --git a/app/code/Magento/CatalogImportExport/composer.json b/app/code/Magento/CatalogImportExport/composer.json index 56307a01e1cb6..a15c1d5e7b220 100644 --- a/app/code/Magento/CatalogImportExport/composer.json +++ b/app/code/Magento/CatalogImportExport/composer.json @@ -16,7 +16,8 @@ "magento/module-import-export": "*", "magento/module-media-storage": "*", "magento/module-store": "*", - "magento/module-tax": "*" + "magento/module-tax": "*", + "magento/module-authorization": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/CatalogImportExport/etc/di.xml b/app/code/Magento/CatalogImportExport/etc/di.xml index 6906272b11d68..4e2fe390e0b17 100644 --- a/app/code/Magento/CatalogImportExport/etc/di.xml +++ b/app/code/Magento/CatalogImportExport/etc/di.xml @@ -25,7 +25,14 @@ <item name="website" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Website</item> <item name="weight" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Weight</item> <item name="quantity" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Quantity</item> + <item name="layout_update" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate</item> + <item name="layout_update_permissions" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions</item> </argument> </arguments> </type> + <type name="Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate"> + <arguments> + <argument name="validationState" xsi:type="object">Magento\Framework\Config\ValidationState\Required</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php b/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php new file mode 100644 index 0000000000000..1bb2037462f3b --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Test\Unit\Model\CatalogImport\LayoutUpdate; + +use Magento\CatalogImportExport\Model\Import\Product; +use Magento\Authorization\Model\UserContextInterface; +use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions; +use Magento\Framework\AuthorizationInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test validation for layout update + */ +class PermissionsValidatorTest extends TestCase +{ + /** + * @var PermissionsValidator|MockObject + */ + private $validator; + + /** + * @var UserContextInterface|MockObject + */ + private $userContext; + + /** + * @var AuthorizationInterface|MockObject + */ + private $authorization; + + /** + * @var Product + */ + private $context; + + protected function setUp() + { + $this->userContext = $this->createMock(UserContextInterface::class); + $this->authorization = $this->createMock(AuthorizationInterface::class); + $this->context = $this->createMock(Product::class); + $this->context + ->method('retrieveMessageTemplate') + ->with('insufficientPermissions') + ->willReturn('oh no "%s"'); + $this->validator = new LayoutUpdatePermissions( + $this->userContext, + $this->authorization + ); + $this->validator->init($this->context); + } + + /** + * @param $value + * @param $userContext + * @param $isAllowed + * @param $isValid + * @dataProvider configurationsProvider + */ + public function testValidationConfiguration($value, $userContext, $isAllowed, $isValid) + { + $this->userContext + ->method('getUserType') + ->willReturn($userContext); + + $this->authorization + ->method('isAllowed') + ->with('Magento_Catalog::edit_product_design') + ->willReturn($isAllowed); + + $result = $this->validator->isValid(['custom_layout_update' => $value]); + $messages = $this->validator->getMessages(); + + self::assertSame($isValid, $result); + + if ($isValid) { + self::assertSame([], $messages); + } else { + self::assertSame(['oh no "custom_layout_update"'], $messages); + } + } + + public function configurationsProvider() + { + return [ + ['', null, null, true], + [null, null, null, true], + ['foo', UserContextInterface::USER_TYPE_ADMIN, true, true], + ['foo', UserContextInterface::USER_TYPE_INTEGRATION, true, true], + ['foo', UserContextInterface::USER_TYPE_ADMIN, false, false], + ['foo', UserContextInterface::USER_TYPE_INTEGRATION, false, false], + ['foo', 'something', null, false], + ]; + } +} diff --git a/app/etc/di.xml b/app/etc/di.xml index cccae25b467eb..5c10216ba757d 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1500,6 +1500,16 @@ </argument> </arguments> </type> + <virtualType name="Magento\Framework\Config\ValidationState\Required" type="Magento\Framework\Config\ValidationState\Configurable"> + <arguments> + <argument name="required" xsi:type="boolean">true</argument> + </arguments> + </virtualType> + <virtualType name="Magento\Framework\Config\ValidationState\NotRequired" type="Magento\Framework\Config\ValidationState\Configurable"> + <arguments> + <argument name="required" xsi:type="boolean">false</argument> + </arguments> + </virtualType> <virtualType name="Magento\Framework\Setup\Declaration\Schema\Config\SchemaLocator" type="Magento\Framework\Config\SchemaLocator"> <arguments> <argument name="realPath" xsi:type="string">urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd</argument> diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 41cd85e6ec2f6..4863aadfac0c9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -95,6 +95,7 @@ protected function tearDown() try { $product = $productRepository->get($productSku, false, null, true); $productRepository->delete($product); + // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (NoSuchEntityException $e) { // nothing to delete } @@ -2335,6 +2336,8 @@ public function testImportImageForNonDefaultStore() */ public function testProductsWithMultipleStoresWhenMediaIsDisabled(): void { + $this->loginAdminUserWithUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME); + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); $source = $this->objectManager->create( @@ -2417,4 +2420,22 @@ private function importFile(string $fileName): void $this->_model->importData(); } + + /** + * Set the current admin session user based on a username + * + * @param string $username + */ + private function loginAdminUserWithUsername(string $username) + { + $user = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\User\Model\User::class + )->loadByUsername($username); + + /** @var $session \Magento\Backend\Model\Auth\Session */ + $session = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Backend\Model\Auth\Session::class + ); + $session->setUser($user); + } } diff --git a/lib/internal/Magento/Framework/Config/Test/Unit/ValidationState/ConfigurableTest.php b/lib/internal/Magento/Framework/Config/Test/Unit/ValidationState/ConfigurableTest.php new file mode 100644 index 0000000000000..cbd9e43632489 --- /dev/null +++ b/lib/internal/Magento/Framework/Config/Test/Unit/ValidationState/ConfigurableTest.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Config\Test\Unit\ValidationState; + +use Magento\Framework\Config\ValidationState\Configurable; +use PHPUnit\Framework\TestCase; + +/** + * Tests for configurable validation state + */ +class ConfigurableTest extends TestCase +{ + public function testTrue() + { + $state = new Configurable(true); + self::assertTrue($state->isValidationRequired()); + } + + public function testFalse() + { + $state = new Configurable(false); + self::assertFalse($state->isValidationRequired()); + } +} diff --git a/lib/internal/Magento/Framework/Config/ValidationState/Configurable.php b/lib/internal/Magento/Framework/Config/ValidationState/Configurable.php new file mode 100644 index 0000000000000..c996b2a3e135d --- /dev/null +++ b/lib/internal/Magento/Framework/Config/ValidationState/Configurable.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\Config\ValidationState; + +use Magento\Framework\Config\ValidationStateInterface; + +/** + * A configurable validation state + */ +class Configurable implements ValidationStateInterface +{ + /** + * @var bool + */ + private $required; + + /** + * @param bool $required + */ + public function __construct(bool $required) + { + $this->required = $required; + } + + /** + * @inheritdoc + */ + public function isValidationRequired(): bool + { + return $this->required; + } +} From 13e06bd99ac2056a623a13621357e9a49afd993a Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 14 Jun 2019 14:30:58 -0500 Subject: [PATCH 0126/2437] MC-16118: Email template name config update - Updated email option array value --- .../Magento/Config/Model/Config/Source/Email/Template.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Config/Model/Config/Source/Email/Template.php b/app/code/Magento/Config/Model/Config/Source/Email/Template.php index 04222733418d3..c6b28cd7c46a9 100644 --- a/app/code/Magento/Config/Model/Config/Source/Email/Template.php +++ b/app/code/Magento/Config/Model/Config/Source/Email/Template.php @@ -62,6 +62,12 @@ public function toOptionArray() $templateLabel = $this->_emailConfig->getTemplateLabel($templateId); $templateLabel = __('%1 (Default)', $templateLabel); array_unshift($options, ['value' => $templateId, 'label' => $templateLabel]); + array_walk( + $options, + function (&$item) { + $item['__disableTmpl'] = true; + } + ); return $options; } } From 939eaf4e1e254376bfd2e398d2c4f6ce288bfa5b Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 14 Jun 2019 14:38:27 -0500 Subject: [PATCH 0127/2437] MC-16118: Email template name config update - Updated email template unit test --- .../Model/Config/Source/Email/TemplateTest.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php index b29ad244e7a81..a5878a04e3e60 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php @@ -76,9 +76,21 @@ public function testToOptionArray() $this->returnValue('Template New') ); $expectedResult = [ - ['value' => 'template_new', 'label' => 'Template New (Default)'], - ['value' => 'template_one', 'label' => 'Template One'], - ['value' => 'template_two', 'label' => 'Template Two'], + [ + 'value' => 'template_new', + 'label' => 'Template New (Default)', + '__disableTmpl' => true + ], + [ + 'value' => 'template_one', + 'label' => 'Template One', + '__disableTmpl' => true + ], + [ + 'value' => 'template_two', + 'label' => 'Template Two', + '__disableTmpl' => true + ], ]; $this->_model->setPath('template/new'); $this->assertEquals($expectedResult, $this->_model->toOptionArray()); From 327cd700be97b505fe9bd61052a3da58cd0d46e5 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Fri, 14 Jun 2019 16:15:44 -0500 Subject: [PATCH 0128/2437] MC-16118: Email template name config update - Resolved static test failures --- app/code/Magento/Config/Model/Config/Source/Email/Template.php | 2 ++ .../Test/Unit/Model/Config/Source/Email/TemplateTest.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/Config/Model/Config/Source/Email/Template.php b/app/code/Magento/Config/Model/Config/Source/Email/Template.php index c6b28cd7c46a9..e4f1ae65bcacd 100644 --- a/app/code/Magento/Config/Model/Config/Source/Email/Template.php +++ b/app/code/Magento/Config/Model/Config/Source/Email/Template.php @@ -6,6 +6,8 @@ namespace Magento\Config\Model\Config\Source\Email; /** + * Source for template + * * @api * @since 100.0.2 */ diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php index a5878a04e3e60..9fabe6fef0c8e 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Source/Email/TemplateTest.php @@ -6,6 +6,9 @@ namespace Magento\Config\Test\Unit\Model\Config\Source\Email; +/** + * Test class for Template. + */ class TemplateTest extends \PHPUnit\Framework\TestCase { /** From 03d1ccc5526d6a8639e75c9748aeda2b01896162 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 17 Jun 2019 08:27:32 -0500 Subject: [PATCH 0129/2437] MC-16284: Visible message alert --- .../base/web/js/form/element/file-uploader.js | 9 +------- .../Image/Adapter/AbstractAdapter.php | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index f4b267758cf1d..a53481ad13868 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -427,8 +427,7 @@ define([ _.each(this.aggregatedErrors, function (error) { notification().add({ error: true, - // %s to be used as placeholders for html injection - message: '%s' + error.filename + '%s ' + $t('was not uploaded.') + '%s' + error.message, + message: error.message, /** * Adds constructed error notification to aggregatedErrorMessages @@ -436,12 +435,6 @@ define([ * @param {String} constructedMessage */ insertMethod: function (constructedMessage) { - // html is escaped in message body for notification widget; prepend unescaped html here - constructedMessage = constructedMessage - .replace('%s', '<strong>') - .replace('%s', '</strong>') - .replace('%s', '<br>'); - aggregatedErrorMessages.push(constructedMessage); } }); diff --git a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php index 6042e4eee491d..276a8a719d6a8 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php @@ -3,11 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Image\Adapter; use Magento\Framework\App\Filesystem\DirectoryList; /** + * Class AbstractAdapter + * * @file Abstract.php * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.TooManyFields) @@ -169,7 +173,8 @@ abstract public function open($fileName); /** * Save image to specific path. - * If some folders of path does not exist they will be created + * + * If some folders of the path do not exist they will be created. * * @param null|string $destination * @param null|string $newName @@ -620,6 +625,7 @@ protected function _checkDimensions($frameWidth, $frameHeight) $frameHeight !== null && $frameHeight <= 0 || empty($frameWidth) && empty($frameHeight) ) { + //phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('Invalid image dimensions.'); } } @@ -687,6 +693,7 @@ protected function _prepareDestination($destination = null, $newName = null) $this->directoryWrite->create($this->directoryWrite->getRelativePath($destination)); } catch (\Magento\Framework\Exception\FileSystemException $e) { $this->logger->critical($e); + //phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('Unable to write file into directory ' . $destination . '. Access forbidden.'); } } @@ -714,11 +721,19 @@ protected function _canProcess() public function validateUploadFile($filePath) { if (!file_exists($filePath)) { - throw new \InvalidArgumentException("File '{$filePath}' does not exists."); + throw new \InvalidArgumentException('Upload file does not exist.'); + } + + try { + $imageSize = getimagesize($filePath); + } catch (\Exception $e) { + $imageSize = false; } - if (!getimagesize($filePath)) { + + if (!$imageSize) { throw new \InvalidArgumentException('Disallowed file type.'); } + $this->checkDependencies(); $this->open($filePath); From 9dd3e4b1c6fee2bf2796f7e9778275f0ac398c85 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 17 Jun 2019 08:43:02 -0500 Subject: [PATCH 0130/2437] MC-16284: Visible message alert --- .../Ui/view/base/web/js/form/element/file-uploader.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index a53481ad13868..68c9e7308bc02 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -427,7 +427,7 @@ define([ _.each(this.aggregatedErrors, function (error) { notification().add({ error: true, - message: error.message, + message: '%s' + error.message, // %s to be used as placeholder for html injection, /** * Adds constructed error notification to aggregatedErrorMessages @@ -435,6 +435,11 @@ define([ * @param {String} constructedMessage */ insertMethod: function (constructedMessage) { + var errorMsgBodyHtml = '<strong>File was not uploaded.</strong><br>'; + + // html is escaped in message body for notification widget; prepend unescaped html here + constructedMessage = constructedMessage.replace('%s', errorMsgBodyHtml); + aggregatedErrorMessages.push(constructedMessage); } }); From 4cea9401c486671e5428e6da835398427faec330 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 17 Jun 2019 08:44:36 -0500 Subject: [PATCH 0131/2437] MC-16284: Visible message alert --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 68c9e7308bc02..2db7aef0acf61 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -427,7 +427,7 @@ define([ _.each(this.aggregatedErrors, function (error) { notification().add({ error: true, - message: '%s' + error.message, // %s to be used as placeholder for html injection, + message: '%s' + error.message, // %s to be used as placeholder for html injection /** * Adds constructed error notification to aggregatedErrorMessages From ce6ef8b47ce93e36cb32dd9658dc8d32a7caf91d Mon Sep 17 00:00:00 2001 From: Aliaksei Yakimovich2 <aliaksei_yakimovich2@epam.com> Date: Mon, 17 Jun 2019 18:06:15 +0300 Subject: [PATCH 0132/2437] MC-15523: Watermark is possible to set up for swatch image type - Removed config section for swatches watermark image; --- .../Mftf/Test/AdminWatermarkUploadTest.xml | 16 ----- app/code/Magento/Swatches/etc/config.xml | 5 -- app/code/Magento/Swatches/etc/di.xml | 33 --------- .../ui_component/design_config_form.xml | 72 ------------------- 4 files changed, 126 deletions(-) delete mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml delete mode 100644 app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml deleted file mode 100644 index e9df186bae5e6..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminWatermarkUploadTest"> - <waitForElement selector="{{AdminDesignConfigSection.imageUploadInputByFieldsetName('Swatch Image')}}" stepKey="waitForInputVisible4" after="waitForPreviewImage3"/> - <attachFile selector="{{AdminDesignConfigSection.imageUploadInputByFieldsetName('Swatch Image')}}" userInput="adobe-small.jpg" stepKey="attachFile4" after="waitForInputVisible4"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Swatch Image')}}" stepKey="waitForPreviewImage4" after="attachFile4"/> - </test> -</tests> diff --git a/app/code/Magento/Swatches/etc/config.xml b/app/code/Magento/Swatches/etc/config.xml index 65b36558c2796..6b487821dd462 100644 --- a/app/code/Magento/Swatches/etc/config.xml +++ b/app/code/Magento/Swatches/etc/config.xml @@ -21,10 +21,5 @@ </input_types> </validator_data> </general> - <design> - <watermark> - <swatch_image_position>stretch</swatch_image_position> - </watermark> - </design> </default> </config> diff --git a/app/code/Magento/Swatches/etc/di.xml b/app/code/Magento/Swatches/etc/di.xml index 5292bfafb6a0f..ca24f1afe8b46 100644 --- a/app/code/Magento/Swatches/etc/di.xml +++ b/app/code/Magento/Swatches/etc/di.xml @@ -40,39 +40,6 @@ </argument> </arguments> </type> - <type name="Magento\Theme\Model\Design\Config\MetadataProvider"> - <arguments> - <argument name="metadata" xsi:type="array"> - <item name="watermark_swatch_image_size" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_size</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - <item name="watermark_swatch_image_imageOpacity" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_imageOpacity</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - <item name="watermark_swatch_image_image" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_image</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - <item name="backend_model" xsi:type="string">Magento\Theme\Model\Design\Backend\Image</item> - <item name="upload_dir" xsi:type="array"> - <item name="config" xsi:type="string">system/filesystem/media</item> - <item name="scope_info" xsi:type="string">1</item> - <item name="value" xsi:type="string">catalog/product/watermark</item> - </item> - <item name="base_url" xsi:type="array"> - <item name="type" xsi:type="string">media</item> - <item name="scope_info" xsi:type="string">1</item> - <item name="value" xsi:type="string">catalog/product/watermark</item> - </item> - </item> - <item name="watermark_swatch_image_position" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_position</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - </argument> - </arguments> - </type> <type name="Magento\Swatches\Model\SwatchAttributeCodes"> <arguments> <argument name="cacheKey" xsi:type="string">swatch-attribute-list</argument> diff --git a/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml deleted file mode 100644 index b38e8ecc6e201..0000000000000 --- a/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> - <fieldset name="other_settings"> - <fieldset name="watermark"> - <fieldset name="swatch_image" sortOrder="40"> - <settings> - <level>2</level> - <label translate="true">Swatch Image</label> - </settings> - <field name="watermark_swatch_image_image" formElement="imageUploader"> - <settings> - <label translate="true">Image</label> - <componentType>imageUploader</componentType> - </settings> - <formElements> - <imageUploader> - <settings> - <allowedExtensions>jpg jpeg gif png</allowedExtensions> - <maxFileSize>2097152</maxFileSize> - <uploaderConfig> - <param xsi:type="string" name="url">theme/design_config_fileUploader/save</param> - </uploaderConfig> - </settings> - </imageUploader> - </formElements> - </field> - <field name="watermark_swatch_image_imageOpacity" formElement="input"> - <settings> - <validation> - <rule name="validate-number" xsi:type="boolean">true</rule> - </validation> - <dataType>text</dataType> - <addAfter>%</addAfter> - <label translate="true">Image Opacity</label> - <dataScope>watermark_swatch_image_imageOpacity</dataScope> - </settings> - </field> - <field name="watermark_swatch_image_size" component="Magento_Catalog/component/image-size-field" formElement="input"> - <settings> - <notice translate="true">Example format: 200x300.</notice> - <validation> - <rule name="validate-image-size-range" xsi:type="boolean">true</rule> - </validation> - <dataType>text</dataType> - <label translate="true">Image Size</label> - <dataScope>watermark_swatch_image_size</dataScope> - </settings> - </field> - <field name="watermark_swatch_image_position" formElement="select"> - <settings> - <dataType>text</dataType> - <label translate="true">Image Position</label> - <dataScope>watermark_swatch_image_position</dataScope> - </settings> - <formElements> - <select> - <settings> - <options class="Magento\Catalog\Model\Config\Source\Watermark\Position"/> - </settings> - </select> - </formElements> - </field> - </fieldset> - </fieldset> - </fieldset> -</form> From 48dba482ef080aa937875427c2127f6b6ffbcbe7 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 17 Jun 2019 11:44:56 -0500 Subject: [PATCH 0133/2437] MC-16284: Visible message alert --- .../Ui/view/base/web/js/form/element/file-uploader.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 2db7aef0acf61..73bef62910644 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -435,7 +435,10 @@ define([ * @param {String} constructedMessage */ insertMethod: function (constructedMessage) { - var errorMsgBodyHtml = '<strong>File was not uploaded.</strong><br>'; + var escapedFileName = $('<div>').text(error.filename).html(), + errorMsgBodyHtml = '<strong>%s</strong> %s.<br>' + .replace('%s', escapedFileName) + .replace('%s', $t('was not uploaded')); // html is escaped in message body for notification widget; prepend unescaped html here constructedMessage = constructedMessage.replace('%s', errorMsgBodyHtml); From 5439b01c11464fab72122cb8b549b3fc844b1713 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Mon, 17 Jun 2019 12:41:02 -0500 Subject: [PATCH 0134/2437] MC-15979: New Customer Attribute Options Issue - Resolved incorrect attribute meta data resolver --- .../Magento/Customer/Model/AttributeMetadataResolver.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index a41d52cdc45a4..acc54424c3acc 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -129,7 +129,14 @@ public function getAttributesMeta( $meta['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource ->getAllOptions(); } else { - $meta['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + $options = $attribute->getSource()->getAllOptions(); + array_walk( + $options, + function (&$item) { + $item['__disableTmpl'] = ['label' => true]; + } + ); + $meta['arguments']['data']['config']['options'] = $options; } } From ffc30aa55a16272a4c698c466a199ceb5e01db40 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 4 Jun 2019 21:21:40 -0500 Subject: [PATCH 0135/2437] MC-16041: Prevent errors from incorrect configuration --- .../Controller/Adminhtml/Page/InlineEdit.php | 27 ++-- .../Adminhtml/Page/PostDataProcessor.php | 1 + .../Cms/Controller/Adminhtml/Page/Save.php | 4 - .../PageRepository/ValidationComposite.php | 92 +++++++++++++ .../Validator/LayoutUpdateValidator.php | 128 ++++++++++++++++++ .../PageRepository/ValidatorInterface.php | 27 ++++ app/code/Magento/Cms/etc/di.xml | 11 +- 7 files changed, 275 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Cms/Model/PageRepository/ValidationComposite.php create mode 100644 app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php create mode 100644 app/code/Magento/Cms/Model/PageRepository/ValidatorInterface.php diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php index 8774d7e69adfe..77f648e5f12c8 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php @@ -7,6 +7,7 @@ use Magento\Backend\App\Action\Context; use Magento\Cms\Api\PageRepositoryInterface as PageRepository; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Cms\Api\Data\PageInterface; @@ -15,7 +16,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class InlineEdit extends \Magento\Backend\App\Action +class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session @@ -56,6 +57,8 @@ public function __construct( } /** + * Process the request + * * @return \Magento\Framework\Controller\ResultInterface * @throws \Magento\Framework\Exception\LocalizedException */ @@ -68,10 +71,12 @@ public function execute() $postItems = $this->getRequest()->getParam('items', []); if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { - return $resultJson->setData([ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ]); + return $resultJson->setData( + [ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ] + ); } foreach (array_keys($postItems) as $pageId) { @@ -79,7 +84,6 @@ public function execute() $page = $this->pageRepository->getById($pageId); try { $pageData = $this->filterPost($postItems[$pageId]); - $this->validatePost($pageData, $page, $error, $messages); $extendedPageData = $page->getData(); $this->setCmsPageData($page, $extendedPageData, $pageData); $this->pageRepository->save($page); @@ -98,10 +102,12 @@ public function execute() } } - return $resultJson->setData([ - 'messages' => $messages, - 'error' => $error - ]); + return $resultJson->setData( + [ + 'messages' => $messages, + 'error' => $error + ] + ); } /** @@ -128,6 +134,7 @@ protected function filterPost($postData = []) * @param bool $error * @param array $messages * @return void + * @deprecated */ protected function validatePost(array $pageData, \Magento\Cms\Model\Page $page, &$error, array &$messages) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index 9b8933c8dba2e..f934cac6da11d 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -80,6 +80,7 @@ public function filter($data) * * @param array $data * @return bool Return FALSE if some item is invalid + * @deprecated */ public function validate($data) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 37cb45753174f..569f6b256163f 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -107,10 +107,6 @@ public function execute() ['page' => $model, 'request' => $this->getRequest()] ); - if (!$this->dataProcessor->validate($data)) { - return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); - } - try { $this->pageRepository->save($model); $this->messageManager->addSuccessMessage(__('You saved the page.')); diff --git a/app/code/Magento/Cms/Model/PageRepository/ValidationComposite.php b/app/code/Magento/Cms/Model/PageRepository/ValidationComposite.php new file mode 100644 index 0000000000000..9fd94d4c11e1c --- /dev/null +++ b/app/code/Magento/Cms/Model/PageRepository/ValidationComposite.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\PageRepository; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaInterface; + +/** + * Validates and saves a page + */ +class ValidationComposite implements PageRepositoryInterface +{ + /** + * @var PageRepositoryInterface + */ + private $repository; + + /** + * @var array + */ + private $validators; + + /** + * @param PageRepositoryInterface $repository + * @param ValidatorInterface[] $validators + */ + public function __construct( + PageRepositoryInterface $repository, + array $validators = [] + ) { + foreach ($validators as $validator) { + if (!$validator instanceof ValidatorInterface) { + throw new \InvalidArgumentException( + sprintf('Supplied validator does not implement %s', ValidatorInterface::class) + ); + } + } + $this->repository = $repository; + $this->validators = $validators; + } + + /** + * @inheritdoc + */ + public function save(PageInterface $page) + { + foreach ($this->validators as $validator) { + $validator->validate($page); + } + + return $this->repository->save($page); + } + + /** + * @inheritdoc + */ + public function getById($pageId) + { + return $this->repository->getById($pageId); + } + + /** + * @inheritdoc + */ + public function getList(SearchCriteriaInterface $searchCriteria) + { + return $this->repository->getList($searchCriteria); + } + + /** + * @inheritdoc + */ + public function delete(PageInterface $page) + { + return $this->repository->delete($page); + } + + /** + * @inheritdoc + */ + public function deleteById($pageId) + { + return $this->repository->deleteById($pageId); + } +} diff --git a/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php b/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php new file mode 100644 index 0000000000000..8cfb6f1433b63 --- /dev/null +++ b/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\PageRepository\Validator; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\PageRepository\ValidatorInterface; +use Magento\Framework\Config\Dom\ValidationException; +use Magento\Framework\Config\Dom\ValidationSchemaException; +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\View\Model\Layout\Update\Validator; +use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; + +/** + * Validate a given page + */ +class LayoutUpdateValidator implements ValidatorInterface +{ + /** + * @var ValidatorFactory + */ + private $validatorFactory; + + /** + * @var ValidationStateInterface + */ + private $validationState; + + /** + * @param ValidatorFactory $validatorFactory + * @param ValidationStateInterface $validationState + */ + public function __construct( + ValidatorFactory $validatorFactory, + ValidationStateInterface $validationState + ) { + $this->validatorFactory = $validatorFactory; + $this->validationState = $validationState; + } + + /** + * Validate the data before saving + * + * @param PageInterface $page + * @throws LocalizedException + */ + public function validate(PageInterface $page): void + { + $this->validateRequiredFields($page); + $this->validateLayoutUpdate($page); + $this->validateCustomLayoutUpdate($page); + } + + /** + * Validate required fields + * + * @param PageInterface $page + * @throws LocalizedException + */ + private function validateRequiredFields(PageInterface $page): void + { + if (empty($page->getTitle())) { + throw new LocalizedException(__(sprintf('Required field "%s" is empty.', 'title'))); + } + } + + /** + * Validate layout update + * + * @param PageInterface $page + * @throws LocalizedException + */ + private function validateLayoutUpdate(PageInterface $page): void + { + $layoutXmlValidator = $this->getLayoutValidator(); + + try { + if (!empty($page->getLayoutUpdateXml()) + && !$layoutXmlValidator->isValid($page->getLayoutUpdateXml()) + ) { + throw new LocalizedException(__('Layout update is invalid')); + } + } catch (ValidationException|ValidationSchemaException $e) { + throw new LocalizedException(__('Layout update is invalid')); + } + } + + /** + * Validate custom layout update + * + * @param PageInterface $page + * @throws LocalizedException + */ + private function validateCustomLayoutUpdate(PageInterface $page): void + { + $layoutXmlValidator = $this->getLayoutValidator(); + + try { + if (!empty($page->getCustomLayoutUpdateXml()) + && !$layoutXmlValidator->isValid($page->getCustomLayoutUpdateXml()) + ) { + throw new LocalizedException(__('Custom layout update is invalid')); + } + } catch (ValidationException|ValidationSchemaException $e) { + throw new LocalizedException(__('Custom layout update is invalid')); + } + } + + /** + * Return a new validator + * + * @return Validator + */ + private function getLayoutValidator(): Validator + { + return $this->validatorFactory->create( + [ + 'validationState' => $this->validationState, + ] + ); + } +} diff --git a/app/code/Magento/Cms/Model/PageRepository/ValidatorInterface.php b/app/code/Magento/Cms/Model/PageRepository/ValidatorInterface.php new file mode 100644 index 0000000000000..ff5c7648a9fa2 --- /dev/null +++ b/app/code/Magento/Cms/Model/PageRepository/ValidatorInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\PageRepository; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Framework\Exception\LocalizedException; + +/** + * Validate a page repository + */ +interface ValidatorInterface +{ + /** + * Assert the given page valid + * + * @param PageInterface $page + * @return void + * @throws LocalizedException + */ + public function validate(PageInterface $page): void; +} diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 66b0edf6e1eac..94b004a9d4b86 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -15,7 +15,7 @@ <preference for="Magento\Cms\Api\Data\PageInterface" type="Magento\Cms\Model\Page" /> <preference for="Magento\Cms\Api\Data\BlockInterface" type="Magento\Cms\Model\Block" /> <preference for="Magento\Cms\Api\BlockRepositoryInterface" type="Magento\Cms\Model\BlockRepository" /> - <preference for="Magento\Cms\Api\PageRepositoryInterface" type="Magento\Cms\Model\PageRepository" /> + <preference for="Magento\Cms\Api\PageRepositoryInterface" type="Magento\Cms\Model\PageRepository\ValidationComposite" /> <preference for="Magento\Ui\Component\Wysiwyg\ConfigInterface" type="Magento\Cms\Model\Wysiwyg\Config"/> <preference for="Magento\Cms\Api\GetUtilityPageIdentifiersInterface" type="Magento\Cms\Model\GetUtilityPageIdentifiers" /> <type name="Magento\Cms\Model\Wysiwyg\Config"> @@ -232,5 +232,14 @@ </argument> </arguments> </type> + + <type name="Magento\Cms\Model\PageRepository\ValidationComposite"> + <arguments> + <argument name="repository" xsi:type="object">Magento\Cms\Model\PageRepository</argument> + <argument name="validators" xsi:type="array"> + <item name="layout_update" xsi:type="object">Magento\Cms\Model\PageRepository\Validator\LayoutUpdateValidator</item> + </argument> + </arguments> + </type> </config> From ba19d6dfa713e3555a4cac91457362f8182ef0e5 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 17 Jun 2019 16:13:58 -0500 Subject: [PATCH 0136/2437] MC-16724: Prevent errors from incorrect import data --- .../LayoutUpdate/PermissionsValidatorTest.php | 101 ------------------ 1 file changed, 101 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php diff --git a/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php b/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php deleted file mode 100644 index 1bb2037462f3b..0000000000000 --- a/app/code/Magento/Cms/Test/Unit/Model/CatalogImport/LayoutUpdate/PermissionsValidatorTest.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Cms\Test\Unit\Model\CatalogImport\LayoutUpdate; - -use Magento\CatalogImportExport\Model\Import\Product; -use Magento\Authorization\Model\UserContextInterface; -use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions; -use Magento\Framework\AuthorizationInterface; -use PHPUnit\Framework\TestCase; -use PHPUnit_Framework_MockObject_MockObject as MockObject; - -/** - * Test validation for layout update - */ -class PermissionsValidatorTest extends TestCase -{ - /** - * @var PermissionsValidator|MockObject - */ - private $validator; - - /** - * @var UserContextInterface|MockObject - */ - private $userContext; - - /** - * @var AuthorizationInterface|MockObject - */ - private $authorization; - - /** - * @var Product - */ - private $context; - - protected function setUp() - { - $this->userContext = $this->createMock(UserContextInterface::class); - $this->authorization = $this->createMock(AuthorizationInterface::class); - $this->context = $this->createMock(Product::class); - $this->context - ->method('retrieveMessageTemplate') - ->with('insufficientPermissions') - ->willReturn('oh no "%s"'); - $this->validator = new LayoutUpdatePermissions( - $this->userContext, - $this->authorization - ); - $this->validator->init($this->context); - } - - /** - * @param $value - * @param $userContext - * @param $isAllowed - * @param $isValid - * @dataProvider configurationsProvider - */ - public function testValidationConfiguration($value, $userContext, $isAllowed, $isValid) - { - $this->userContext - ->method('getUserType') - ->willReturn($userContext); - - $this->authorization - ->method('isAllowed') - ->with('Magento_Catalog::edit_product_design') - ->willReturn($isAllowed); - - $result = $this->validator->isValid(['custom_layout_update' => $value]); - $messages = $this->validator->getMessages(); - - self::assertSame($isValid, $result); - - if ($isValid) { - self::assertSame([], $messages); - } else { - self::assertSame(['oh no "custom_layout_update"'], $messages); - } - } - - public function configurationsProvider() - { - return [ - ['', null, null, true], - [null, null, null, true], - ['foo', UserContextInterface::USER_TYPE_ADMIN, true, true], - ['foo', UserContextInterface::USER_TYPE_INTEGRATION, true, true], - ['foo', UserContextInterface::USER_TYPE_ADMIN, false, false], - ['foo', UserContextInterface::USER_TYPE_INTEGRATION, false, false], - ['foo', 'something', null, false], - ]; - } -} From 425c34f75f3d43c145248fe0a04772c28b5e288a Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 17 Jun 2019 16:52:46 -0500 Subject: [PATCH 0137/2437] MC-16041: Prevent errors from incorrect configuration --- .../ValidationCompositeTest.php | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Unit/Model/PageRepository/ValidationCompositeTest.php diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/ValidationCompositeTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/ValidationCompositeTest.php new file mode 100644 index 0000000000000..f73396230a669 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/ValidationCompositeTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Test\Unit\Model\PageRepository; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\PageRepository\ValidationComposite; +use Magento\Cms\Model\PageRepository\ValidatorInterface; +use Magento\Framework\Api\SearchCriteriaInterface; +use Magento\Framework\Exception\LocalizedException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Validate behavior of the validation composite + */ +class ValidationCompositeTest extends TestCase +{ + /** + * @var PageRepositoryInterface|MockObject + */ + private $subject; + + protected function setUp() + { + /** @var PageRepositoryInterface subject */ + $this->subject = $this->createMock(PageRepositoryInterface::class); + } + + /** + * @param $validators + * @expectedException \InvalidArgumentException + * @dataProvider constructorArgumentProvider + */ + public function testConstructorValidation($validators) + { + new ValidationComposite($this->subject, $validators); + } + + public function testSaveInvokesValidatorsWithSucess() + { + $validator1 = $this->createMock(ValidatorInterface::class); + $validator2 = $this->createMock(ValidatorInterface::class); + $page = $this->createMock(PageInterface::class); + + // Assert each are called + $validator1 + ->expects($this->once()) + ->method('validate') + ->with($page); + $validator2 + ->expects($this->once()) + ->method('validate') + ->with($page); + + // Assert that the success is called + $this->subject + ->expects($this->once()) + ->method('save') + ->with($page) + ->willReturn('foo'); + + $composite = new ValidationComposite($this->subject, [$validator1, $validator2]); + $result = $composite->save($page); + + self::assertSame('foo', $result); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Oh no. That isn't right. + */ + public function testSaveInvokesValidatorsWithErrors() + { + $validator1 = $this->createMock(ValidatorInterface::class); + $validator2 = $this->createMock(ValidatorInterface::class); + $page = $this->createMock(PageInterface::class); + + // Assert the first is called + $validator1 + ->expects($this->once()) + ->method('validate') + ->with($page) + ->willThrowException(new LocalizedException(__('Oh no. That isn\'t right.'))); + + // Assert the second is NOT called + $validator2 + ->expects($this->never()) + ->method('validate'); + + // Assert that the success is NOT called + $this->subject + ->expects($this->never()) + ->method('save'); + + $composite = new ValidationComposite($this->subject, [$validator1, $validator2]); + $composite->save($page); + } + + /** + * @param $method + * @param $arg + * @dataProvider passthroughMethodDataProvider + */ + public function testPassthroughMethods($method, $arg) + { + $this->subject + ->method($method) + ->with($arg) + ->willReturn('foo'); + + $composite = new ValidationComposite($this->subject, []); + $result = $composite->{$method}($arg); + + self::assertSame('foo', $result); + } + + public function constructorArgumentProvider() + { + return [ + [[null], false], + [[''], false], + [['foo'], false], + [[new \stdClass()], false], + [[$this->createMock(ValidatorInterface::class), 'foo'], false], + ]; + } + + public function passthroughMethodDataProvider() + { + return [ + ['save', $this->createMock(PageInterface::class)], + ['getById', 1], + ['getList', $this->createMock(SearchCriteriaInterface::class)], + ['delete', $this->createMock(PageInterface::class)], + ['deleteById', 1], + ]; + } +} From 9e322c82af00ee46451aa90d3adf6174edd02b1f Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 18 Jun 2019 11:32:50 +0300 Subject: [PATCH 0138/2437] MC-17303: Customer login fix --- .../Customer/Controller/Account/LoginPost.php | 2 ++ .../Customer/Model/Plugin/CustomerNotification.php | 7 +++++++ app/code/Magento/Customer/Model/Session.php | 12 ++++++++++-- app/code/Magento/Rss/Controller/Feed.php | 2 ++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 9afaab4121644..48221b00bbb4d 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -26,6 +26,8 @@ use Magento\Framework\Phrase; /** + * Post login customer action. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LoginPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface diff --git a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php index 9b655fc843529..577c97a19268a 100644 --- a/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php +++ b/app/code/Magento/Customer/Model/Plugin/CustomerNotification.php @@ -15,6 +15,11 @@ use Magento\Framework\Exception\NoSuchEntityException; use Psr\Log\LoggerInterface; +/** + * Plugin before \Magento\Framework\App\Action\AbstractAction::dispatch. + * + * Plugin to remove notifications from cache. + */ class CustomerNotification { /** @@ -66,6 +71,8 @@ public function __construct( } /** + * Removes notifications from cache. + * * @param AbstractAction $subject * @param RequestInterface $request * @return void diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index c37a28d04fc52..e6612fe77a9bc 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -17,6 +17,7 @@ * @api * @method string getNoReferer() * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Session extends \Magento\Framework\Session\SessionManager @@ -354,8 +355,9 @@ public function setCustomerGroupId($id) } /** - * Get customer group id - * If customer is not logged in system, 'not logged in' group id will be returned + * Get customer group id. + * + * If customer is not logged in system, 'not logged in' group id will be returned. * * @return int */ @@ -407,6 +409,8 @@ public function checkCustomerId($customerId) } /** + * Sets customer as logged in. + * * @param Customer $customer * @return $this */ @@ -420,6 +424,8 @@ public function setCustomerAsLoggedIn($customer) } /** + * Sets customer as logged in. + * * @param CustomerData $customer * @return $this */ @@ -567,6 +573,8 @@ public function regenerateId() } /** + * Creates \Magento\Framework\UrlInterface object. + * * @return \Magento\Framework\UrlInterface */ protected function _createUrl() diff --git a/app/code/Magento/Rss/Controller/Feed.php b/app/code/Magento/Rss/Controller/Feed.php index 634540dde5d96..c3ef4fb80833e 100644 --- a/app/code/Magento/Rss/Controller/Feed.php +++ b/app/code/Magento/Rss/Controller/Feed.php @@ -76,6 +76,8 @@ public function __construct( } /** + * Authenticate not logged in customer. + * * @return bool */ protected function auth() From 878140b1a1df622107cb09be33598da59e933a6c Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Tue, 18 Jun 2019 08:51:04 -0500 Subject: [PATCH 0139/2437] MC-16284: Visible message alert * Updated Integration test --- .../Framework/Image/Adapter/InterfaceTest.php | 39 ++++++++++++++++++- .../Magento/Framework/Image/_files/empty.png | 0 .../Framework/Image/_files/notanimage.txt | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Image/_files/empty.png create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Image/_files/notanimage.txt diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php index bc8281d55ac4f..59ad82334a958 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php @@ -703,12 +703,47 @@ public function testValidateUploadFile() } /** + * @dataProvider testValidateUploadFileExceptionDataProvider * @expectedException \InvalidArgumentException + * @param string $fileName + * @param string $expectedErrorMsg + * @param bool $useFixture */ - public function testValidateUploadFileException() + public function testValidateUploadFileException($fileName, $expectedErrorMsg, $useFixture) { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $imageAdapter = $objectManager->get(\Magento\Framework\Image\AdapterFactory::class)->create(); - $imageAdapter->validateUploadFile(__FILE__); + $filePath = $useFixture ? $this->_getFixture($fileName) : $fileName; + + try { + $imageAdapter->validateUploadFile($filePath); + } catch (\InvalidArgumentException $e) { + $this->assertEquals($expectedErrorMsg, $e->getMessage()); + throw $e; + } + } + + /** + * @return array + */ + public function testValidateUploadFileExceptionDataProvider() + { + return [ + 'image_notfound' => [ + 'fileName' => 'notfound.png', + 'expectedErrorMsg' => 'Upload file does not exist.', + 'useFixture' => false + ], + 'image_empty' => [ + 'fileName' => 'empty.png', + 'expectedErrorMsg' => 'Disallowed file type.', + 'useFixture' => true + ], + 'notanimage' => [ + 'fileName' => 'notanimage.txt', + 'expectedErrorMsg' => 'Disallowed file type.', + 'useFixture' => true + ] + ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/_files/empty.png b/dev/tests/integration/testsuite/Magento/Framework/Image/_files/empty.png new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/_files/notanimage.txt b/dev/tests/integration/testsuite/Magento/Framework/Image/_files/notanimage.txt new file mode 100644 index 0000000000000..81bc3abd37125 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Image/_files/notanimage.txt @@ -0,0 +1 @@ +Not an image. From 5f4222b47413c88b906ea8fbaa341b35ec6d47d5 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 18 Jun 2019 09:49:15 -0500 Subject: [PATCH 0140/2437] MC-16041: Prevent errors from incorrect configuration --- .../Validator/LayoutUpdateValidator.php | 2 +- .../Validator/LayoutUpdateValidatorTest.php | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php diff --git a/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php b/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php index 8cfb6f1433b63..721fd9efbdd91 100644 --- a/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php +++ b/app/code/Magento/Cms/Model/PageRepository/Validator/LayoutUpdateValidator.php @@ -66,7 +66,7 @@ public function validate(PageInterface $page): void private function validateRequiredFields(PageInterface $page): void { if (empty($page->getTitle())) { - throw new LocalizedException(__(sprintf('Required field "%s" is empty.', 'title'))); + throw new LocalizedException(__('Required field "%1" is empty.', 'title')); } } diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php new file mode 100644 index 0000000000000..8fbea15250732 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\PageRepository\Validator; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Framework\Config\Dom\ValidationException; +use Magento\Framework\Config\Dom\ValidationSchemaException as SchemaException; +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; +use Magento\Framework\View\Model\Layout\Update\Validator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test cases for the layout update validator + */ +class LayoutUpdateValidatorTest extends TestCase +{ + /** + * @var Validator|MockObject + */ + private $layoutValidator; + + /** + * @var LayoutUpdateValidator + */ + private $validator; + + protected function setUp() + { + $layoutValidatorFactory = $this->createMock(ValidatorFactory::class); + $this->layoutValidator = $this->createMock(Validator::class); + $layoutValidatorState = $this->createMock(ValidationStateInterface::class); + + $layoutValidatorFactory + ->method('create') + ->with(['validationState' => $layoutValidatorState]) + ->willReturn($this->layoutValidator); + + $this->validator = new LayoutUpdateValidator($layoutValidatorFactory, $layoutValidatorState); + } + + /** + * @dataProvider validationSetDataProvider + */ + public function testValidate($data, $expectedExceptionMessage, $layoutValidatorException, $isLayoutValid = false) + { + if ($expectedExceptionMessage) { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + } + + if ($layoutValidatorException) { + $this->layoutValidator + ->method('isValid') + ->with($data['getLayoutUpdateXml'] ?? $data['getCustomLayoutUpdateXml']) + ->willThrowException($layoutValidatorException); + } elseif (!empty($data['getLayoutUpdateXml'])) { + $this->layoutValidator + ->method('isValid') + ->with($data['getLayoutUpdateXml']) + ->willReturn($isLayoutValid); + } elseif (!empty($data['getCustomLayoutUpdateXml'])) { + $this->layoutValidator + ->method('isValid') + ->with($data['getCustomLayoutUpdateXml']) + ->willReturn($isLayoutValid); + } + + $page = $this->createMock(PageInterface::class); + foreach ($data as $method => $value) { + $page + ->method($method) + ->willReturn($value); + } + + self::assertNull($this->validator->validate($page)); + } + + public function validationSetDataProvider() + { + $layoutError = 'Layout update is invalid'; + $customLayoutError = 'Custom layout update is invalid'; + + return [ + [['getTitle' => ''], 'Required field "title" is empty.', null], + [['getTitle' => null], 'Required field "title" is empty.', null], + [['getTitle' => false], 'Required field "title" is empty.', null], + [['getTitle' => 0], 'Required field "title" is empty.', null], + [['getTitle' => '0'], 'Required field "title" is empty.', null], + [['getTitle' => []], 'Required field "title" is empty.', null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => ''], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => null], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => false], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 0], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => '0'], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => []], null, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, null], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, new ValidationException], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, new SchemaException(__(''))], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], null, null, true], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => ''], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => null], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => false], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 0], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => '0'], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => []], null, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, null], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, new ValidationException], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, new SchemaException(__(''))], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], null, null, true], + ]; + } +} From d7b059f3689ef29ac58cd46eb16140c84e7193e7 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 18 Jun 2019 10:47:08 -0500 Subject: [PATCH 0141/2437] MC-16041: Prevent errors from incorrect configuration --- .../Adminhtml/Page/PostDataProcessor.php | 3 +-- .../Validator/LayoutUpdateValidatorTest.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index f934cac6da11d..46f68955531a3 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -12,8 +12,7 @@ use Magento\Framework\Config\Dom\ValidationSchemaException; /** - * Class PostDataProcessor - * @package Magento\Cms\Controller\Adminhtml\Page + * Processes form data */ class PostDataProcessor { diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php index 8fbea15250732..83db12d77ab3d 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php @@ -6,11 +6,11 @@ declare(strict_types=1); -namespace Magento\Cms\Model\PageRepository\Validator; +namespace Magento\Cms\Test\Unit\Model\PageRepository\Validator; use Magento\Cms\Api\Data\PageInterface; use Magento\Framework\Config\Dom\ValidationException; -use Magento\Framework\Config\Dom\ValidationSchemaException as SchemaException; +use Magento\Framework\Config\Dom\ValidationSchemaException; use Magento\Framework\Config\ValidationStateInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; @@ -88,6 +88,8 @@ public function validationSetDataProvider() { $layoutError = 'Layout update is invalid'; $customLayoutError = 'Custom layout update is invalid'; + $validationException = new ValidationException('Invalid format'); + $schemaException = new ValidationSchemaException(__('Invalid format')); return [ [['getTitle' => ''], 'Required field "title" is empty.', null], @@ -103,8 +105,8 @@ public function validationSetDataProvider() [['getTitle' => 'foo', 'getLayoutUpdateXml' => '0'], null, null], [['getTitle' => 'foo', 'getLayoutUpdateXml' => []], null, null], [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, null], - [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, new ValidationException], - [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, new SchemaException(__(''))], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, $validationException], + [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], $layoutError, $schemaException], [['getTitle' => 'foo', 'getLayoutUpdateXml' => 'foo'], null, null, true], [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => ''], null, null], [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => null], null, null], @@ -113,8 +115,8 @@ public function validationSetDataProvider() [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => '0'], null, null], [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => []], null, null], [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, null], - [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, new ValidationException], - [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, new SchemaException(__(''))], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, $validationException], + [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], $customLayoutError, $schemaException], [['getTitle' => 'foo', 'getCustomLayoutUpdateXml' => 'foo'], null, null, true], ]; } From a3180003aec1eff6dcacc05e5c39fc633496d5bb Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 18 Jun 2019 11:23:02 -0500 Subject: [PATCH 0142/2437] MC-16041: Prevent errors from incorrect configuration --- app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php | 4 ++-- .../Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php index 77f648e5f12c8..2237f35ed0b84 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/InlineEdit.php @@ -84,6 +84,7 @@ public function execute() $page = $this->pageRepository->getById($pageId); try { $pageData = $this->filterPost($postItems[$pageId]); + $this->validatePost($pageData, $page, $error, $messages); $extendedPageData = $page->getData(); $this->setCmsPageData($page, $extendedPageData, $pageData); $this->pageRepository->save($page); @@ -134,11 +135,10 @@ protected function filterPost($postData = []) * @param bool $error * @param array $messages * @return void - * @deprecated */ protected function validatePost(array $pageData, \Magento\Cms\Model\Page $page, &$error, array &$messages) { - if (!($this->dataProcessor->validate($pageData) && $this->dataProcessor->validateRequireEntry($pageData))) { + if (!$this->dataProcessor->validateRequireEntry($pageData)) { $error = true; foreach ($this->messageManager->getMessages(true)->getItems() as $error) { $messages[] = $this->getErrorWithPageId($page, $error->getText()); diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php index 9d51431b26d8f..ea5e8d607f485 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php @@ -102,10 +102,6 @@ public function prepareMocksForTestExecute() ->method('filter') ->with($postData[1]) ->willReturnArgument(0); - $this->dataProcessor->expects($this->once()) - ->method('validate') - ->with($postData[1]) - ->willReturn(false); $this->messageManager->expects($this->once()) ->method('getMessages') ->with(true) From 1d70b8d7c219ae7ba4f3065f0974d2efc7556fe2 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 11 Jun 2019 22:43:11 +0400 Subject: [PATCH 0143/2437] MAGETWO-98703: CSV file is not exported - Added automated test --- ...ageAfterAddingExportingFileToQueueTest.xml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml new file mode 100644 index 0000000000000..f947efff9f2c9 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckTheMessageAfterAddingExportingFileToQueueTest"> + <annotations> + <title value="Check the message after adding exporting file to queue"/> + <description value="Check the message after adding exporting file to queue"/> + <features value="ImportExport"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-17177"/> + <useCaseId value="MAGETWO-98703"/> + <group value="importexport"/> + </annotations> + <before> + <!-- Create category and simple product, then log in --> + <comment userInput="Create category and simple product, then log in" stepKey="createDataAndLogIn"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete exported file, created data and log out --> + <comment userInput="Delete exported file, created data and log out" stepKey="deleteCreatedData"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Go to System -> Data Transfer -> Export page --> + <comment userInput="Go to System -> Data Transfer -> Export page" stepKey="goToAdminExportIndexPage"/> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="navigateToAdminExportIndexPage"/> + <waitForPageLoad stepKey="waitPageToLoad"/> + <!-- Export all products --> + <comment userInput="Export all products" stepKey="exportAllProds"/> + <actionGroup ref="exportAllProducts" stepKey="exportAllProducts"/> + </test> +</tests> From 073ce7843732f336c148249d9457f0850c105c77 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 18 Jun 2019 12:46:40 -0500 Subject: [PATCH 0144/2437] MC-16041: Prevent errors from incorrect configuration --- .../Adminhtml/Page/InlineEditTest.php | 88 +++++++++++-------- .../Validator/LayoutUpdateValidatorTest.php | 1 + 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php index ea5e8d607f485..681e62d159863 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php @@ -118,19 +118,23 @@ public function prepareMocksForTestExecute() ->willReturn('1'); $this->cmsPage->expects($this->atLeastOnce()) ->method('getData') - ->willReturn([ - 'layout' => '1column', - 'identifier' => 'test-identifier' - ]); + ->willReturn( + [ + 'layout' => '1column', + 'identifier' => 'test-identifier' + ] + ); $this->cmsPage->expects($this->once()) ->method('setData') - ->with([ - 'layout' => '1column', - 'title' => '404 Not Found', - 'identifier' => 'no-route', - 'custom_theme' => '1', - 'custom_root_template' => '2' - ]); + ->with( + [ + 'layout' => '1column', + 'title' => '404 Not Found', + 'identifier' => 'no-route', + 'custom_theme' => '1', + 'custom_root_template' => '2' + ] + ); $this->jsonFactory->expects($this->once()) ->method('create') ->willReturn($this->resultJson); @@ -145,13 +149,15 @@ public function testExecuteWithLocalizedException() ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('LocalizedException'))); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [ - '[Page ID: 1] Error message', - '[Page ID: 1] LocalizedException' - ], - 'error' => true - ]) + ->with( + [ + 'messages' => [ + '[Page ID: 1] Error message', + '[Page ID: 1] LocalizedException' + ], + 'error' => true + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); @@ -166,13 +172,15 @@ public function testExecuteWithRuntimeException() ->willThrowException(new \RuntimeException(__('RuntimeException'))); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [ - '[Page ID: 1] Error message', - '[Page ID: 1] RuntimeException' - ], - 'error' => true - ]) + ->with( + [ + 'messages' => [ + '[Page ID: 1] Error message', + '[Page ID: 1] RuntimeException' + ], + 'error' => true + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); @@ -187,13 +195,15 @@ public function testExecuteWithException() ->willThrowException(new \Exception(__('Exception'))); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [ - '[Page ID: 1] Error message', - '[Page ID: 1] Something went wrong while saving the page.' - ], - 'error' => true - ]) + ->with( + [ + 'messages' => [ + '[Page ID: 1] Error message', + '[Page ID: 1] Something went wrong while saving the page.' + ], + + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); @@ -214,12 +224,14 @@ public function testExecuteWithoutData() ); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [ - 'Please correct the data sent.' - ], - 'error' => true - ]) + ->with( + [ + 'messages' => [ + 'Please correct the data sent.' + ], + 'error' => true + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php index 83db12d77ab3d..487a90bb9a185 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/PageRepository/Validator/LayoutUpdateValidatorTest.php @@ -9,6 +9,7 @@ namespace Magento\Cms\Test\Unit\Model\PageRepository\Validator; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\PageRepository\Validator\LayoutUpdateValidator; use Magento\Framework\Config\Dom\ValidationException; use Magento\Framework\Config\Dom\ValidationSchemaException; use Magento\Framework\Config\ValidationStateInterface; From 3333179e9f3bff83e873a353f4e374baa4cc3b0d Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Wed, 19 Jun 2019 14:36:11 +0300 Subject: [PATCH 0145/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Added translation --- app/code/Magento/Sales/i18n/en_US.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index 662781204f78b..a881a59b535fd 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -797,4 +797,4 @@ Created,Created Refunds,Refunds "Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" "Allow Zero GrandTotal","Allow Zero GrandTotal" -"Could not save the shipment tracking" \ No newline at end of file +"Could not save the shipment tracking","Could not save the shipment tracking" \ No newline at end of file From f86eafd52c2a90608c62cfa5569d8abf1092fa2c Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 19 Jun 2019 08:41:24 -0500 Subject: [PATCH 0146/2437] MC-16284: Visible message alert --- .../Magento/Framework/Image/Adapter/AbstractAdapter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php index 276a8a719d6a8..5a2102708b414 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php +++ b/lib/internal/Magento/Framework/Image/Adapter/AbstractAdapter.php @@ -291,7 +291,7 @@ public function getMimeType() if ($this->_fileMimeType) { return $this->_fileMimeType; } else { - $this->_fileMimeType = image_type_to_mime_type($this->getImageType()); + $this->_fileMimeType = image_type_to_mime_type((int) $this->getImageType()); return $this->_fileMimeType; } } @@ -523,7 +523,7 @@ public function backgroundColor($value = null) */ protected function _getFileAttributes() { - $pathinfo = pathinfo($this->_fileName); + $pathinfo = pathinfo((string) $this->_fileName); $this->_fileSrcPath = $pathinfo['dirname']; $this->_fileSrcName = $pathinfo['basename']; @@ -675,7 +675,7 @@ protected function _prepareDestination($destination = null, $newName = null) $destination = $this->_fileSrcPath; } else { if (empty($newName)) { - $info = pathinfo($destination); + $info = pathinfo((string) $destination); $newName = $info['basename']; $destination = $info['dirname']; } From fe7c222607d72226dbc93c56a810d47cdfa16d0e Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 19 Jun 2019 11:49:47 -0500 Subject: [PATCH 0147/2437] MC-15978: Catalog Product Attribute Set Name --- .../Magento/Catalog/Model/Product/AttributeSet/Options.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php index d0c7103851499..26ca1f4d31c73 100644 --- a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php +++ b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php @@ -33,6 +33,10 @@ public function toOptionArray() $this->options = $this->collectionFactory->create() ->setEntityTypeFilter($this->product->getTypeId()) ->toOptionArray(); + + array_walk($this->options, function (&$option) { + $option['__disableTmpl'] = true; + }); } return $this->options; } From 5752b6568517fb707deff7ef3a4dad629d94b005 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 19 Jun 2019 12:03:22 -0500 Subject: [PATCH 0148/2437] MC-15978: Catalog Product Attribute Set Name --- .../Magento/Catalog/Model/Product/AttributeSet/Options.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php index 26ca1f4d31c73..ab3f7b1a84fb8 100644 --- a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php +++ b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\Product\AttributeSet; +/** + * Attribute Set Options + */ class Options implements \Magento\Framework\Data\OptionSourceInterface { /** From b7dbf8991629cfc120f6de237e01056c3362a309 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 19 Jun 2019 14:21:08 -0500 Subject: [PATCH 0149/2437] MC-15978: Catalog Product Attribute Set Name --- .../Catalog/Model/Product/AttributeSet/Options.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php index ab3f7b1a84fb8..57d08916bcd40 100644 --- a/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php +++ b/app/code/Magento/Catalog/Model/Product/AttributeSet/Options.php @@ -11,7 +11,7 @@ class Options implements \Magento\Framework\Data\OptionSourceInterface { /** - * @var null|array + * @var array */ protected $options; @@ -28,7 +28,7 @@ public function __construct( } /** - * @return array|null + * @inheritDoc */ public function toOptionArray() { @@ -37,10 +37,14 @@ public function toOptionArray() ->setEntityTypeFilter($this->product->getTypeId()) ->toOptionArray(); - array_walk($this->options, function (&$option) { - $option['__disableTmpl'] = true; - }); + array_walk( + $this->options, + function (&$option) { + $option['__disableTmpl'] = true; + } + ); } + return $this->options; } } From 4d0ae578c4adb31c2ae51db1102973d20cdc5e9f Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 19 Jun 2019 15:36:03 -0500 Subject: [PATCH 0150/2437] MC-17581: Import Export --- lib/internal/Magento/Framework/Archive/Zip.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Archive/Zip.php b/lib/internal/Magento/Framework/Archive/Zip.php index c41f8b28ce348..eee2a3bf4a55e 100644 --- a/lib/internal/Magento/Framework/Archive/Zip.php +++ b/lib/internal/Magento/Framework/Archive/Zip.php @@ -54,7 +54,7 @@ public function unpack($source, $destination) $zip = new \ZipArchive(); if ($zip->open($source) === true) { $filename = $this->filterRelativePaths($zip->getNameIndex(0) ?: ''); - if ($filename) { + if (!preg_match('#[:"*?|<>%]#', $filename)) { $zip->extractTo(dirname($destination), $filename); rename(dirname($destination).'/'.$filename, $destination); } else { From 0599bb53cfa42bcfc47f4db2cfd1a36fd5a7d8ee Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 19 Jun 2019 15:55:19 -0500 Subject: [PATCH 0151/2437] MC-16724: Prevent errors from incorrect import data --- .../Magento/CatalogImportExport/Model/Import/Product.php | 5 +++-- .../Import/Product/Validator/LayoutUpdatePermissions.php | 5 +---- .../Import/Product/Validator/LayoutUpdatePermissionsTest.php | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 036bd5de0fb27..9088868a595fa 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -301,8 +301,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually', ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => 'Value for multiselect attribute %s contains duplicated values', 'invalidNewToDateValue' => 'Make sure new_to_date is later than or the same as new_from_date', - 'invalidLayoutUpdate' => 'Invalid layout update', - 'insufficientPermissions' => 'You do not have permissions to update "%s"', + // Can't add new translated strings in patch release + 'invalidLayoutUpdate' => 'Invalid format.', + 'insufficientPermissions' => 'Invalid format.', ]; //@codingStandardsIgnoreEnd diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php index d1df04b8e95c8..50d38cedfb754 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/LayoutUpdatePermissions.php @@ -68,10 +68,7 @@ public function isValid($data): bool if (!$isValid) { $this->_addMessages( [ - sprintf( - $this->context->retrieveMessageTemplate(self::ERROR_INSUFFICIENT_PERMISSIONS), - 'custom_layout_update' - ) + $this->context->retrieveMessageTemplate(self::ERROR_INSUFFICIENT_PERMISSIONS), ] ); } diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php index 2f883b5f4146b..e018fc0cf5ccf 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Validator/LayoutUpdatePermissionsTest.php @@ -48,7 +48,7 @@ protected function setUp() $this->context ->method('retrieveMessageTemplate') ->with('insufficientPermissions') - ->willReturn('oh no "%s"'); + ->willReturn('oh no'); $this->validator = new LayoutUpdatePermissions( $this->userContext, $this->authorization @@ -82,7 +82,7 @@ public function testValidationConfiguration($value, $userContext, $isAllowed, $i if ($isValid) { self::assertSame([], $messages); } else { - self::assertSame(['oh no "custom_layout_update"'], $messages); + self::assertSame(['oh no'], $messages); } } From cb86e985f7067679fdfd10b9d526cf473e66629d Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 20 Jun 2019 10:09:15 +0300 Subject: [PATCH 0152/2437] MC-17303: Customer login fix --- app/code/Magento/Customer/Controller/Account/LoginPost.php | 1 - app/code/Magento/Customer/Controller/Ajax/Login.php | 1 - app/code/Magento/Customer/Model/Session.php | 1 + app/code/Magento/Rss/Controller/Feed.php | 1 - 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 48221b00bbb4d..4091a068e3094 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -184,7 +184,6 @@ public function execute() if (!empty($login['username']) && !empty($login['password'])) { try { $customer = $this->customerAccountManagement->authenticate($login['username'], $login['password']); - $this->session->regenerateId(); $this->session->setCustomerDataAsLoggedIn($customer); if ($this->getCookieManager()->getCookie('mage-cache-sessid')) { $metadata = $this->getCookieMetadataFactory()->createCookieMetadata(); diff --git a/app/code/Magento/Customer/Controller/Ajax/Login.php b/app/code/Magento/Customer/Controller/Ajax/Login.php index d5425bfc27332..1215c47ab9902 100644 --- a/app/code/Magento/Customer/Controller/Ajax/Login.php +++ b/app/code/Magento/Customer/Controller/Ajax/Login.php @@ -190,7 +190,6 @@ public function execute() $credentials['username'], $credentials['password'] ); - $this->customerSession->regenerateId(); $this->customerSession->setCustomerDataAsLoggedIn($customer); $redirectRoute = $this->getAccountRedirect()->getRedirectCookie(); if ($this->cookieManager->getCookie('mage-cache-sessid')) { diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php index e6612fe77a9bc..ca15b8ab2034a 100644 --- a/app/code/Magento/Customer/Model/Session.php +++ b/app/code/Magento/Customer/Model/Session.php @@ -431,6 +431,7 @@ public function setCustomerAsLoggedIn($customer) */ public function setCustomerDataAsLoggedIn($customer) { + $this->regenerateId(); $this->_httpContext->setValue(Context::CONTEXT_AUTH, true, false); $this->setCustomerData($customer); diff --git a/app/code/Magento/Rss/Controller/Feed.php b/app/code/Magento/Rss/Controller/Feed.php index c3ef4fb80833e..4c5acf97bde8c 100644 --- a/app/code/Magento/Rss/Controller/Feed.php +++ b/app/code/Magento/Rss/Controller/Feed.php @@ -86,7 +86,6 @@ protected function auth() list($login, $password) = $this->httpAuthentication->getCredentials(); try { $customer = $this->customerAccountManagement->authenticate($login, $password); - $this->customerSession->regenerateId(); $this->customerSession->setCustomerDataAsLoggedIn($customer); } catch (\Exception $e) { $this->logger->critical($e); From ab4641cc603e76a63b2768679de2e08dc55081c3 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 20 Jun 2019 12:24:35 +0300 Subject: [PATCH 0153/2437] MC-17303: Customer login fix --- .../Test/Unit/Controller/Account/LoginPostTest.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 762c76b695dee..78acc339284c2 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -292,9 +292,8 @@ public function testExecuteSuccessCustomRedirect() ->method('setCustomerDataAsLoggedIn') ->with($customerMock) ->willReturnSelf(); - $this->session->expects($this->once()) - ->method('regenerateId') - ->willReturnSelf(); + $this->session->expects($this->never()) + ->method('regenerateId'); $this->accountRedirect->expects($this->never()) ->method('getRedirect') @@ -357,9 +356,8 @@ public function testExecuteSuccess() ->method('setCustomerDataAsLoggedIn') ->with($customerMock) ->willReturnSelf(); - $this->session->expects($this->once()) - ->method('regenerateId') - ->willReturnSelf(); + $this->session->expects($this->never()) + ->method('regenerateId'); $this->accountRedirect->expects($this->once()) ->method('getRedirect') From 492a377cf660964b11d512b9cec26df8f5b88310 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 20 Jun 2019 13:10:49 +0300 Subject: [PATCH 0154/2437] MC-16042: Zip archive validation --- .../Magento/Framework/Archive/Zip.php | 35 ++----------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/lib/internal/Magento/Framework/Archive/Zip.php b/lib/internal/Magento/Framework/Archive/Zip.php index 925adda48504c..20f408a605c10 100644 --- a/lib/internal/Magento/Framework/Archive/Zip.php +++ b/lib/internal/Magento/Framework/Archive/Zip.php @@ -53,11 +53,10 @@ public function unpack($source, $destination) { $zip = new \ZipArchive(); if ($zip->open($source) === true) { - $filename = $this->filterRelativePaths($zip->getNameIndex(0) ?: ''); - $filename = $this->filterExcludedFiles($filename); + $zip->renameIndex(0, basename($destination)); + $filename = $zip->getNameIndex(0) ?: ''; if ($filename) { $zip->extractTo(dirname($destination), $filename); - rename(dirname($destination).'/'.$filename, $destination); } else { $destination = ''; } @@ -68,34 +67,4 @@ public function unpack($source, $destination) return $destination; } - - /** - * Filter file names with relative paths. - * - * @param string $path - * @return string - */ - private function filterRelativePaths(string $path): string - { - if ($path && preg_match('#^\s*(../)|(/../)#i', $path)) { - $path = ''; - } - - return $path; - } - - /** - * Filter excluded files. - * - * @param string $file - * @return string - */ - private function filterExcludedFiles(string $file): string - { - if ($file && preg_match('/^\.htaccess$/', $file)) { - $file = ''; - } - - return $file; - } } From 5aa361068d0f5722d4d4587e66255ff0977b2db2 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 20 Jun 2019 12:11:48 -0500 Subject: [PATCH 0155/2437] MC-17581: Import Export --- app/code/Magento/ImportExport/Model/Import/Source/Zip.php | 8 +++++++- lib/internal/Magento/Framework/Archive/Zip.php | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ImportExport/Model/Import/Source/Zip.php b/app/code/Magento/ImportExport/Model/Import/Source/Zip.php index 6fa87ab5d5c4d..7e69d837d6526 100644 --- a/app/code/Magento/ImportExport/Model/Import/Source/Zip.php +++ b/app/code/Magento/ImportExport/Model/Import/Source/Zip.php @@ -33,6 +33,12 @@ public function __construct( throw new ValidatorException(__('Sorry, but the data is invalid or the file is not uploaded.')); } $directory->delete($directory->getRelativePath($file)); - parent::__construct($csvFile, $directory, $options); + + try { + parent::__construct($csvFile, $directory, $options); + } catch (\LogicException $e) { + $directory->delete($directory->getRelativePath($csvFile)); + throw $e; + } } } diff --git a/lib/internal/Magento/Framework/Archive/Zip.php b/lib/internal/Magento/Framework/Archive/Zip.php index eee2a3bf4a55e..2cb6e47d39426 100644 --- a/lib/internal/Magento/Framework/Archive/Zip.php +++ b/lib/internal/Magento/Framework/Archive/Zip.php @@ -54,7 +54,8 @@ public function unpack($source, $destination) $zip = new \ZipArchive(); if ($zip->open($source) === true) { $filename = $this->filterRelativePaths($zip->getNameIndex(0) ?: ''); - if (!preg_match('#[:"*?|<>%]#', $filename)) { + if ($filename && !preg_match('#[:"*?|<>%]#', $filename)) { + // extract first entry in zip file to destination directory $zip->extractTo(dirname($destination), $filename); rename(dirname($destination).'/'.$filename, $destination); } else { From dedd073a80bebb83233c6d937c0f3cee61d74612 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 21 Jun 2019 12:26:24 +0300 Subject: [PATCH 0156/2437] MC-17650: Change Confirmation key generation --- app/code/Magento/Customer/Model/Customer.php | 10 +++++-- .../Customer/Test/Unit/Model/CustomerTest.php | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index 1287dbe5df708..32b0a9c1ec481 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -20,6 +20,7 @@ use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Math\Random; /** * Customer model @@ -180,7 +181,7 @@ class Customer extends \Magento\Framework\Model\AbstractModel protected $_encryptor; /** - * @var \Magento\Framework\Math\Random + * @var Random */ protected $mathRandom; @@ -248,6 +249,7 @@ class Customer extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param AccountConfirmation|null $accountConfirmation + * @param Random|null $mathRandom * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -272,7 +274,8 @@ public function __construct( \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - AccountConfirmation $accountConfirmation = null + AccountConfirmation $accountConfirmation = null, + Random $mathRandom = null ) { $this->metadataService = $metadataService; $this->_scopeConfig = $scopeConfig; @@ -291,6 +294,7 @@ public function __construct( $this->indexerRegistry = $indexerRegistry; $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance() ->get(AccountConfirmation::class); + $this->mathRandom = $mathRandom ?: ObjectManager::getInstance()->get(Random::class); parent::__construct( $context, $registry, @@ -805,7 +809,7 @@ public function isConfirmationRequired() */ public function getRandomConfirmationKey() { - return md5(uniqid()); + return $this->mathRandom->getRandomString(32); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php index 65831069aa1fb..8bb37adc875cc 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php @@ -15,6 +15,7 @@ use Magento\Customer\Model\AccountConfirmation; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory as AddressCollectionFactory; use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Framework\Math\Random; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -86,6 +87,14 @@ class CustomerTest extends \PHPUnit\Framework\TestCase */ private $dataObjectHelper; + /** + * @var Random|\PHPUnit_Framework_MockObject_MockObject + */ + private $mathRandom; + + /** + * @inheritdoc + */ protected function setUp() { $this->_website = $this->createMock(\Magento\Store\Model\Website::class); @@ -130,6 +139,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['populateWithArray']) ->getMock(); + $this->mathRandom = $this->createMock(Random::class); $this->_model = $helper->getObject( \Magento\Customer\Model\Customer::class, @@ -146,7 +156,8 @@ protected function setUp() 'accountConfirmation' => $this->accountConfirmation, '_addressesFactory' => $this->addressesFactory, 'customerDataFactory' => $this->customerDataFactory, - 'dataObjectHelper' => $this->dataObjectHelper + 'dataObjectHelper' => $this->dataObjectHelper, + 'mathRandom' => $this->mathRandom, ] ); } @@ -383,4 +394,20 @@ public function testGetDataModel() $this->_model->getDataModel(); $this->assertEquals($customerDataObject, $this->_model->getDataModel()); } + + /** + * Check getRandomConfirmationKey use cryptographically secure function + * + * @return void + */ + public function testGetRandomConfirmationKey() : void + { + $this->mathRandom + ->expects($this->once()) + ->method('getRandomString') + ->with(32) + ->willReturn('random_string'); + + $this->_model->getRandomConfirmationKey(); + } } From fe9deec39afeab1a30cb4a7c2507b754fa2c6930 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 21 Jun 2019 15:09:53 +0300 Subject: [PATCH 0157/2437] MC-17650: Change Confirmation key generation --- .../Customer/Test/Unit/Model/CustomerTest.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php index 8bb37adc875cc..170cd001e5b9e 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php @@ -230,15 +230,17 @@ public function testSendNewAccountEmailWithoutStoreId() ->method('getTransport') ->will($this->returnValue($transportMock)); - $this->_model->setData([ - 'website_id' => 1, - 'store_id' => 1, - 'email' => 'email@example.com', - 'firstname' => 'FirstName', - 'lastname' => 'LastName', - 'middlename' => 'MiddleName', - 'prefix' => 'Name Prefix', - ]); + $this->_model->setData( + [ + 'website_id' => 1, + 'store_id' => 1, + 'email' => 'email@example.com', + 'firstname' => 'FirstName', + 'lastname' => 'LastName', + 'middlename' => 'MiddleName', + 'prefix' => 'Name Prefix', + ] + ); $this->_model->sendNewAccountEmail('registered'); } From ce3e108be879190254b2d5ae38b0125ca73abae6 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Fri, 21 Jun 2019 11:41:15 -0500 Subject: [PATCH 0158/2437] MC-17648: Registration email confirmation --- app/code/Magento/Customer/Model/AccountManagement.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index cfb788a2516a2..e030f2107356b 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -977,6 +977,7 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU $templateType = self::NEW_ACCOUNT_EMAIL_REGISTERED_NO_PASSWORD; } $this->getEmailNotification()->newAccount($customer, $templateType, $redirectUrl, $customer->getStoreId()); + $customer->setConfirmation(null); } catch (MailException $e) { // If we are not able to send a new account email, this should be ignored $this->logger->critical($e); From f358005a86a3face6392fc0f53d623888e75e532 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Sun, 23 Jun 2019 21:42:19 +0300 Subject: [PATCH 0159/2437] Add test for admin user role update functionality --- .../AssertAdminUserIsInGridActionGroup.xml | 14 +++++ ...ertUserRoleRestrictedAccessActionGroup.xml | 15 +++++ ...ssertUserSuccessSaveMessageActionGroup.xml | 14 +++++ .../AdminDeleteUserRoleActionGroup.xml | 18 ++++++ .../AdminNavigateToUserRolesActionGroup.xml | 16 +++++ .../AdminOpenUserEditPageActionGroup.xml | 19 ++++++ .../AdminUpdateUserRoleActionGroup.xml | 21 +++++++ .../User/Test/Mftf/Data/UserRoleData.xml | 5 ++ .../Mftf/Section/AdminDeleteRoleSection.xml | 3 + .../Mftf/Section/AdminUserGridSection.xml | 1 + .../Mftf/Test/AdminUpdateUserRoleTest.xml | 60 +++++++++++++++++++ 11 files changed, 186 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml new file mode 100644 index 0000000000000..bb73606a92b93 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminUserIsInGridActionGroup"> + <seeElement selector="{{AdminUserGridSection.theUser}}" stepKey="seeAdminUserInGrid"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml new file mode 100644 index 0000000000000..b98731e117e1f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertUserRoleRestrictedAccessActionGroup"> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> + <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml new file mode 100644 index 0000000000000..2d451875c6358 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertUserSuccessSaveMessageActionGroup"> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the user." stepKey="seeAdminUserInGrid"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml new file mode 100644 index 0000000000000..24d652313c544 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteUserRoleActionGroup"> + <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.salesRole}}"/> + <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/> + <waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml new file mode 100644 index 0000000000000..d86bc7d11dbbf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToUserRolesActionGroup"> + <click selector="#menu-magento-backend-system" stepKey="clickOnSystemIcon"/> + <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> + <click selector="//span[contains(text(), 'User Roles')]" stepKey="clickToSelectUserRoles"/> + <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml new file mode 100644 index 0000000000000..ff7871cc1d5fe --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenUserEditPageActionGroup"> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="enterUserName" /> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="seeUser" /> + <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> + <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml new file mode 100644 index 0000000000000..c7f5078832cf5 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateUserRoleActionGroup"> + <arguments> + <argument name="roleName" type="string" defaultValue="Sales"/> + </arguments> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> + <click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="openUserRoleTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened" time="15"/> + <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.userRole(roleName)}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 1c18ca13b9731..3f437e4c0ad8f 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -24,4 +24,9 @@ <data key="resourceAccess">Custom</data> <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> + <entity name="salesRole" type="role"> + <data key="name" unique="suffix">Sales</data> + <data key="resourceAccess">Custom</data> + <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails']</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml index 1b55d09d0597e..0ddf5010432ce 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml @@ -10,8 +10,11 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDeleteRoleSection"> <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> + <element name="salesRole" selector="//td[contains(text(), 'Sales')]" type="button"/> + <element name="userRole" selector="//td[contains(text(), '{{userRole}}')]" type="button" parameterized="true"/> <element name="current_pass" type="button" selector="#current_password"/> <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> </section> </sections> + diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml index c21a8b875e95b..ad2be384e0bad 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml @@ -13,6 +13,7 @@ <element name="usernameInFirstRow" type="text" selector=".col-username"/> <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> <element name="successMessage" type="text" selector=".message-success"/> + <element name="theUser" selector="//td[contains(text(), '{{_ENV.MAGENTO_ADMIN_USERNAME}}')]" type="button"/> </section> <section name="AdminDeleteUserSection"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml new file mode 100644 index 0000000000000..b528b907867ab --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateUserRoleTest"> + <annotations> + <features value="User"/> + <title value="Admin user role update"/> + <description value="Admin user with full access role should be able to change the role to custom one with restricted permission"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Create New Role--> + <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="openUserRolesGrig"/> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="salesRole"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRole"/> + + <!--Assign the new role--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openAdminUserEditPage"/> + <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"/> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> + <actionGroup ref="AssertUserSuccessSaveMessageActionGroup" stepKey="seeSuccessSaveUserMessage"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"/> + + <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> + <actionGroup ref="LoginAsAdmin" stepKey="logInToAdminPanel"/> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> + + + <!--Unassign the new role + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="goBackToUserEditPage"/> + <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="backToDefaultUserRole"> + <argument name="roleName" value="Admin"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUserWithOldRole"/>--> + + <!--Delete Role + <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="goBackToUserRolesGrig"/> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteRole"/>--> + + </test> + </tests> \ No newline at end of file From e3a482370db7f9784fa4a4e353a80098cc183d9e Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Mon, 24 Jun 2019 10:59:28 +0300 Subject: [PATCH 0160/2437] Code refactoring of AdminUpdateUserRoleTest --- .../AssertAdminUserIsInGridActionGroup.xml | 10 ++++- .../AdminOpenUserEditPageActionGroup.xml | 7 ++- .../AdminUpdateUserRoleActionGroup.xml | 19 +++++--- .../Mftf/Section/AdminUserGridSection.xml | 1 - .../Mftf/Test/AdminUpdateUserRoleTest.xml | 45 ++++++++++++------- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml index bb73606a92b93..01e6b67b59a6c 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml @@ -9,6 +9,14 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertAdminUserIsInGridActionGroup"> - <seeElement selector="{{AdminUserGridSection.theUser}}" stepKey="seeAdminUserInGrid"/> + <arguments> + <argument name="user" type="entity"/> + </arguments> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter" /> + <waitForPageLoad stepKey="waitForFiltersReset" time="15"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml index ff7871cc1d5fe..a32027a6ac182 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml @@ -8,11 +8,14 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenUserEditPageActionGroup"> + <arguments> + <argument name="user" type="entity"/> + </arguments> <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="enterUserName" /> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="seeUser" /> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> </actionGroup> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml index c7f5078832cf5..313bf0b215d68 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml @@ -9,13 +9,18 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminUpdateUserRoleActionGroup"> <arguments> - <argument name="roleName" type="string" defaultValue="Sales"/> + <argument name="role" type="entity"/> </arguments> - <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> - <scrollToTopOfPage stepKey="scrollToTop"/> - <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> - <click selector="{{AdminEditUserSection.userRoleTab}}" stepKey="openUserRoleTab"/> - <waitForPageLoad stepKey="waitForUserRoleTabOpened" time="15"/> - <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.userRole(roleName)}}"/> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> + <click selector="{{AdminNewUserFormSection.userRoleTab}}" stepKey="openUserRoleTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened" /> + <click selector="{{AdminNewUserFormSection.resetFilter}}" stepKey="resetGridFilter" /> + <waitForPageLoad stepKey="waitForFiltersReset" /> + <fillField selector="{{AdminNewUserFormSection.roleFilterField}}" userInput="{{role.name}}" stepKey="fillRoleFilterField" /> + <click selector="{{AdminNewUserFormSection.search}}" stepKey="clickSearchButton" /> + <waitForPageLoad stepKey="waitForFiltersApplied" /> + <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(role.name)}}" stepKey="assignRole"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml index ad2be384e0bad..c21a8b875e95b 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminUserGridSection.xml @@ -13,7 +13,6 @@ <element name="usernameInFirstRow" type="text" selector=".col-username"/> <element name="searchResultFirstRow" type="text" selector=".data-grid>tbody>tr"/> <element name="successMessage" type="text" selector=".message-success"/> - <element name="theUser" selector="//td[contains(text(), '{{_ENV.MAGENTO_ADMIN_USERNAME}}')]" type="button"/> </section> <section name="AdminDeleteUserSection"> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml index b528b907867ab..35f4a6e133c95 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml @@ -24,6 +24,14 @@ <actionGroup ref="logout" stepKey="logOut"/> </after> + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="NewAdminUser" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <!--Create New Role--> <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="openUserRolesGrig"/> <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> @@ -32,29 +40,36 @@ </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRole"/> - <!--Assign the new role--> - <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openAdminUserEditPage"/> - <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"/> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> - <actionGroup ref="AssertUserSuccessSaveMessageActionGroup" stepKey="seeSuccessSaveUserMessage"/> - <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"/> + <!--Assign new role--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"> + <argument name="role" value="salesRole"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> + <actionGroup ref="AssertUserSuccessSaveMessageActionGroup" stepKey="seeSuccessSaveUserMessage"/> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> - <actionGroup ref="LoginAsAdmin" stepKey="logInToAdminPanel"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> + <argument name="adminUser" value="NewAdminUser"/> + </actionGroup> <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> - - <!--Unassign the new role - <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="goBackToUserEditPage"/> - <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="backToDefaultUserRole"> - <argument name="roleName" value="Admin"/> + <!--Delete new User--> + <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="NewAdminUser"/> </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUserWithOldRole"/>--> - <!--Delete Role + <!--Delete Role--> <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="goBackToUserRolesGrig"/> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteRole"/>--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"/> </test> </tests> \ No newline at end of file From 69cec9091e52bdfd15ed65c4903fe890dca393fe Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Mon, 24 Jun 2019 15:09:20 +0300 Subject: [PATCH 0161/2437] MC-17303: Customer login fix --- .../Unit/Controller/Account/LoginPostTest.php | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 78acc339284c2..13cf195ab5f69 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -93,13 +93,14 @@ protected function setUp() $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'setCustomerDataAsLoggedIn', - 'regenerateId', - 'setUsername', - ]) - ->getMock(); + ->setMethods( + [ + 'isLoggedIn', + 'setCustomerDataAsLoggedIn', + 'regenerateId', + 'setUsername', + ] + )->getMock(); $this->accountManagement = $this->getMockBuilder(\Magento\Customer\Api\AccountManagementInterface::class) ->getMockForAbstractClass(); @@ -253,10 +254,12 @@ public function testExecuteSuccessCustomRedirect() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -334,10 +337,12 @@ public function testExecuteSuccess() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -424,10 +429,12 @@ public function testExecuteWithException( $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $exception = new $exceptionData['exception'](__($exceptionData['message'])); @@ -486,11 +493,12 @@ protected function prepareContext() $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() - ->setMethods([ - 'isPost', - 'getPost', - ]) - ->getMock(); + ->setMethods( + [ + 'isPost', + 'getPost', + ] + )->getMock(); $this->resultRedirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) ->disableOriginalConstructor() From 983e5fbd97f36bd22cc4491b676ccb46b769557b Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Mon, 24 Jun 2019 17:43:48 +0300 Subject: [PATCH 0162/2437] Code refactoring of AdminUpdateUserRoleTest --- .../Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml | 1 - .../Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml index 0ddf5010432ce..a3a82f6ce38e0 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml @@ -11,7 +11,6 @@ <section name="AdminDeleteRoleSection"> <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> <element name="salesRole" selector="//td[contains(text(), 'Sales')]" type="button"/> - <element name="userRole" selector="//td[contains(text(), '{{userRole}}')]" type="button" parameterized="true"/> <element name="current_pass" type="button" selector="#current_password"/> <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml index 35f4a6e133c95..3e7bb3e1889ef 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml @@ -12,7 +12,7 @@ <annotations> <features value="User"/> <title value="Admin user role update"/> - <description value="Admin user with full access role should be able to change the role to custom one with restricted permission"/> + <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> <group value="user"/> <group value="mtf_migrated"/> </annotations> @@ -64,7 +64,7 @@ <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> - <argument name="user" value="NewAdminUser"/> + <argument name="user" value="NewAdminUser"/> </actionGroup> <!--Delete Role--> From 6f33211850d2c2666b99278b23200cd542ae20d2 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 24 Jun 2019 16:23:19 -0500 Subject: [PATCH 0163/2437] MC-16284: Visible message alert --- .../Adminhtml/Product/Gallery/Upload.php | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index ff7311e931755..779617254f6ad 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -8,6 +8,7 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterface { @@ -23,6 +24,16 @@ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterf */ protected $resultRawFactory; + /** + * @var array + */ + private $allowedMimeTypes = [ + 'jpg' => 'image/jpg', + 'jpeg' => 'image/jpeg', + 'gif' => 'image/png', + 'png' => 'image/gif' + ]; + /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory @@ -45,7 +56,12 @@ public function execute() \Magento\MediaStorage\Model\File\Uploader::class, ['fileId' => 'image'] ); - $uploader->setAllowedExtensions(['jpg', 'jpeg', 'gif', 'png']); + $uploader->setAllowedExtensions($this->getAllowedExtensions()); + + if (!$uploader->checkMimeType($this->getAllowedMimeTypes())) { + throw new LocalizedException(__('Disallowed File Type.')); + } + /** @var \Magento\Framework\Image\Adapter\AdapterInterface $imageAdapter */ $imageAdapter = $this->_objectManager->get(\Magento\Framework\Image\AdapterFactory::class)->create(); $uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile'); @@ -78,4 +94,24 @@ public function execute() $response->setContents(json_encode($result)); return $response; } + + /** + * Get the set of allowed file extensions. + * + * @return array + */ + private function getAllowedExtensions() + { + return array_keys($this->allowedMimeTypes); + } + + /** + * Get the set of allowed mime types. + * + * @return array + */ + private function getAllowedMimeTypes() + { + return array_values($this->allowedMimeTypes); + } } From 31caade6fa679b6c492120cd73ef3d0c50f9b00f Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Mon, 24 Jun 2019 16:39:30 -0500 Subject: [PATCH 0164/2437] MC-16284: Visible message alert --- .../Catalog/Controller/Adminhtml/Product/Gallery/Upload.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index 779617254f6ad..820f2e5e5b8cd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -10,6 +9,9 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\LocalizedException; +/** + * Class Upload + */ class Upload extends \Magento\Backend\App\Action implements HttpPostActionInterface { /** @@ -47,6 +49,8 @@ public function __construct( } /** + * Upload image(s) to the product gallery. + * * @return \Magento\Framework\Controller\Result\Raw */ public function execute() From 7e3bc32a1fdbbebf7557e6e282bc8f291c305748 Mon Sep 17 00:00:00 2001 From: Mark Berube <berube@adobe.com> Date: Mon, 24 Jun 2019 17:45:41 -0500 Subject: [PATCH 0165/2437] MC-16110: Fixing column name for store --- .../Listing/Column/Store/Options.php | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php b/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php index 1fc13390e30b5..907eb74e20fa2 100644 --- a/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php +++ b/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php @@ -68,6 +68,26 @@ public function toOptionArray() return $this->options; } + /** + * Sanitize website/store option name + * + * @param string $name + * + * @return string + */ + protected function sanitizeName($name) + { + $matches = []; + preg_match('/\$[:]*{(.)*}/', $name, $matches); + if (count($matches) > 0) { + $name = $this->escaper->escapeHtml($this->escaper->escapeJs($name)); + } else { + $name = $this->escaper->escapeHtml($name); + } + + return $name; + } + /** * Generate current options * @@ -88,20 +108,20 @@ protected function generateCurrentOptions() /** @var \Magento\Store\Model\Store $store */ foreach ($storeCollection as $store) { if ($store->getGroupId() == $group->getId()) { - $name = $this->escaper->escapeHtml($store->getName()); + $name = $this->sanitizeName($store->getName()); $stores[$name]['label'] = str_repeat(' ', 8) . $name; $stores[$name]['value'] = $store->getId(); } } if (!empty($stores)) { - $name = $this->escaper->escapeHtml($group->getName()); + $name = $this->sanitizeName($group->getName()); $groups[$name]['label'] = str_repeat(' ', 4) . $name; $groups[$name]['value'] = array_values($stores); } } } if (!empty($groups)) { - $name = $this->escaper->escapeHtml($website->getName()); + $name = $this->sanitizeName($website->getName()); $this->currentOptions[$name]['label'] = $name; $this->currentOptions[$name]['value'] = array_values($groups); } From 6db05a87e98474826e406890ef9d0643e279d568 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 24 Jun 2019 23:00:18 -0500 Subject: [PATCH 0166/2437] MC-16724: Prevent errors from incorrect import data --- .../Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php index 681e62d159863..7f2ff2086df91 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/InlineEditTest.php @@ -201,7 +201,7 @@ public function testExecuteWithException() '[Page ID: 1] Error message', '[Page ID: 1] Something went wrong while saving the page.' ], - + 'error' => true ] ) ->willReturnSelf(); From 2ea850138272a59bc6acc116ff2ac9dbc413f8cb Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 25 Jun 2019 08:29:57 +0300 Subject: [PATCH 0167/2437] Covering the create backend order getting an out of stock product --- ...sertAdminProductStockStatusActionGroup.xml | 19 +++++++ .../Catalog/Test/Mftf/Data/ProductData.xml | 3 ++ .../Data/ProductExtensionAttributeData.xml | 3 ++ .../Catalog/Test/Mftf/Data/StockItemData.xml | 4 ++ .../AdminSelectPaymentMethodActionGroup.xml | 20 +++++++ .../Test/Mftf/Data/PaymentMethodData.xml | 4 ++ .../Mftf/Metadata/payment_method-meta.xml | 14 +++++ .../AdminCreateOrderWithSimpleProductTest.xml | 54 +++++++++++++++++++ .../Test/TestCase/CreateOrderBackendTest.xml | 1 + 9 files changed, 122 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml create mode 100644 app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml new file mode 100644 index 0000000000000..56c088887cd4b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminProductStockStatusActionGroup"> + <arguments> + <argument name="productId" type="string"/> + <argument name="stockStatus" type="string"/> + </arguments> + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProductEditPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <seeOptionIsSelected selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{stockStatus}}" stepKey="checkProductStatus" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 33dab7ee8fd7e..f882a3cfe9900 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -39,6 +39,9 @@ <data key="name">Pursuit Lumaflex&trade; Tone Band</data> <data key="sku" unique="suffix">x&trade;</data> </entity> + <entity name="SimpleProduct_25" type="product" extends="SimpleProduct2"> + <requiredEntity type="product_extension_attribute">EavStock25</requiredEntity> + </entity> <entity name="ApiSimpleProductWithCustomPrice" type="product" extends="ApiSimpleProduct"> <data key="price">100</data> </entity> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml index 5a6a0b5dd9518..2576d8cdd7022 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml @@ -26,4 +26,7 @@ <entity name="EavStock777" type="product_extension_attribute"> <requiredEntity type="stock_item">Qty_777</requiredEntity> </entity> + <entity name="EavStock25" type="product_extension_attribute"> + <requiredEntity type="stock_item">Qty_25</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml index 32f4dc1404dd7..bef0b9f56eab6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml @@ -40,4 +40,8 @@ <data key="qty">777</data> <data key="is_in_stock">true</data> </entity> + <entity name="Qty_25" type="stock_item"> + <data key="qty">25</data> + <data key="is_in_stock">true</data> + </entity> </entities> diff --git a/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml b/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..aabb7245492f8 --- /dev/null +++ b/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectPaymentMethodActionGroup"> + <arguments> + <argument name="paymentMethod" type="string"/> + <argument name="paymentTitle" type="string"/> + </arguments> + <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> + <waitForPageLoad stepKey="waitForJavascriptToFinish"/> + <selectOption selector="{{paymentMethod}}" userInput="{{paymentTitle}}" stepKey="checkPaymentMethod"/> + <waitForPageLoad stepKey="waitForApplyingPaymentMethod"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml index 14c8bd0fecde7..eeae53d082443 100644 --- a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml +++ b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml @@ -11,4 +11,8 @@ <entity name="PaymentMethodCheckMoneyOrder" type="payment_method"> <data key="method">checkmo</data> </entity> + + <entity name="CashOnDeliveryPaymentMethodDefault" type="cashondelivery_payment_method"> + <requiredEntity type="active">CashOnDeliveryEnableConfigData</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml index 39506a682038f..3ad3a0e1c60de 100644 --- a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml +++ b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml @@ -11,4 +11,18 @@ <operation name="CreatePaymentMethod" dataType="payment_method" type="create"> <field key="method">string</field> </operation> + <operation name="cashondeliveryPaymentMethodSetup" dataType="cashondelivery_payment_method" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> + <object key="groups" dataType="cashondelivery_payment_method"> + <object key="cashondelivery" dataType="cashondelivery_payment_method"> + <object key="fields" dataType="cashondelivery_payment_method"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + <object key="title" dataType="title"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> </operations> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml new file mode 100644 index 0000000000000..86dcc227946b5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithSimpleProductTest"> + <annotations> + <title value="Create Order in Admin with simple product"/> + <stories value="MAGETWO-12798: Create order with condition available product qty = ordered product qty"/> + <description value="Create order with simple product and assert if it gets out of stock after ordering it."/> + <features value="Sales"/> + <severity value="AVERAGE"/> + <group value="Sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + <createData entity="CashOnDeliveryPaymentMethodDefault" stepKey="cashOnDeliveryPaymentMethod"/> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + <createData entity="SimpleProduct_25" stepKey="simpleProduct"> + <field key="price">5</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="25"/> + </actionGroup> + <actionGroup ref="AdminSelectPaymentMethodActionGroup" stepKey="selectPaymentMethod"> + <argument name="paymentMethod" value="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}"/> + <argument name="paymentTitle" value="$$cashOnDeliveryPaymentMethod.title$$"/> + </actionGroup> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="AssertAdminProductStockStatusActionGroup" stepKey="checkProductStockStatus"> + <argument name="productId" value="$$simpleProduct.id$$"/> + <argument name="stockStatus" value="Out of Stock"/> + </actionGroup> + <after> + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + </after> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml index c4e03b94d2ada..408b0fdcbb40f 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\CreateOrderBackendTest" summary="Create Order from Admin within Offline Payment Methods" ticketId="MAGETWO-28696"> <variation name="CreateOrderBackendTestVariation18" summary="Create order with condition available product qty = ordered product qty" ticketId="MAGETWO-12798"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">catalogProductSimple::product_with_qty_25</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> From 97927cd29832fdc114576bf9d95584ee07bdeaa0 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 25 Jun 2019 09:33:12 +0300 Subject: [PATCH 0168/2437] Reusing existing ActionGroup for selecting the cash on delivery method --- .../AdminSelectPaymentMethodActionGroup.xml | 20 ------------------- .../AdminCreateOrderWithSimpleProductTest.xml | 5 +---- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml diff --git a/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml b/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml deleted file mode 100644 index aabb7245492f8..0000000000000 --- a/app/code/Magento/Payment/Test/Mftf/ActionGroup/AdminSelectPaymentMethodActionGroup.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSelectPaymentMethodActionGroup"> - <arguments> - <argument name="paymentMethod" type="string"/> - <argument name="paymentTitle" type="string"/> - </arguments> - <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> - <waitForPageLoad stepKey="waitForJavascriptToFinish"/> - <selectOption selector="{{paymentMethod}}" userInput="{{paymentTitle}}" stepKey="checkPaymentMethod"/> - <waitForPageLoad stepKey="waitForApplyingPaymentMethod"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml index 86dcc227946b5..041fed644f859 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -34,10 +34,7 @@ <argument name="product" value="$$simpleProduct$$"/> <argument name="productQty" value="25"/> </actionGroup> - <actionGroup ref="AdminSelectPaymentMethodActionGroup" stepKey="selectPaymentMethod"> - <argument name="paymentMethod" value="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}"/> - <argument name="paymentTitle" value="$$cashOnDeliveryPaymentMethod.title$$"/> - </actionGroup> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod" /> <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> From 2b70fe8e112f538ead0e8af519f9ee136803ba58 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 25 Jun 2019 14:35:33 +0400 Subject: [PATCH 0169/2437] MAGETWO-98703: CSV file is not exported - Added validation message to 'exportAllProducts' actionGroup --- .../ActionGroup/AdminExportActionGroup.xml | 2 +- ...ageAfterAddingExportingFileToQueueTest.xml | 45 ------------------- 2 files changed, 1 insertion(+), 46 deletions(-) delete mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml index 65588daa96cc4..d628667df5937 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml @@ -34,7 +34,7 @@ <wait stepKey="waitForScroll" time="5"/> <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> <wait stepKey="waitForClick" time="5"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file" stepKey="seeSuccessMessage"/> </actionGroup> <!-- Download first file in the grid --> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml deleted file mode 100644 index f947efff9f2c9..0000000000000 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckTheMessageAfterAddingExportingFileToQueueTest.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckTheMessageAfterAddingExportingFileToQueueTest"> - <annotations> - <title value="Check the message after adding exporting file to queue"/> - <description value="Check the message after adding exporting file to queue"/> - <features value="ImportExport"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-17177"/> - <useCaseId value="MAGETWO-98703"/> - <group value="importexport"/> - </annotations> - <before> - <!-- Create category and simple product, then log in --> - <comment userInput="Create category and simple product, then log in" stepKey="createDataAndLogIn"/> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <!-- Delete exported file, created data and log out --> - <comment userInput="Delete exported file, created data and log out" stepKey="deleteCreatedData"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Go to System -> Data Transfer -> Export page --> - <comment userInput="Go to System -> Data Transfer -> Export page" stepKey="goToAdminExportIndexPage"/> - <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="navigateToAdminExportIndexPage"/> - <waitForPageLoad stepKey="waitPageToLoad"/> - <!-- Export all products --> - <comment userInput="Export all products" stepKey="exportAllProds"/> - <actionGroup ref="exportAllProducts" stepKey="exportAllProducts"/> - </test> -</tests> From 9bea49f2e5e1b41c18aa4bf0b12510e083a5f1e4 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Tue, 25 Jun 2019 17:13:01 +0400 Subject: [PATCH 0170/2437] MC-15523: Watermark is possible to set up for swatch image type - Added automated test script --- .../AdminSetUpWatermarkForSwatchImageTest.xml | 57 +++++++++++++++++++ .../Mftf/Section/AdminDesignConfigSection.xml | 4 ++ 2 files changed, 61 insertions(+) create mode 100644 app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml new file mode 100644 index 0000000000000..66043a51db183 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSetUpWatermarkForSwatchImageTest"> + <annotations> + <features value="Swatches"/> + <title value="Possibility to set up watermark for a swatch image type"/> + <description value="Possibility to set up watermark for a swatch image type"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17607"/> + <useCaseId value="MC-15523"/> + <group value="swatches"/> + </annotations> + <before> + <!-- Login as Admin --> + <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Log out --> + <comment userInput="Log out" stepKey="commentLogOut"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Go to Admin > Content > Configuration page --> + <comment userInput="Go to Configuration Page" stepKey="commentOpenConfigurationPage"/> + <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage"/> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'" /> + </actionGroup> + <!-- Select Edit next to the Default Store View --> + <comment userInput="Select Edit next to the Default Store View" stepKey="commentEditDefaultView"/> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickToEditDefaultStoreView"/> + <waitForPageLoad stepKey="waitForDefaultStorePage"/> + <!-- Expand the Product Image Watermarks section--> + <comment userInput="Expand the Product Image Watermarks section" stepKey="commentOpenWatermarksSection"/> + <click selector="{{AdminDesignConfigSection.watermarkSectionHeader}}" stepKey="clickToProductImageWatermarks"/> + <waitForPageLoad stepKey="waitForWatermarksPage"/> + <!-- See Base, Thumbnail, Small image types are displayed --> + <comment userInput="See Base, Thumbnail, Small image types are displayed" stepKey="commentSeeImageTypes"/> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkBase}}" stepKey="seeElementBaseWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkThumbnail}}" stepKey="waitForThumbnailVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkThumbnail}}" stepKey="seeElementThumbnailWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkSmall}}" stepKey="waitForSmallVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkSmall}}" stepKey="seeElementSmallWatermark"/> + <!-- See Swatch Image type is absent --> + <comment userInput="See Swatch Image type is absent" stepKey="commentSeeTypeAbsent"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{AdminDesignConfigSection.imageWatermarkSwatchImage}}" stepKey="dontSeeImageWatermarkSwatchImage"/> + </test> +</tests> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index c2652f33f7606..a65dcc5a1aa14 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -31,5 +31,9 @@ <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> + <element name="imageWatermarkBase" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Base')]"/> + <element name="imageWatermarkThumbnail" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Thumbnail')]"/> + <element name="imageWatermarkSmall" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Small')]"/> + <element name="imageWatermarkSwatchImage" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Swatch Image')]"/> </section> </sections> From 6705536c470405e535374c8c226b8b878fb68bae Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 26 Jun 2019 14:43:38 -0500 Subject: [PATCH 0171/2437] MC-17806: Store name rendering in customer section - Resolved template issue for store name in customer admin section - Updated unit test for related issue --- app/code/Magento/Store/Model/System/Store.php | 6 ++++++ .../Store/Test/Unit/Model/System/StoreTest.php | 9 +++++---- .../Ui/view/base/web/js/grid/editing/record.js | 5 +++++ .../web/js/lib/knockout/bindings/optgroup.js | 17 ++++++++++++----- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Store/Model/System/Store.php b/app/code/Magento/Store/Model/System/Store.php index 86eec555f1d59..f0ec7a375add4 100644 --- a/app/code/Magento/Store/Model/System/Store.php +++ b/app/code/Magento/Store/Model/System/Store.php @@ -152,6 +152,12 @@ public function getStoreValuesForForm($empty = false, $all = false) } } } + array_walk( + $options, + function (&$item) { + $item['__disableTmpl'] = true; + } + ); return $options; } diff --git a/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php index 9cc4bb6ac8e5b..a9208876eaa76 100644 --- a/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php @@ -262,14 +262,15 @@ public function getStoreValuesForFormDataProvider() 'storeGroupId' => $groupId, 'groupWebsiteId' => $websiteId, 'expectedResult' => [ - ['label' => '', 'value' => ''], - ['label' => __('All Store Views'), 'value' => 0], - ['label' => $websiteName, 'value' => []], + ['label' => '', 'value' => '','__disableTmpl' => true], + ['label' => __('All Store Views'), 'value' => 0,'__disableTmpl' => true], + ['label' => $websiteName, 'value' => [],'__disableTmpl' => true], [ 'label' => str_repeat($nonEscapableNbspChar, 4) . $groupName, 'value' => [ ['label' => str_repeat($nonEscapableNbspChar, 4) . $storeName, 'value' => $storeId] - ] + ], + '__disableTmpl' => true ], ] ], diff --git a/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js b/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js index 9b8998368c5ff..14bed73a694c6 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js @@ -134,6 +134,11 @@ define([ field = utils.extend({}, fields.base, field); + field.__disableTmpl = { + label: true, + options: true + }; + return utils.template(field, { record: this, column: column diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 2925c5859eddd..148256f72d01c 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -222,10 +222,14 @@ define([ ko.utils.setTextContent(option, allBindings.get('optionsCaption')); ko.selectExtensions.writeValue(option, undefined); } else if (typeof arrayEntry[optionsValue] === 'undefined') { // empty value === optgroup - option = utils.template(optgroupTmpl, { - label: arrayEntry[optionsText], - title: arrayEntry[optionsText + 'title'] - }); + if (arrayEntry['__disableTmpl']) { + option = '<optgroup label="' + arrayEntry[optionsText] + '"></optgroup>'; + } else { + option = utils.template(optgroupTmpl, { + label: arrayEntry[optionsText], + title: arrayEntry[optionsText + 'title'] + }); + } option = ko.utils.parseHtmlFragment(option)[0]; } else { @@ -302,11 +306,14 @@ define([ space = '\u2007\u2007\u2007'; obj[optionTitle] = applyToObject(option, optionsText + 'title', value); - if (disabled) { obj.disabled = disabled; } + if ("__disableTmpl" in option) { + obj.__disableTmpl = option.__disableTmpl; + } + label = label.replace(nbspRe, '').trim(); if (Array.isArray(value)) { From f4479aa992334e22c5fba0829c7b75c14821109e Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Wed, 26 Jun 2019 15:05:48 -0500 Subject: [PATCH 0172/2437] MC-16112: Signifyd Guarantee Option - fix options, actions, mass actions rendering --- .../Catalog/Ui/Component/Product/MassAction.php | 2 +- .../Magento/Translation/Model/Inline/Parser.php | 13 ++++++++++++- .../Component/Form/Element/AbstractOptionsField.php | 7 +++++++ app/code/Magento/Ui/Component/MassAction.php | 2 +- .../Framework/View/Element/UiComponent/Context.php | 8 ++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php b/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php index 894e2b701b5ac..e54605fbe3ee1 100644 --- a/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php +++ b/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php @@ -49,7 +49,7 @@ public function prepare() : void foreach ($this->getChildComponents() as $actionComponent) { $actionType = $actionComponent->getConfiguration()['type']; if ($this->isActionAllowed($actionType)) { - $config['actions'][] = $actionComponent->getConfiguration(); + $config['actions'][] = array_merge($actionComponent->getConfiguration(), ['__disableTmpl' => true]); } } $origConfig = $this->getConfiguration(); diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php index e0b2ce40405d1..9dc123854e871 100644 --- a/app/code/Magento/Translation/Model/Inline/Parser.php +++ b/app/code/Magento/Translation/Model/Inline/Parser.php @@ -6,6 +6,9 @@ namespace Magento\Translation\Model\Inline; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Escaper; + /** * This class is responsible for parsing content and applying necessary html element * wrapping and client scripts for inline translation. @@ -196,6 +199,9 @@ public function processAjaxPost(array $translateParams) /** @var $validStoreId int */ $validStoreId = $this->_storeManager->getStore()->getId(); + /** @var $escaper Escaper */ + $escaper = ObjectManager::getInstance()->get(Escaper::class); + /** @var $resource \Magento\Translation\Model\ResourceModel\StringUtils */ $resource = $this->_resourceFactory->create(); foreach ($translateParams as $param) { @@ -209,7 +215,12 @@ public function processAjaxPost(array $translateParams) $storeId = $validStoreId; } } - $resource->saveTranslate($param['original'], $param['custom'], null, $storeId); + $resource->saveTranslate( + $param['original'], + $escaper->escapeHtml($param['custom']), + null, + $storeId + ); } return $this->getCacheManger()->updateAndGetTranslations(); diff --git a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php index 09583f65bf157..55b4ae15cdce1 100644 --- a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php +++ b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php @@ -59,6 +59,13 @@ public function prepare() if (empty($config['rawOptions'])) { $options = $this->convertOptionsValueToString($options); } + array_walk( + $options, + function (&$item) { + $item['__disableTmpl'] = true; + } + ); + $config['options'] = array_values(array_merge_recursive($config['options'], $options)); } $this->setData('config', (array)$config); diff --git a/app/code/Magento/Ui/Component/MassAction.php b/app/code/Magento/Ui/Component/MassAction.php index 4cca8d4c012bb..5af263dd861ce 100644 --- a/app/code/Magento/Ui/Component/MassAction.php +++ b/app/code/Magento/Ui/Component/MassAction.php @@ -28,7 +28,7 @@ public function prepare() if ($disabledAction) { continue; } - $config['actions'][] = $componentConfig; + $config['actions'][] = array_merge($componentConfig, ['__disableTmpl' => true]); } $origConfig = $this->getConfiguration(); diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index fbb84712b2afd..e95a9d44edbbd 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -382,6 +382,14 @@ protected function prepareDataSource(array & $data, UiComponentInterface $compon } } $data = $component->prepareDataSource($data); + foreach ($data['data']['items'] as & $item) { + $name = $component->getData('name'); + if (array_key_exists($name, $item) && is_array($item[$name])) { + foreach ($item[$name] as $action => &$actionItem) { + !is_array($actionItem) ?: $actionItem['__disableTmpl'] = true; + } + } + } } /** From f829086961d62dad8d185cbf112f6d6d5547e113 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 26 Jun 2019 16:27:44 -0500 Subject: [PATCH 0173/2437] MC-17806: Store name rendering in customer section - Resolved static and integration test failures --- .../base/web/js/lib/knockout/bindings/optgroup.js | 2 +- .../UrlRewrite/Block/Catalog/Edit/FormTest.php | 15 +++++++++------ .../UrlRewrite/Block/Cms/Page/Edit/FormTest.php | 5 +++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 148256f72d01c..81ce64f64ce55 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -222,7 +222,7 @@ define([ ko.utils.setTextContent(option, allBindings.get('optionsCaption')); ko.selectExtensions.writeValue(option, undefined); } else if (typeof arrayEntry[optionsValue] === 'undefined') { // empty value === optgroup - if (arrayEntry['__disableTmpl']) { + if (arrayEntry.__disableTmpl) { option = '<optgroup label="' + arrayEntry[optionsText] + '"></optgroup>'; } else { option = utils.template(optgroupTmpl, { diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php index 8dcc309514733..c1cfa3cf02d50 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php @@ -234,10 +234,11 @@ public static function getEntityStoresDataProvider() null, ['entity_id' => 3, 'store_ids' => [1]], [ - ['label' => 'Main Website', 'value' => []], + ['label' => 'Main Website', 'value' => [], '__disableTmpl' => true], [ 'label' => '    Main Website Store', - 'value' => [['label' => '    Default Store View', 'value' => 1]] + 'value' => [['label' => '    Default Store View', 'value' => 1]], + '__disableTmpl' => true ] ], ], @@ -245,10 +246,11 @@ public static function getEntityStoresDataProvider() ['entity_id' => 2, 'name' => 'product2', 'url_key' => 'product2', 'store_ids' => [1]], null, [ - ['label' => 'Main Website', 'value' => []], + ['label' => 'Main Website', 'value' => [], '__disableTmpl' => true], [ 'label' => '    Main Website Store', - 'value' => [['label' => '    Default Store View', 'value' => 1]] + 'value' => [['label' => '    Default Store View', 'value' => 1]], + '__disableTmpl' => true ] ] ], @@ -256,10 +258,11 @@ public static function getEntityStoresDataProvider() ['entity_id' => 2, 'name' => 'product2', 'url_key' => 'product2', 'store_ids' => [1]], ['entity_id' => 3, 'name' => 'product3', 'url_key' => 'product3', 'store_ids' => [1]], [ - ['label' => 'Main Website', 'value' => []], + ['label' => 'Main Website', 'value' => [], '__disableTmpl' => true], [ 'label' => '    Main Website Store', - 'value' => [['label' => '    Default Store View', 'value' => 1]] + 'value' => [['label' => '    Default Store View', 'value' => 1]], + '__disableTmpl' => true ] ] ] diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php index c3b93efece456..5bbd276402157 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php @@ -78,10 +78,11 @@ public function testGetEntityStores() $form = $this->_getFormInstance($args); $expectedStores = [ - ['label' => 'Main Website', 'value' => []], + ['label' => 'Main Website', 'value' => [], '__disableTmpl' => true], [ 'label' => '    Main Website Store', - 'value' => [['label' => '    Default Store View', 'value' => 1]] + 'value' => [['label' => '    Default Store View', 'value' => 1]], + '__disableTmpl' => true ], ]; $this->assertEquals($expectedStores, $form->getElement('store_id')->getValues()); From 8572d837c58a8717dd6d75835c1b793e13cc8198 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Wed, 26 Jun 2019 16:58:21 -0500 Subject: [PATCH 0174/2437] MC-17806: Store name rendering in customer section - Resolved static test failures --- .../Ui/view/base/web/js/lib/knockout/bindings/optgroup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 81ce64f64ce55..a1646e0cff068 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -310,7 +310,7 @@ define([ obj.disabled = disabled; } - if ("__disableTmpl" in option) { + if (option.hasOwnProperty("__disableTmpl")) { obj.__disableTmpl = option.__disableTmpl; } From b2a48682e7ea2e3a3c88724104e629d408118fc6 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Thu, 27 Jun 2019 12:14:12 +0000 Subject: [PATCH 0175/2437] MAGETWO-98703: CSV file is not exported - Fix CR comments --- app/code/Magento/ImportExport/i18n/en_US.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv index d7680a71ac5f7..7075c69a3c29c 100644 --- a/app/code/Magento/ImportExport/i18n/en_US.csv +++ b/app/code/Magento/ImportExport/i18n/en_US.csv @@ -123,3 +123,4 @@ Summary,Summary "New product data is added to existing product data entries in the database. All fields except SKU can be updated.","New product data is added to existing product data entries in the database. All fields except SKU can be updated." "All existing product data is replaced with the imported new data. <b>Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost.</b>","All existing product data is replaced with the imported new data. <b>Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost.</b>" "Any entities in the import data that match existing entities in the database are deleted from the database.","Any entities in the import data that match existing entities in the database are deleted from the database." +"Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file","Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file" From 9077c1cc0f09088a1624180d9da0fcb613172d19 Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <TomashKhamlai@users.noreply.github.com> Date: Thu, 27 Jun 2019 17:45:48 +0300 Subject: [PATCH 0176/2437] Update AddBundleProductToCartTest.php --- .../Magento/GraphQl/Bundle/AddBundleProductToCartTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php index 7905484ec51df..b1574c06c2d63 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -80,7 +80,7 @@ public function testAddBundleProductToCart() mutation { addBundleProductsToCart(input:{ cart_id:"{$maskedQuoteId}" - cartItems:[ + cart_items:[ { data:{ sku:"{$sku}" @@ -163,7 +163,7 @@ public function testAddBundleToCartWithoutOptions() mutation { addBundleProductsToCart(input:{ cart_id:"{$maskedQuoteId}" - cartItems:[ + cart_items:[ { data:{ sku:"bundle-product" From c44db50097ca188f27cca2aced224ca57a61913b Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Thu, 27 Jun 2019 10:41:03 -0500 Subject: [PATCH 0177/2437] MC-17806: Store name rendering in customer section - Resolved static test failures --- app/code/Magento/Store/Model/System/Store.php | 2 ++ app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php | 3 +++ .../Ui/view/base/web/js/lib/knockout/bindings/optgroup.js | 2 +- .../Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php | 2 ++ .../Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Model/System/Store.php b/app/code/Magento/Store/Model/System/Store.php index f0ec7a375add4..53338a8fec5ae 100644 --- a/app/code/Magento/Store/Model/System/Store.php +++ b/app/code/Magento/Store/Model/System/Store.php @@ -118,6 +118,7 @@ public function getStoreValuesForForm($empty = false, $all = false) $options[] = ['label' => __('All Store Views'), 'value' => 0]; } + //phpcs:ignore Magento2.Functions.DiscouragedFunction $nonEscapableNbspChar = html_entity_decode(' ', ENT_NOQUOTES, 'UTF-8'); foreach ($this->_websiteCollection as $website) { @@ -404,6 +405,7 @@ public function getStoreCollection() /** * Load/Reload collection(s) by type + * * Allowed types: website, group, store or null for all * * @param string $type diff --git a/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php index a9208876eaa76..6befb28e35383 100644 --- a/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php +++ b/app/code/Magento/Store/Test/Unit/Model/System/StoreTest.php @@ -6,6 +6,9 @@ namespace Magento\Store\Test\Unit\Model\System; +/** + * Class StoreTest covers Magento\Store\Model\System\Store. + */ class StoreTest extends \PHPUnit\Framework\TestCase { /** diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index a1646e0cff068..1c4ab4eaebb47 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -310,7 +310,7 @@ define([ obj.disabled = disabled; } - if (option.hasOwnProperty("__disableTmpl")) { + if (option.hasOwnProperty('__disableTmpl')) { obj.__disableTmpl = option.__disableTmpl; } diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php index c1cfa3cf02d50..65cba5947ba05 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Catalog/Edit/FormTest.php @@ -190,6 +190,7 @@ public function testGetEntityStoresCategoryStoresException() * * @static * @return array + * phpcs:disable Magento2.Functions.StaticFunction */ public static function formPostInitDataProvider() { @@ -226,6 +227,7 @@ public static function formPostInitDataProvider() * * @static * @return array + * phpcs:disable Magento2.Functions.StaticFunction */ public static function getEntityStoresDataProvider() { diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php index 5bbd276402157..041a2c268a55a 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Block/Cms/Page/Edit/FormTest.php @@ -111,6 +111,7 @@ public function testGetEntityStoresProductStoresException() * * @static * @return array + * phpcs:disable Magento2.Functions.StaticFunction */ public static function formPostInitDataProvider() { From 64421d8acf0004ac3f1688a825948a2770fb1331 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Thu, 27 Jun 2019 12:10:47 -0500 Subject: [PATCH 0178/2437] MC-17806: Store name rendering in customer section - Resolved static test failures --- .../Ui/view/base/web/js/lib/knockout/bindings/optgroup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 1c4ab4eaebb47..6ff7c1f673213 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -306,6 +306,7 @@ define([ space = '\u2007\u2007\u2007'; obj[optionTitle] = applyToObject(option, optionsText + 'title', value); + if (disabled) { obj.disabled = disabled; } From 1a3dacd7e27b8befa81ce7d2aedd45b4853dd273 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Thu, 27 Jun 2019 13:27:11 -0500 Subject: [PATCH 0179/2437] MC-16112: Signifyd Guarantee Option - clean up code, fix test failures --- .../Ui/Component/Product/MassActionTest.php | 24 ++++++---- .../Translation/Model/Inline/Parser.php | 47 +++++++++++-------- .../Form/Element/AbstractOptionsField.php | 13 +++-- .../Ui/Test/Unit/Component/MassActionTest.php | 6 ++- .../View/Element/UiComponent/Context.php | 15 ++++-- 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php index dcd50d4739d70..1b2a58672a5c9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php @@ -103,7 +103,8 @@ public function getPrepareDataProvider() : array [ 'type' => 'first_action', 'label' => 'First Action', - 'url' => '/module/controller/firstAction' + 'url' => '/module/controller/firstAction', + '__disableTmpl' => true ], ], [ @@ -122,7 +123,8 @@ public function getPrepareDataProvider() : array 'label' => 'Second Sub Action 2', 'url' => '/module/controller/secondSubAction2' ], - ] + ], + '__disableTmpl' => true ], ], [ @@ -141,7 +143,8 @@ public function getPrepareDataProvider() : array 'label' => 'Second Sub Action 2', 'url' => '/module/controller/disable' ], - ] + ], + '__disableTmpl' => true ], ], [ @@ -160,7 +163,8 @@ public function getPrepareDataProvider() : array 'label' => 'Second Sub Action 2', 'url' => '/module/controller/disable' ], - ] + ], + '__disableTmpl' => true ], false, false @@ -170,7 +174,8 @@ public function getPrepareDataProvider() : array [ 'type' => 'delete', 'label' => 'First Action', - 'url' => '/module/controller/delete' + 'url' => '/module/controller/delete', + '__disableTmpl' => true ], ], [ @@ -178,7 +183,8 @@ public function getPrepareDataProvider() : array [ 'type' => 'delete', 'label' => 'First Action', - 'url' => '/module/controller/delete' + 'url' => '/module/controller/delete', + '__disableTmpl' => true ], false, false @@ -188,7 +194,8 @@ public function getPrepareDataProvider() : array [ 'type' => 'delete', 'label' => 'First Action', - 'url' => '/module/controller/attributes' + 'url' => '/module/controller/attributes', + '__disableTmpl' => true ], ], [ @@ -196,7 +203,8 @@ public function getPrepareDataProvider() : array [ 'type' => 'delete', 'label' => 'First Action', - 'url' => '/module/controller/attributes' + 'url' => '/module/controller/attributes', + '__disableTmpl' => true ], false, false diff --git a/app/code/Magento/Translation/Model/Inline/Parser.php b/app/code/Magento/Translation/Model/Inline/Parser.php index 9dc123854e871..c64622acbb6c0 100644 --- a/app/code/Magento/Translation/Model/Inline/Parser.php +++ b/app/code/Magento/Translation/Model/Inline/Parser.php @@ -6,12 +6,8 @@ namespace Magento\Translation\Model\Inline; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Escaper; - /** - * This class is responsible for parsing content and applying necessary html element - * wrapping and client scripts for inline translation. + * Parses content and applies necessary html element wrapping and client scripts for inline translation. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -135,6 +131,8 @@ class Parser implements \Magento\Framework\Translate\Inline\ParserInterface private $relatedCacheTypes; /** + * Return cache manager + * * @return \Magento\Translation\Model\Inline\CacheManager * * @deprecated 100.1.0 @@ -152,8 +150,8 @@ private function getCacheManger() /** * Initialize base inline translation model * - * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Translation\Model\ResourceModel\StringUtilsFactory $resource + * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Zend_Filter_Interface $inputFilter * @param \Magento\Framework\App\State $appState * @param \Magento\Framework\App\Cache\TypeListInterface $appCache @@ -199,9 +197,6 @@ public function processAjaxPost(array $translateParams) /** @var $validStoreId int */ $validStoreId = $this->_storeManager->getStore()->getId(); - /** @var $escaper Escaper */ - $escaper = ObjectManager::getInstance()->get(Escaper::class); - /** @var $resource \Magento\Translation\Model\ResourceModel\StringUtils */ $resource = $this->_resourceFactory->create(); foreach ($translateParams as $param) { @@ -217,7 +212,7 @@ public function processAjaxPost(array $translateParams) } $resource->saveTranslate( $param['original'], - $escaper->escapeHtml($param['custom']), + $param['custom'], null, $storeId ); @@ -247,7 +242,7 @@ protected function _validateTranslationParams(array $translateParams) /** * Apply input filter to values of translation parameters * - * @param array &$translateParams + * @param array $translateParams * @param array $fieldNames Names of fields values of which are to be filtered * @return void */ @@ -371,7 +366,7 @@ protected function _applySpecialTagsFormat($tagHtml, $tagName, $trArr) * Format translation for simple tags. Added translate mode attribute for vde requests. * * @param string $tagHtml - * @param string $tagName + * @param string $tagName * @param array $trArr * @return string */ @@ -397,7 +392,7 @@ protected function _applySimpleTagsFormat($tagHtml, $tagName, $trArr) * Get translate data by regexp * * @param string $regexp - * @param string &$text + * @param string $text * @param string|array $locationCallback * @param array $options * @return array @@ -412,7 +407,7 @@ private function _getTranslateData($regexp, &$text, $locationCallback, $options 'shown' => htmlspecialchars_decode($matches[1][0]), 'translated' => htmlspecialchars_decode($matches[2][0]), 'original' => htmlspecialchars_decode($matches[3][0]), - 'location' => htmlspecialchars_decode(call_user_func($locationCallback, $matches, $options)), + 'location' => htmlspecialchars_decode($locationCallback($matches, $options)), ] ); $text = substr_replace($text, $matches[1][0], $matches[0][1], strlen($matches[0][0])); @@ -523,16 +518,28 @@ private function _getHtmlQuote() */ private function _specialTags() { - $this->_translateTags($this->_content, $this->_allowedTagsGlobal, '_applySpecialTagsFormat'); - $this->_translateTags($this->_content, $this->_allowedTagsSimple, '_applySimpleTagsFormat'); + $this->_translateTags( + $this->_content, + $this->_allowedTagsGlobal, + function ($tagHtml, $tagName, $trArr) { + return $this->_applySpecialTagsFormat($tagHtml, $tagName, $trArr); + } + ); + $this->_translateTags( + $this->_content, + $this->_allowedTagsSimple, + function ($tagHtml, $tagName, $trArr) { + return $this->_applySimpleTagsFormat($tagHtml, $tagName, $trArr); + } + ); } /** * Prepare simple tags * - * @param string &$content + * @param string $content * @param array $tagsList - * @param string|array $formatCallback + * @param callable $formatCallback * @return void */ private function _translateTags(&$content, $tagsList, $formatCallback) @@ -586,10 +593,10 @@ private function _translateTags(&$content, $tagsList, $formatCallback) if (array_key_exists($tagName, $this->_allowedTagsGlobal) && $tagBodyOpenStartPosition > $tagMatch[0][1] ) { - $tagHtmlHead = call_user_func([$this, $formatCallback], $tagHtml, $tagName, $trArr); + $tagHtmlHead = $formatCallback($tagHtml, $tagName, $trArr); $headTranslateTags .= substr($tagHtmlHead, strlen($tagHtml)); } else { - $tagHtml = call_user_func([$this, $formatCallback], $tagHtml, $tagName, $trArr); + $tagHtml = $formatCallback($tagHtml, $tagName, $trArr); } } diff --git a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php index 55b4ae15cdce1..1229fbeec77c7 100644 --- a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php +++ b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** + * Class AbstractOptionsField + * * @api * @since 100.1.0 */ @@ -91,11 +93,14 @@ abstract public function getIsSelected($optionValue); */ protected function convertOptionsValueToString(array $options) { - array_walk($options, function (&$value) { - if (isset($value['value']) && is_scalar($value['value'])) { - $value['value'] = (string)$value['value']; + array_walk( + $options, + function (&$value) { + if (isset($value['value']) && is_scalar($value['value'])) { + $value['value'] = (string)$value['value']; + } } - }); + ); return $options; } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/MassActionTest.php b/app/code/Magento/Ui/Test/Unit/Component/MassActionTest.php index b2f494351597f..c2e064bb3b069 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/MassActionTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/MassActionTest.php @@ -104,7 +104,8 @@ public function getPrepareDataProvider() [ 'type' => 'first_action', 'label' => 'First Action', - 'url' => '/module/controller/firstAction' + 'url' => '/module/controller/firstAction', + '__disableTmpl' => true ], ], [ @@ -123,7 +124,8 @@ public function getPrepareDataProvider() 'label' => 'Second Sub Action 2', 'url' => '/module/controller/secondSubAction2' ], - ] + ], + '__disableTmpl' => true ], ], ]; diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index e95a9d44edbbd..62ee657bbd6d0 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -372,6 +372,7 @@ public function getUrl($route = '', $params = []) * @param array $data * @param UiComponentInterface $component * @return void + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function prepareDataSource(array & $data, UiComponentInterface $component) { @@ -382,11 +383,15 @@ protected function prepareDataSource(array & $data, UiComponentInterface $compon } } $data = $component->prepareDataSource($data); - foreach ($data['data']['items'] as & $item) { - $name = $component->getData('name'); - if (array_key_exists($name, $item) && is_array($item[$name])) { - foreach ($item[$name] as $action => &$actionItem) { - !is_array($actionItem) ?: $actionItem['__disableTmpl'] = true; + if (isset($data['data']['items'])) { + foreach ($data['data']['items'] as & $item) { + $name = $component->getData('name'); + if (array_key_exists($name, $item) && is_array($item[$name])) { + foreach ($item[$name] as $action => &$actionItem) { + if (is_array($actionItem)) { + $actionItem['__disableTmpl'] = true; + } + } } } } From 269137ffcd98c696c59f9da6dd3bcf8a499b602f Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Thu, 27 Jun 2019 15:38:13 -0500 Subject: [PATCH 0180/2437] MC-16112: Signifyd Guarantee Option - clean up code, fix test failures --- .../Test/Unit/Ui/Component/Product/MassActionTest.php | 3 +++ .../Magento/Catalog/Ui/Component/Product/MassAction.php | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php index 1b2a58672a5c9..c704d9f89581d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/Product/MassActionTest.php @@ -12,6 +12,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Element\UiComponent\ContextInterface; +/** + * MassAction test + */ class MassActionTest extends \PHPUnit\Framework\TestCase { /** diff --git a/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php b/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php index e54605fbe3ee1..8db1bf8268b66 100644 --- a/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php +++ b/app/code/Magento/Catalog/Ui/Component/Product/MassAction.php @@ -12,6 +12,9 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\AbstractComponent; +/** + * Class MassAction + */ class MassAction extends AbstractComponent { const NAME = 'massaction'; @@ -40,7 +43,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function prepare() : void { @@ -64,7 +67,7 @@ public function prepare() : void } /** - * {@inheritdoc} + * @inheritdoc */ public function getComponentName() : string { From 438733108448fa37c1971741e4782ad673c79507 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 27 Jun 2019 18:07:59 -0400 Subject: [PATCH 0181/2437] fix static tests --- .../Model/Cart/BundleOptionDataProvider.php | 9 +++++++++ .../Model/Cart/BuyRequest/BundleDataProvider.php | 3 +++ .../BundleGraphQl/Model/Resolver/BundleOption.php | 7 +++++++ app/code/Magento/BundleGraphQl/etc/module.xml | 1 + .../Model/Cart/AddSimpleProductToCart.php | 2 +- .../Model/Cart/BuyRequest/BuyRequestBuilder.php | 13 +++++++++++++ .../BuyRequest/BuyRequestDataProviderInterface.php | 9 +++++++++ .../BuyRequest/CustomizableOptionsDataProvider.php | 3 +++ .../Model/Cart/BuyRequest/DefaultDataProvider.php | 3 +++ .../GraphQl/Bundle/AddBundleProductToCartTest.php | 3 +++ 10 files changed, 52 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php index 8db1c64c23c42..5cdfdc88e7dc1 100644 --- a/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php @@ -13,6 +13,9 @@ use Magento\Framework\Pricing\Helper\Data; use Magento\Framework\Serialize\SerializerInterface; +/** + * Data provider for bundled product options + */ class BundleOptionDataProvider { /** @@ -46,6 +49,8 @@ public function __construct( } /** + * Extract data for a bundled cart item + * * @param Item $item * @return array */ @@ -82,6 +87,8 @@ public function getData(Item $item): array } /** + * Build bundle product options based on current selection + * * @param \Magento\Bundle\Model\Option[] $bundleOptions * @param Item $item * @return array @@ -106,6 +113,8 @@ private function buildBundleOptions(array $bundleOptions, Item $item): array } /** + * Build bundle product option values based on current selection + * * @param Product[] $selections * @param Item $item * @return array diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php index 72a72dd5b3bcf..37a9309092166 100644 --- a/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php @@ -10,6 +10,9 @@ use Magento\Framework\Stdlib\ArrayManager; use Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestDataProviderInterface; +/** + * Data provider for bundle product buy requests + */ class BundleDataProvider implements BuyRequestDataProviderInterface { /** diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php index 9bccbf936f18d..6b64310fcb1e3 100644 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php @@ -13,6 +13,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +/** + * Resolver for bundle product options + */ class BundleOption implements ResolverInterface { /** @@ -29,6 +32,10 @@ public function __construct( $this->dataProvider = $bundleOptionDataProvider; } + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($value['model'])) { diff --git a/app/code/Magento/BundleGraphQl/etc/module.xml b/app/code/Magento/BundleGraphQl/etc/module.xml index 352a46d7c171e..8d6725054867e 100644 --- a/app/code/Magento/BundleGraphQl/etc/module.xml +++ b/app/code/Magento/BundleGraphQl/etc/module.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_BundleGraphQl" > <sequence> + <module name="Magento_QuoteGraphQl"/> <module name="Magento_Catalog"/> <module name="Magento_BundleProduct"/> <module name="Magento_Store"/> diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 13079c86f48d7..3f6cc42614030 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -114,4 +114,4 @@ private function extractQuantity(array $cartItemData): float } return $quantity; } -} \ No newline at end of file +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php index 492dd18f14e03..90f32e96a5fde 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestBuilder.php @@ -10,6 +10,9 @@ use Magento\Framework\DataObject; use Magento\Framework\DataObjectFactory; +/** + * Build buy request for adding products to cart + */ class BuyRequestBuilder { /** @@ -22,6 +25,10 @@ class BuyRequestBuilder */ private $dataObjectFactory; + /** + * @param DataObjectFactory $dataObjectFactory + * @param array $providers + */ public function __construct( DataObjectFactory $dataObjectFactory, array $providers = [] @@ -30,6 +37,12 @@ public function __construct( $this->providers = $providers; } + /** + * Build buy request for adding product to cart + * + * @param array $cartItemData + * @return DataObject + */ public function build(array $cartItemData): DataObject { $requestData = []; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php index adfc5b13b762c..e606c6b225da2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php @@ -7,7 +7,16 @@ namespace Magento\QuoteGraphQl\Model\Cart\BuyRequest; +/** + * Provide buy request data from add to cart item request + */ interface BuyRequestDataProviderInterface { + /** + * Provide buy request data from add to cart item request + * + * @param array $cartItemData + * @return array + */ public function execute(array $cartItemData): array; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php index a6b4ae00ab602..d777fbb039350 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php @@ -9,6 +9,9 @@ use Magento\Framework\Stdlib\ArrayManager; +/** + * Extract buy request elements require for custom options + */ class CustomizableOptionsDataProvider implements BuyRequestDataProviderInterface { /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php index 3185510e42865..6ef1679a3382c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php @@ -10,6 +10,9 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\Stdlib\ArrayManager; +/** + * Provides QTY buy request data for adding products to cart + */ class DefaultDataProvider implements BuyRequestDataProviderInterface { /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php index b1574c06c2d63..a8e75d7778f82 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -14,6 +14,9 @@ use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +/** + * Test adding bundled products to cart + */ class AddBundleProductToCartTest extends GraphQlAbstract { /** From 840fbedd4560beb5ed708a263e493b6bb6115aec Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 27 Jun 2019 18:09:52 -0400 Subject: [PATCH 0182/2437] Rename DefaultDataProvider QuantityDataProvider The name now reflects the purpose --- .../{DefaultDataProvider.php => QuantityDataProvider.php} | 2 +- app/code/Magento/QuoteGraphQl/etc/graphql/di.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/{DefaultDataProvider.php => QuantityDataProvider.php} (93%) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php similarity index 93% rename from app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php index 6ef1679a3382c..a3c5c33342724 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/DefaultDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php @@ -13,7 +13,7 @@ /** * Provides QTY buy request data for adding products to cart */ -class DefaultDataProvider implements BuyRequestDataProviderInterface +class QuantityDataProvider implements BuyRequestDataProviderInterface { /** * @var ArrayManager diff --git a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml index cd1408f445180..88f97d32b71d0 100644 --- a/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/graphql/di.xml @@ -13,7 +13,7 @@ <type name="Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestBuilder"> <arguments> <argument name="providers" xsi:type="array"> - <item name="default" xsi:type="object">Magento\QuoteGraphQl\Model\Cart\BuyRequest\DefaultDataProvider</item> + <item name="quantity" xsi:type="object">Magento\QuoteGraphQl\Model\Cart\BuyRequest\QuantityDataProvider</item> <item name="customizable_options" xsi:type="object">Magento\QuoteGraphQl\Model\Cart\BuyRequest\CustomizableOptionsDataProvider</item> </argument> </arguments> From 989038c543a75f5438a351799e91379e0613a59b Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 27 Jun 2019 18:38:21 -0400 Subject: [PATCH 0183/2437] Update tests for current schema mutation function --- .../Bundle/AddBundleProductToCartTest.php | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php index a8e75d7778f82..21fd88519d22e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -87,7 +87,7 @@ public function testAddBundleProductToCart() { data:{ sku:"{$sku}" - qty:1 + quantity:1 } bundle_options:[ { @@ -102,10 +102,9 @@ public function testAddBundleProductToCart() ] }) { cart { - cart_id items { id - qty + quantity product { sku } @@ -128,12 +127,11 @@ public function testAddBundleProductToCart() } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertArrayHasKey('addBundleProductsToCart', $response); $this->assertArrayHasKey('cart', $response['addBundleProductsToCart']); $cart = $response['addBundleProductsToCart']['cart']; - $this->assertEquals($maskedQuoteId, $cart['cart_id']); $bundleItem = current($cart['items']); $this->assertEquals($sku, $bundleItem['product']['sku']); $bundleItemOption = current($bundleItem['bundle_options']); @@ -170,7 +168,7 @@ public function testAddBundleToCartWithoutOptions() { data:{ sku:"bundle-product" - qty:1 + quantity:1 } bundle_options:[ { @@ -185,12 +183,31 @@ public function testAddBundleToCartWithoutOptions() ] }) { cart { - cart_id + items { + id + quantity + product { + sku + } + ... on BundleCartItem { + bundle_options { + id + label + type + values { + id + label + price + quantity + } + } + } + } } } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } } From 226cbf75320bab7bd3d5e69d5107a1df7cbc442b Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 27 Jun 2019 21:47:00 -0400 Subject: [PATCH 0184/2437] Update add to cart with extendible buy request builder --- .../Model/Cart/AddSimpleProductToCart.php | 41 ++++--------------- .../CustomizableOptionsDataProvider.php | 28 +++++++++++-- .../Cart/BuyRequest/QuantityDataProvider.php | 4 +- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 3f6cc42614030..f029305c118fa 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -12,6 +12,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote; +use Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestBuilder; /** * Add simple product to cart @@ -19,25 +20,25 @@ class AddSimpleProductToCart { /** - * @var CreateBuyRequest + * @var ProductRepositoryInterface */ - private $createBuyRequest; + private $productRepository; /** - * @var ProductRepositoryInterface + * @var BuyRequestBuilder */ - private $productRepository; + private $buyRequestBuilder; /** * @param ProductRepositoryInterface $productRepository - * @param CreateBuyRequest $createBuyRequest + * @param BuyRequestBuilder $buyRequestBuilder */ public function __construct( ProductRepositoryInterface $productRepository, - CreateBuyRequest $createBuyRequest + BuyRequestBuilder $buyRequestBuilder ) { $this->productRepository = $productRepository; - $this->createBuyRequest = $createBuyRequest; + $this->buyRequestBuilder = $buyRequestBuilder; } /** @@ -53,8 +54,6 @@ public function __construct( public function execute(Quote $cart, array $cartItemData): void { $sku = $this->extractSku($cartItemData); - $quantity = $this->extractQuantity($cartItemData); - $customizableOptions = $cartItemData['customizable_options'] ?? []; try { $product = $this->productRepository->get($sku); @@ -63,7 +62,7 @@ public function execute(Quote $cart, array $cartItemData): void } try { - $result = $cart->addProduct($product, $this->createBuyRequest->execute($quantity, $customizableOptions)); + $result = $cart->addProduct($product, $this->buyRequestBuilder->build($cartItemData)); } catch (\Exception $e) { throw new GraphQlInputException( __( @@ -92,26 +91,4 @@ private function extractSku(array $cartItemData): string } return (string)$cartItemData['data']['sku']; } - - /** - * Extract quantity from cart item data - * - * @param array $cartItemData - * @return float - * @throws GraphQlInputException - */ - private function extractQuantity(array $cartItemData): float - { - if (!isset($cartItemData['data']['quantity'])) { - throw new GraphQlInputException(__('Missed "qty" in cart item data')); - } - $quantity = (float)$cartItemData['data']['quantity']; - - if ($quantity <= 0) { - throw new GraphQlInputException( - __('Please enter a number greater than 0 in this field.') - ); - } - return $quantity; - } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php index d777fbb039350..70e536411f947 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php @@ -33,13 +33,33 @@ public function __construct( */ public function execute(array $cartItemData): array { - $customizableOptions = $this->arrayManager->get('customizable_options', $cartItemData, []); + $customizableOptionsData = $this->arrayManager->get('customizable_options', $cartItemData, []); - $customizableOptionsData = []; - foreach ($customizableOptions as $customizableOption) { - $customizableOptionsData[$customizableOption['id']] = $customizableOption['value']; + $customizableOptions = []; + foreach ($customizableOptionsData as $customizableOption) { + if (isset($customizableOption['value_string'])) { + $customizableOptions[$customizableOption['id']] = $this->convertCustomOptionValue( + $customizableOption['value_string'] + ); + } } return ['options' => $customizableOptionsData]; } + + /** + * Convert custom options value + * + * @param string $value + * @return string|array + */ + private function convertCustomOptionValue(string $value) + { + $value = trim($value); + if (substr($value, 0, 1) === "[" && + substr($value, strlen($value) - 1, 1) === "]") { + return explode(',', substr($value, 1, -1)); + } + return $value; + } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php index a3c5c33342724..d02b7cdb17d2e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php @@ -34,9 +34,9 @@ public function __construct( */ public function execute(array $cartItemData): array { - $qty = $this->arrayManager->get('data/qty', $cartItemData); + $qty = $this->arrayManager->get('data/quantity', $cartItemData); if (!isset($qty)) { - throw new GraphQlInputException(__('Missing key "qty" in cart item data')); + throw new GraphQlInputException(__('Missing key "quantity" in cart item data')); } return ['qty' => (float)$qty]; From 1f2aedaedb58661746207d959d67635dfb6cd0ae Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Thu, 27 Jun 2019 22:31:54 -0400 Subject: [PATCH 0185/2437] Fix qty validation and custom option buy request provider --- .../Cart/BuyRequest/CustomizableOptionsDataProvider.php | 2 +- .../Model/Cart/BuyRequest/QuantityDataProvider.php | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php index 70e536411f947..bc7b16a12b581 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/CustomizableOptionsDataProvider.php @@ -44,7 +44,7 @@ public function execute(array $cartItemData): array } } - return ['options' => $customizableOptionsData]; + return ['options' => $customizableOptions]; } /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php index d02b7cdb17d2e..6202ed04c1d60 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/QuantityDataProvider.php @@ -38,7 +38,14 @@ public function execute(array $cartItemData): array if (!isset($qty)) { throw new GraphQlInputException(__('Missing key "quantity" in cart item data')); } + $qty = (float)$qty; - return ['qty' => (float)$qty]; + if ($qty <= 0) { + throw new GraphQlInputException( + __('Please enter a number greater than 0 in this field.') + ); + } + + return ['qty' => $qty]; } } From 03d5342b206b0b4fa9fefa7e6df0ec61229021e7 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 28 Jun 2019 11:09:41 -0500 Subject: [PATCH 0186/2437] MC-16112: Signifyd Guarantee Option - clean up code, fix test failures --- .../Ui/Component/Listing/Columns/ProductActions.php | 1 + .../Cms/Ui/Component/Listing/Column/BlockActions.php | 2 ++ .../Cms/Ui/Component/Listing/Column/PageActions.php | 7 +++++-- .../Customer/Ui/Component/Listing/Column/Actions.php | 1 + .../Ui/Component/Listing/Column/GroupActions.php | 4 +++- .../Ui/Component/Listing/Column/SynonymActions.php | 4 +++- .../Theme/Ui/Component/Listing/Column/EditAction.php | 3 ++- .../Theme/Ui/Component/Listing/Column/ViewAction.php | 3 ++- .../Framework/View/Element/UiComponent/Context.php | 12 ------------ 9 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/ProductActions.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/ProductActions.php index 0c4efa87c1a32..596b0f4118599 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/ProductActions.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/ProductActions.php @@ -60,6 +60,7 @@ public function prepareDataSource(array $dataSource) ), 'label' => __('Edit'), 'hidden' => false, + '__disableTmpl' => true ]; } } diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php index 6e9eef47281c0..65940c5d7b4f9 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/BlockActions.php @@ -70,6 +70,7 @@ public function prepareDataSource(array $dataSource) ] ), 'label' => __('Edit'), + '__disableTmpl' => true, ], 'delete' => [ 'href' => $this->urlBuilder->getUrl( @@ -84,6 +85,7 @@ public function prepareDataSource(array $dataSource) 'message' => __('Are you sure you want to delete a %1 record?', $title), ], 'post' => true, + '__disableTmpl' => true, ], ]; } diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php index cfac3bbf54956..ca86aec033554 100644 --- a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php +++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php @@ -77,7 +77,8 @@ public function prepareDataSource(array $dataSource) if (isset($item['page_id'])) { $item[$name]['edit'] = [ 'href' => $this->urlBuilder->getUrl($this->editUrl, ['page_id' => $item['page_id']]), - 'label' => __('Edit') + 'label' => __('Edit'), + '__disableTmpl' => true, ]; $title = $this->getEscaper()->escapeHtml($item['title']); $item[$name]['delete'] = [ @@ -89,6 +90,7 @@ public function prepareDataSource(array $dataSource) '__disableTmpl' => true, ], 'post' => true, + '__disableTmpl' => true, ]; } if (isset($item['identifier'])) { @@ -98,7 +100,8 @@ public function prepareDataSource(array $dataSource) isset($item['_first_store_id']) ? $item['_first_store_id'] : null, isset($item['store_code']) ? $item['store_code'] : null ), - 'label' => __('View') + 'label' => __('View'), + '__disableTmpl' => true, ]; } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/Actions.php index d6a4067ef3db6..9441beeb7dc61 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/Actions.php @@ -60,6 +60,7 @@ public function prepareDataSource(array $dataSource) ), 'label' => __('Edit'), 'hidden' => false, + '__disableTmpl' => true ]; } } diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php index 6870bd1136d10..12f6f2705125b 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php @@ -79,6 +79,7 @@ public function prepareDataSource(array $dataSource) ] ), 'label' => __('Edit'), + '__disableTmpl' => true ], ]; @@ -102,7 +103,8 @@ public function prepareDataSource(array $dataSource) $this->escaper->escapeJs($title) ) ], - 'post' => true + 'post' => true, + '__disableTmpl' => true ]; } } diff --git a/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php b/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php index 8cc9b809ff888..f42ce50d2804b 100644 --- a/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php +++ b/app/code/Magento/Search/Ui/Component/Listing/Column/SynonymActions.php @@ -63,11 +63,13 @@ public function prepareDataSource(array $dataSource) 'confirm' => [ 'title' => __('Delete'), 'message' => __('Are you sure you want to delete synonym group with id: %1?', $item['group_id']) - ] + ], + '__disableTmpl' => true ]; $item[$name]['edit'] = [ 'href' => $this->urlBuilder->getUrl(self::SYNONYM_URL_PATH_EDIT, ['group_id' => $item['group_id']]), 'label' => __('View/Edit'), + '__disableTmpl' => true ]; } } diff --git a/app/code/Magento/Theme/Ui/Component/Listing/Column/EditAction.php b/app/code/Magento/Theme/Ui/Component/Listing/Column/EditAction.php index 801f30ce30b0a..1eeeaccff88ce 100644 --- a/app/code/Magento/Theme/Ui/Component/Listing/Column/EditAction.php +++ b/app/code/Magento/Theme/Ui/Component/Listing/Column/EditAction.php @@ -73,7 +73,8 @@ public function prepareDataSource(array $dataSource) 'scope_id' => $scopeId, ] ), - 'label' => __('Edit') + 'label' => __('Edit'), + '__disableTmpl' => true, ] ]; } diff --git a/app/code/Magento/Theme/Ui/Component/Listing/Column/ViewAction.php b/app/code/Magento/Theme/Ui/Component/Listing/Column/ViewAction.php index 774d5bab660af..9e47e2c52bddf 100644 --- a/app/code/Magento/Theme/Ui/Component/Listing/Column/ViewAction.php +++ b/app/code/Magento/Theme/Ui/Component/Listing/Column/ViewAction.php @@ -65,7 +65,8 @@ public function prepareDataSource(array $dataSource) : array $urlEntityParamName => $item[$indexField] ] ), - 'label' => __('View') + 'label' => __('View'), + '__disableTmpl' => true, ] ]; } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index 62ee657bbd6d0..c49832dac9636 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -383,18 +383,6 @@ protected function prepareDataSource(array & $data, UiComponentInterface $compon } } $data = $component->prepareDataSource($data); - if (isset($data['data']['items'])) { - foreach ($data['data']['items'] as & $item) { - $name = $component->getData('name'); - if (array_key_exists($name, $item) && is_array($item[$name])) { - foreach ($item[$name] as $action => &$actionItem) { - if (is_array($actionItem)) { - $actionItem['__disableTmpl'] = true; - } - } - } - } - } } /** From 0ff326fc4574fbf1dc7140adad69bc46c22141b6 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 28 Jun 2019 11:13:03 -0500 Subject: [PATCH 0187/2437] MC-16112: Signifyd Guarantee Option - clean up code, fix test failures --- .../Magento/Framework/View/Element/UiComponent/Context.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php index c49832dac9636..fbb84712b2afd 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php @@ -372,7 +372,6 @@ public function getUrl($route = '', $params = []) * @param array $data * @param UiComponentInterface $component * @return void - * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function prepareDataSource(array & $data, UiComponentInterface $component) { From 05cd916091d467c7c7ead9ce537d30d4c0507636 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 28 Jun 2019 12:54:42 -0500 Subject: [PATCH 0188/2437] MC-16112: Signifyd Guarantee Option - clean up code, fix test failures --- .../Listing/Column/BlockActionsTest.php | 13 +++-- .../Listing/Column/PageActionsTest.php | 4 +- .../Component/Listing/Column/ActionsTest.php | 6 ++- .../Listing/Column/EditActionTest.php | 4 ++ .../Listing/Column/ViewActionTest.php | 50 +++++++++++++++---- 5 files changed, 61 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php index 6981a7983049e..4ffe4a6ad8774 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/BlockActionsTest.php @@ -56,10 +56,13 @@ protected function setUp() ->setMethods(['escapeHtmlAttr']) ->getMock(); - $this->blockActions = $objectManager->getObject(BlockActions::class, [ - 'context' => $context, - 'urlBuilder' => $this->urlBuilder - ]); + $this->blockActions = $objectManager->getObject( + BlockActions::class, + [ + 'context' => $context, + 'urlBuilder' => $this->urlBuilder + ] + ); $objectManager->setBackwardCompatibleProperty($this->blockActions, 'escaper', $this->escaper); } @@ -93,6 +96,7 @@ public function testPrepareDataSource() 'edit' => [ 'href' => 'test/url/edit', 'label' => __('Edit'), + '__disableTmpl' => true, ], 'delete' => [ 'href' => 'test/url/delete', @@ -102,6 +106,7 @@ public function testPrepareDataSource() 'message' => __('Are you sure you want to delete a %1 record?', $title), ], 'post' => true, + '__disableTmpl' => true, ], ], ], diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php index 32bbeed0788a3..53d8ee5220768 100644 --- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php +++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php @@ -65,6 +65,7 @@ public function testPrepareItemsByPageId() 'edit' => [ 'href' => 'test/url/edit', 'label' => __('Edit'), + '__disableTmpl' => true, ], 'delete' => [ 'href' => 'test/url/delete', @@ -75,6 +76,7 @@ public function testPrepareItemsByPageId() '__disableTmpl' => true, ], 'post' => true, + '__disableTmpl' => true, ], ], ], @@ -84,7 +86,6 @@ public function testPrepareItemsByPageId() ->method('escapeHtml') ->with($title) ->willReturn($title); - // Configure mocks and object data $urlBuilderMock->expects($this->any()) ->method('getUrl') @@ -106,7 +107,6 @@ public function testPrepareItemsByPageId() ], ] ); - $model->setName($name); $items = $model->prepareDataSource($items); // Run test diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ActionsTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ActionsTest.php index 056c7e71e1827..4a16acd98d827 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ActionsTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ActionsTest.php @@ -7,6 +7,9 @@ use Magento\Customer\Ui\Component\Listing\Column\Actions; +/** + * Class ActionsTest + */ class ActionsTest extends \PHPUnit\Framework\TestCase { /** @var Actions */ @@ -64,7 +67,8 @@ public function testPrepareDataSource() 'edit' => [ 'href' => 'http://magento.com/customer/index/edit', 'label' => new \Magento\Framework\Phrase('Edit'), - 'hidden' => false + 'hidden' => false, + '__disableTmpl' => true, ] ] ], diff --git a/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/EditActionTest.php b/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/EditActionTest.php index 2ac959d0a9881..22cc1c9e89fbe 100644 --- a/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/EditActionTest.php +++ b/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/EditActionTest.php @@ -9,6 +9,9 @@ use Magento\Store\Model\ScopeInterface; use Magento\Theme\Ui\Component\Listing\Column\EditAction; +/** + * Class EditActionTest + */ class EditActionTest extends \PHPUnit\Framework\TestCase { /** @var EditAction */ @@ -64,6 +67,7 @@ public function testPrepareDataSource($dataSourceItem, $scope, $scopeId) 'edit' => [ 'href' => 'http://magento.com/theme/design_config/edit', 'label' => new \Magento\Framework\Phrase('Edit'), + '__disableTmpl' => true, ] ], ]; diff --git a/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/ViewActionTest.php b/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/ViewActionTest.php index 03d1fe70f2f07..5e2fe51043885 100644 --- a/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/ViewActionTest.php +++ b/app/code/Magento/Theme/Test/Unit/Ui/Component/Listing/Column/ViewActionTest.php @@ -99,20 +99,52 @@ public function getPrepareDataSourceDataProvider() { return [ [ - ['name' => 'itemName', 'config' => []], - [['itemName' => '', 'entity_id' => 1]], - [['itemName' => ['view' => ['href' => 'url', 'label' => __('View')]], 'entity_id' => 1]], + [ + 'name' => 'itemName', + 'config' => [] + ], + [ + ['itemName' => '', 'entity_id' => 1] + ], + [ + [ + 'itemName' => [ + 'view' => [ + 'href' => 'url', + 'label' => __('View'), + '__disableTmpl' => true, + ] + ], + 'entity_id' => 1 + ] + ], '#', ['id' => 1] ], [ - ['name' => 'itemName', 'config' => [ - 'viewUrlPath' => 'url_path', - 'urlEntityParamName' => 'theme_id', - 'indexField' => 'theme_id'] + [ + 'name' => 'itemName', + 'config' => [ + 'viewUrlPath' => 'url_path', + 'urlEntityParamName' => 'theme_id', + 'indexField' => 'theme_id' + ] + ], + [ + ['itemName' => '', 'theme_id' => 2] + ], + [ + [ + 'itemName' => [ + 'view' => [ + 'href' => 'url', + 'label' => __('View'), + '__disableTmpl' => true, + ] + ], + 'theme_id' => 2 + ] ], - [['itemName' => '', 'theme_id' => 2]], - [['itemName' => ['view' => ['href' => 'url', 'label' => __('View')]], 'theme_id' => 2]], 'url_path', ['theme_id' => 2] ] From a9ca7a9858b4e07e88dc71930d05a07158209e6b Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Mon, 1 Jul 2019 17:02:59 +0300 Subject: [PATCH 0189/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) --- .../SearchAdapter/Query/Builder/Match.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index afd383c13421f..f8f70170de155 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Elasticsearch\SearchAdapter\Query\Builder; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; @@ -80,7 +82,8 @@ public function __construct( */ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType) { - $queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType); + $preparedValue = $this->prepareValue($requestQuery->getValue()); + $queryValue = $this->prepareQuery($preparedValue, $conditionType); $queries = $this->buildQueries($requestQuery->getMatches(), $queryValue); $requestQueryBoost = $requestQuery->getBoost() ?: 1; foreach ($queries as $query) { @@ -113,6 +116,19 @@ protected function prepareQuery($queryValue, $conditionType) ]; } + /** + * Removes special query characters which are cause of mysql error: '(', ')', '?' + * + * @param string $queryValue + * @return string + */ + private function prepareValue($queryValue) + { + $pattern = '/(\(|\)|\?)/'; + $replace = ''; + return preg_replace($pattern, $replace, $queryValue); + } + /** * Creates valid ElasticSearch search conditions from Match queries. * From ff714873a9f30fdaec4b7b61948f5f22b6e1abae Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 2 Jul 2019 22:30:49 +0300 Subject: [PATCH 0190/2437] Covering the Reorder functionality for an Out of Stock product --- ...ntCustomerElementNotVisibleActionGroup.xml | 16 ++++++ ...vigateToCustomerAccountPageActionGroup.xml | 17 ++++++ ...AdminCreateOrderAndCheckTheReorderTest.xml | 57 +++++++++++++++++++ .../Test/TestCase/CreateOrderBackendTest.xml | 1 + 4 files changed, 91 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml new file mode 100644 index 0000000000000..d6d66f9f2b708 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCustomerElementNotVisibleActionGroup"> + <arguments> + <argument name="selector" type="string"/> + </arguments> + <dontSeeElement selector="{{selector}}" stepKey="assertNotVisibleElement"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml new file mode 100644 index 0000000000000..ce7b023f1d57d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCustomerAccountPageActionGroup"> + <arguments> + <argument name="page" type="string" /> + </arguments> + <amOnPage url="{{page}}" stepKey="amOnTheCustomerPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml new file mode 100644 index 0000000000000..147d6ff85cecf --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderAndCheckTheReorderTest"> + <annotations> + <title value="v"/> + <stories value="MAGETWO-63924: 'Reorder' button is not visible for customer if ordered item is out of stock"/> + <description value="'Reorder' button is not visible for customer if ordered item is out of stock"/> + <features value="Sales"/> + <severity value="AVERAGE"/> + <group value="Sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + <createData entity="CashOnDeliveryPaymentMethodDefault" stepKey="cashOnDeliveryPaymentMethod"/> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + <createData entity="SimpleProduct_25" stepKey="simpleProduct"> + <field key="price">5</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="25"/> + </actionGroup> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod" /> + <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="frontendCustomerLogIn"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="NavigateToCustomerAccountPageActionGroup" stepKey="goToOrderHistoryPage"> + <argument name="page" value="{{StorefrontCustomerOrdersHistoryPage.url}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomerElementNotVisibleActionGroup" stepKey="checkReorderButton"> + <argument name="selector" value="{{StorefrontCustomerOrderViewSection.reorder}}"/> + </actionGroup> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + <actionGroup ref="logout" stepKey="adminLogout"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + </after> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml index 408b0fdcbb40f..b1e3b9a9d9f1e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml @@ -25,6 +25,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductsOutOfStock" /> </variation> <variation name="CreateOrderBackendTestVariation19" summary="'Reorder' button is not visible for customer if ordered item is out of stock" ticketId="MAGETWO-63924"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">catalogProductSimple::default_qty_1</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> From 02821fcbf2ed25a8e5133b75e8390096e39caa1e Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 2 Jul 2019 22:59:31 +0300 Subject: [PATCH 0191/2437] Improving the quantity usage --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 1 + .../Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 65a9d93cc6ac6..4f7a4622b450f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -40,6 +40,7 @@ <data key="sku" unique="suffix">x&trade;</data> </entity> <entity name="SimpleProduct_25" type="product" extends="SimpleProduct2"> + <data key="quantity">25</data> <requiredEntity type="product_extension_attribute">EavStock25</requiredEntity> </entity> <entity name="ApiSimpleProductWithCustomPrice" type="product" extends="ApiSimpleProduct"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 147d6ff85cecf..757550efcf83a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -32,7 +32,7 @@ </actionGroup> <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$simpleProduct$$"/> - <argument name="productQty" value="25"/> + <argument name="productQty" value="{{SimpleProduct_25.quantity}}"/> </actionGroup> <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod" /> <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> From 8421886d6e79a21f1cde45337d6e190ef2ee7a09 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 4 Jul 2019 13:44:26 +0400 Subject: [PATCH 0192/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Added automated test script. --- .../AdminProductAttributeActionGroup.xml | 6 ++ .../AdminCreateProductAttributeSection.xml | 1 + .../Test/Mftf/Data/CatalogSearchData.xml | 9 +- .../Mftf/Metadata/catalog_search-meta.xml | 3 + ...ontElasticsearchSearchInvalidValueTest.xml | 96 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index ed0c4387cdedf..c9ef3db45f5b7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -163,6 +163,7 @@ <argument name="ProductAttributeCode" type="string"/> </arguments> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <waitForPageLoad stepKey="waitForAttributeGridPgeLoad"/> <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttributeCode}}" stepKey="setAttributeCode"/> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> @@ -236,6 +237,11 @@ <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> </actionGroup> + <actionGroup name="CreateSearchableProductAttribute" extends="createProductAttribute" insertAfter="checkRequired"> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{StorefrontPropertiesSection.PageTitle}}" stepKey="waitTabLoad"/> + <selectOption selector="{{StorefrontPropertiesSection.isSearchable}}" userInput="Yes" stepKey="setSearchable"/> + </actionGroup> <!-- Inputs text default value and attribute code--> <actionGroup name="createProductAttributeWithTextField" extends="createProductAttribute" insertAfter="checkRequired"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 9b75f7e6908a2..8e10e1f302373 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -49,6 +49,7 @@ <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> + <element name="isSearchable" type="select" selector="#is_searchable"/> </section> <section name="WYSIWYGProductAttributeSection"> <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml index 6868456079110..58e2929090059 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml @@ -11,6 +11,9 @@ <entity name="SetMinQueryLengthToDefault" type="catalog_search_config_def"> <requiredEntity type="enable">DefaultMinQueryLength</requiredEntity> </entity> + <entity name="SetSearchEngineToDefault" type="catalog_search_config_def"> + <requiredEntity type="enable_engine">DefaultSearchEngine</requiredEntity> + </entity> <entity name="UncheckMinQueryLengthAndSet" type="catalog_search_config_query_length"> <requiredEntity type="number">SetMinQueryLengthToOne</requiredEntity> </entity> @@ -23,5 +26,7 @@ <entity name="SetMinQueryLengthToOne" type="number"> <data key="value">1</data> </entity> - -</entities> \ No newline at end of file + <entity name="DefaultSearchEngine" type="enable_engine"> + <data key="inherit">true</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml index 7405377249aa4..7e880262d5922 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml @@ -14,6 +14,9 @@ <object key="min_query_length" dataType="enable"> <field key="inherit">boolean</field> </object> + <object key="engine" dataType="enable_engine"> + <field key="inherit">boolean</field> + </object> </object> </object> </object> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml new file mode 100644 index 0000000000000..cf6e94f905aba --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StrorefrontElasticsearchSearchInvalidValueTest"> + <annotations> + <features value="Search"/> + <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> + <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17906"/> + <useCaseId value="MC-15759"/> + <group value="search"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Enable Elasticsearch--> + <comment userInput="Enable Elasticsearch" stepKey="commentEnableElasticsearch"/> + <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="enableElasticsearch"/> + <!--Set Minimal Query Length--> + <comment userInput="Set Minimal Query Length" stepKey="commentSetMinQueryLength"/> + <magentoCLI command="config:set catalog/search/min_query_length 2" stepKey="setMinQueryLength"/> + </before> + <after> + <!--Set configs to default--> + <comment userInput="Set configs to default" stepKey="commentSetDefault"/> + <createData entity="SetMinQueryLengthToDefault" stepKey="setMinimumQueryLengthToDefault"/> + <createData entity="SetSearchEngineToDefault" stepKey="setSearchEngineToDefault"/> + <!--Delete create data--> + <comment userInput="Delete create data" stepKey="commentDeletedData"/> + <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> + <argument name="ProductAttributeCode" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <waitForPageLoad stepKey="waitForAttributePageLoad"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{SimpleProduct.sku}}"/> + </actionGroup> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Create new searchable product attribute--> + <comment userInput="Create new searchable product attribute" stepKey="commentCreateAttribute"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <actionGroup ref="CreateSearchableProductAttribute" stepKey="createAttribute"> + <argument name="attribute" value="textProductAttribute"/> + </actionGroup> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> + <!--Assign attribute to the Default set--> + <comment userInput="Assign attribute to the Default set" stepKey="commentAssignToDefaultSet"/> + <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <!--Create product and fill new attribute field--> + <comment userInput="Create product and fill new attribute field" stepKey="commentCreateProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <fillField selector="{{AdminProductFormSection.attributeRequiredInput(textProductAttribute.attribute_code)}}" userInput="searchable" stepKey="fillTheAttributeRequiredInputField"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <!--Assert search results on storefront--> + <comment userInput="Assert search results on storefront" stepKey="commentAssertSearchResult"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFirstSearchTerm"> + <argument name="phrase" value="searchable"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductName"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForSecondSearchTerm"> + <argument name="phrase" value="?;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForThirdSearchTerm"> + <argument name="phrase" value="?anythingcangobetween;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForThirdSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForForthSearchTerm"> + <argument name="phrase" value="? anything at all ;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForForthSearchTerm"/> + </test> +</tests> From d32285cb23f2047777176bc7b2a6b4ab1685df3d Mon Sep 17 00:00:00 2001 From: mmularski <mmularczyk9@gmail.com> Date: Fri, 5 Jul 2019 13:40:43 +0200 Subject: [PATCH 0193/2437] Issue-709. Convert CreateAdminUserEntityTest to MFTF --- .../AdminCreateUserActionGroup.xml | 26 +++++++++++- .../Magento/User/Test/Mftf/Data/UserData.xml | 32 ++++++++++++++ .../Mftf/Section/AdminNewUserFormSection.xml | 1 + .../Test/AdminCreateActiveUserEntityTest.xml | 42 +++++++++++++++++++ .../AdminCreateInactiveUserEntityTest.xml | 41 ++++++++++++++++++ 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml index d550d855fcdd0..bcc7ec78e87a5 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml @@ -36,7 +36,6 @@ <!--Create new user with role--> <actionGroup name="AdminCreateUserWithRoleActionGroup"> <arguments> - <argument name="role"/> <argument name="user" defaultValue="newAdmin"/> </arguments> <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> @@ -50,9 +49,32 @@ <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> <scrollToTopOfPage stepKey="scrollToTopOfPage" /> <click stepKey="clickUserRole" selector="{{AdminCreateUserSection.userRoleTab}}"/> - <click stepKey="chooseRole" selector="{{AdminStoreSection.createdRoleInUserPage(role.name)}}"/> + <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(user.role)}}" stepKey="assignRole"/> <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser" /> <waitForPageLoad stepKey="waitForSaveTheUser" /> <see userInput="You saved the user." stepKey="seeSuccessMessage" /> </actionGroup> + + <!--Create new user with role and active/inactive setting--> + <actionGroup name="AdminCreateUserWithRoleAndIsActiveActionGroup"> + <arguments> + <argument name="user" defaultValue="newAdmin"/> + </arguments> + <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> + <waitForPageLoad stepKey="waitForUsersPage" /> + <fillField selector="{{AdminNewUserFormSection.username}}" userInput="{{user.username}}" stepKey="enterUserName" /> + <fillField selector="{{AdminNewUserFormSection.firstname}}" userInput="{{user.firstName}}" stepKey="enterFirstName" /> + <fillField selector="{{AdminNewUserFormSection.lastname}}" userInput="{{user.lastName}}" stepKey="enterLastName" /> + <fillField selector="{{AdminNewUserFormSection.email}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail" /> + <fillField selector="{{AdminNewUserFormSection.password}}" userInput="{{user.password}}" stepKey="enterPassword" /> + <fillField selector="{{AdminNewUserFormSection.passwordConfirmation}}" userInput="{{user.password}}" stepKey="confirmPassword" /> + <checkOption selector="{{AdminNewUserFormSection.userIsActive(user.is_active)}}" stepKey="checkIsActive" /> + <fillField selector="{{AdminNewUserFormSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> + <scrollToTopOfPage stepKey="scrollToTopOfPage" /> + <click stepKey="clickUserRole" selector="{{AdminNewUserFormSection.userRoleTab}}"/> + <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(user.role)}}" stepKey="assignRole"/> + <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> + <waitForPageLoad stepKey="waitForSaveTheUser" /> + <see userInput="You saved the user." stepKey="seeSuccessMessage" /> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index e665736ae28f1..636dd877bb639 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -74,4 +74,36 @@ <item>1</item> </array> </entity> + <entity name="activeAdmin" type="user"> + <data key="username" unique="suffix">AdminUser</data> + <data key="firstname" unique="suffix">FirstName</data> + <data key="lastname" unique="suffix">LastName</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">1</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> + <entity name="inactiveAdmin" type="user"> + <data key="username" unique="suffix">AdminUser</data> + <data key="firstname" unique="suffix">FirstName</data> + <data key="lastname" unique="suffix">LastName</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">0</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml index 9b030b216ce2c..79195067315db 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -19,6 +19,7 @@ <element name="password" type="input" selector="#page_tabs_main_section_content input[name='password']"/> <element name="passwordConfirmation" type="input" selector="#page_tabs_main_section_content input[name='password_confirmation']"/> <element name="interfaceLocale" type="select" selector="#page_tabs_main_section_content select[name='interface_locale']"/> + <element name="userIsActive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='{{var}}']" parameterized="true"/> <element name="currentPassword" type="input" selector="#page_tabs_main_section_content input[name='current_password']"/> <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml new file mode 100644 index 0000000000000..1cc2294a3d33e --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateActiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create active admin user"/> + <description value="Admin user should be able to create active admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="user" value="activeAdmin"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{activeAdmin.username}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{activeAdmin.password}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{activeAdmin.username}}" stepKey="fillUsernameSearch"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad time="10" stepKey="wait1"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{activeAdmin.username}}" stepKey="seeFoundUsername"/> + <actionGroup ref="logout" stepKey="logoutCreatedUser"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml new file mode 100644 index 0000000000000..0768f3c6e240e --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateInactiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create inactive admin user"/> + <description value="Admin user should be able to create inactive admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsernameSearch"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad time="10" stepKey="wait1"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{inactiveAdmin.username}}" stepKey="seeFoundUsername"/> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{inactiveAdmin.password}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeUserErrorMessage" /> + </test> +</tests> From cdf56a8105361fa4c4a339369b0bfd0128cee76a Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Mon, 8 Jul 2019 09:06:51 -0500 Subject: [PATCH 0194/2437] MC-17309: Sql builder readability --- .../Rule/Model/Condition/Sql/Builder.php | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 33e1bf97c3474..961474b9d286f 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -145,11 +145,8 @@ protected function _joinTablesToCollection( * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function _getMappedSqlCondition( - AbstractCondition $condition, - string $value = '', - bool $isDefaultStoreUsed = true - ): string { + protected function _getMappedSqlCondition(AbstractCondition $condition, string $value = ''): string + { $argument = $condition->getMappedSqlField(); // If rule hasn't valid argument - create negative expression to prevent incorrect rule behavior. @@ -241,6 +238,7 @@ protected function _getMappedSqlCombination( * @param AbstractCollection $collection * @param Combine $combine * @return void + * @throws \Magento\Framework\Exception\LocalizedException */ public function attachConditionToCollection( AbstractCollection $collection, @@ -250,29 +248,45 @@ public function attachConditionToCollection( $this->_joinTablesToCollection($collection, $combine); $whereExpression = (string)$this->_getMappedSqlCombination($combine); if (!empty($whereExpression)) { - if (!empty($combine->getConditions())) { - $conditions = ''; - $attributeField = ''; - foreach ($combine->getConditions() as $condition) { - if ($condition->getData('attribute') === \Magento\Catalog\Api\Data\ProductInterface::SKU) { - $conditions = $condition->getData('value'); - $attributeField = $condition->getMappedSqlField(); - } - } + $collection->getSelect()->where($whereExpression); + $this->buildConditions($collection, $combine); + } + } - $collection->getSelect()->where($whereExpression); + /** + * Build sql conditions from combination. + * + * @param AbstractCollection $collection + * @param Combine $combine + * @return void + */ + private function buildConditions(AbstractCollection $collection, Combine $combine) : void + { + if (!empty($combine->getConditions())) { + $conditions = ''; + $attributeField = ''; + foreach ($combine->getConditions() as $condition) { + if ($condition->getData('attribute') === \Magento\Catalog\Api\Data\ProductInterface::SKU) { + $conditions = $condition->getData('value'); + $attributeField = $condition->getMappedSqlField(); + } + } - if (!empty($conditions) && !empty($attributeField)) { - $conditions = explode(',', $conditions); - foreach ($conditions as &$condition) { - $condition = "'" . trim($condition) . "'"; - } - $conditions = implode(', ', $conditions); - $collection->getSelect()->order("FIELD($attributeField, $conditions)"); + if (!empty($conditions) && !empty($attributeField)) { + $conditions = explode(',', $conditions); + foreach ($conditions as &$condition) { + $condition = trim($condition); } - } else { - // Select ::where method adds braces even on empty expression - $collection->getSelect()->where($whereExpression); + $conditions = implode(', ', $conditions); + $collection->getSelect()->order( + $this->_connection->quoteInto( + "FIELD(?, ?)", + [ + $attributeField, + $conditions + ] + ) + ); } } } From 60c3c25db3b0ad5eae6f700dfbb6d60d8f64314f Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Mon, 8 Jul 2019 14:11:58 -0500 Subject: [PATCH 0195/2437] MC-16112: Signifyd Guarantee Option - fix test failure --- .../Magento/Ui/Component/Form/Element/AbstractOptionsField.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php index 1229fbeec77c7..d8d1733ffdef9 100644 --- a/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php +++ b/app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php @@ -11,6 +11,7 @@ /** * Class AbstractOptionsField * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.1.0 */ From 20ed50f4a77a961ad34869e3167dc3047a7cd7ad Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Tue, 9 Jul 2019 12:11:34 -0500 Subject: [PATCH 0196/2437] MC-17309: Sql builder readability --- app/code/Magento/Rule/Model/Condition/Sql/Builder.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 961474b9d286f..6b1692c9723db 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -149,9 +149,11 @@ protected function _getMappedSqlCondition(AbstractCondition $condition, string $ { $argument = $condition->getMappedSqlField(); - // If rule hasn't valid argument - create negative expression to prevent incorrect rule behavior. + // If rule hasn't valid argument - prevent incorrect rule behavior. if (empty($argument)) { return $this->_expressionFactory->create(['expression' => '1 = -1']); + } elseif (preg_match('/[^a-z0-9\-_\.\`]/i', $argument) > 0) { + throw new \Magento\Framework\Exception\LocalizedException(__('Invalid field')); } $conditionOperator = $condition->getOperatorForValidate(); @@ -192,7 +194,6 @@ protected function _getMappedSqlCondition(AbstractCondition $condition, string $ ); } } - return $this->_expressionFactory->create( ['expression' => $expression] ); From 06a7fcd3295aa5429ccae25c6080d1625fd0aa83 Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Tue, 9 Jul 2019 14:25:45 -0500 Subject: [PATCH 0197/2437] MC-17309: Sql builder readability --- app/code/Magento/Rule/Model/Condition/Sql/Builder.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index 6b1692c9723db..f44b788354103 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -145,8 +145,11 @@ protected function _joinTablesToCollection( * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function _getMappedSqlCondition(AbstractCondition $condition, string $value = ''): string - { + protected function _getMappedSqlCondition( + AbstractCondition $condition, + string $value = '', + bool $isDefaultStoreUsed = true + ): string { $argument = $condition->getMappedSqlField(); // If rule hasn't valid argument - prevent incorrect rule behavior. From 019f8dc91394681f4b5b9adccabb7ac51cdf79da Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Tue, 9 Jul 2019 21:37:09 -0400 Subject: [PATCH 0198/2437] Prevent updates to child cart items via updateCartItems mutation --- .../Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php index db6a43513cc30..cd687d86d87ce 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php @@ -105,6 +105,11 @@ private function processCartItems(Quote $cart, array $items): void $itemId = (int)$item['cart_item_id']; $customizableOptions = $item['customizable_options'] ?? []; + $cartItem = $cart->getItemById($itemId); + if ($cartItem && $cartItem->getParentItemId()) { + throw new GraphQlInputException(__('Child items may not be updated.')); + } + if (count($customizableOptions) === 0 && !isset($item['quantity'])) { throw new GraphQlInputException(__('Required parameter "quantity" for "cart_items" is missing.')); } From 04f44c3295679ce5efbf21b37bafca8717887579 Mon Sep 17 00:00:00 2001 From: Mark Berube <berube@adobe.com> Date: Mon, 15 Jul 2019 14:12:33 -0500 Subject: [PATCH 0199/2437] MC-18101: Validate file deletion path on data export --- .../ImportExport/Controller/Adminhtml/Export/File/Delete.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php index 6996ba90c3e10..4ba0d024ef68f 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/File/Delete.php @@ -67,6 +67,11 @@ public function execute() } $directory = $this->filesystem->getDirectoryRead(DirectoryList::VAR_DIR); $path = $directory->getAbsolutePath() . 'export/' . $fileName; + + if (!$directory->isFile($path)) { + throw new LocalizedException(__('Sorry, but the data is invalid or the file is not uploaded.')); + } + $this->file->deleteFile($path); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); From c98cfd565d5f660bca73221214ac8241105777e7 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 16 Jul 2019 16:54:17 +0300 Subject: [PATCH 0200/2437] MC-18099: Changes in Downloadable product upload controller --- .../Adminhtml/Downloadable/File/Upload.php | 30 +++++++++++------- .../Adminhtml/Downloadable/FileTest.php | 31 +++++++++++++++++-- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php index 83b2797050db9..c8f7a4ff4a507 100644 --- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php +++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php @@ -7,6 +7,8 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\LocalizedException; /** * Class Upload @@ -76,23 +78,27 @@ public function __construct( */ public function execute() { - $type = $this->getRequest()->getParam('type'); - $tmpPath = ''; - if ($type == 'samples') { - $tmpPath = $this->_sample->getBaseTmpPath(); - } elseif ($type == 'links') { - $tmpPath = $this->_link->getBaseTmpPath(); - } elseif ($type == 'link_samples') { - $tmpPath = $this->_link->getBaseSampleTmpPath(); - } - try { + $type = $this->getRequest()->getParam('type'); + $tmpPath = ''; + if ($type == 'samples') { + $tmpPath = $this->_sample->getBaseTmpPath(); + } elseif ($type == 'links') { + $tmpPath = $this->_link->getBaseTmpPath(); + } elseif ($type == 'link_samples') { + $tmpPath = $this->_link->getBaseSampleTmpPath(); + } else { + throw new LocalizedException(__('Upload type can not be determined.')); + } + $uploader = $this->uploaderFactory->create(['fileId' => $type]); $result = $this->_fileHelper->uploadFromTmp($tmpPath, $uploader); if (!$result) { - throw new \Exception('File can not be moved from temporary folder to the destination folder.'); + throw new FileSystemException( + __('File can not be moved from temporary folder to the destination folder.') + ); } unset($result['tmp_name'], $result['path']); @@ -101,7 +107,7 @@ public function execute() $relativePath = rtrim($tmpPath, '/') . '/' . ltrim($result['file'], '/'); $this->storageDatabase->saveFile($relativePath); } - } catch (\Exception $e) { + } catch (\Throwable $e) { $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()]; } diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php index 60c2a41fae49f..ac35fa9a23b1d 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php @@ -1,4 +1,9 @@ <?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + namespace Magento\Downloadable\Controller\Adminhtml\Downloadable; use Magento\Framework\Serialize\Serializer\Json; @@ -7,9 +12,10 @@ /** * Magento\Downloadable\Controller\Adminhtml\Downloadable\File * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. * @magentoAppArea adminhtml + * + * phpcs:disable Magento2.Functions.DiscouragedFunction + * phpcs:disable Magento2.Security.Superglobal */ class FileTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -90,4 +96,25 @@ public function extensionsDataProvider() ['sample.php7'], ]; } + + /** + * @return void + */ + public function testUploadWrongUploadType(): void + { + $postData = [ + 'type' => [ + 'tmp_name' => 'test.txt', + 'name' => 'result.txt', + ], + ]; + $this->getRequest()->setPostValue($postData); + + $this->getRequest()->setMethod('POST'); + $this->dispatch('backend/admin/downloadable_file/upload'); + $body = $this->getResponse()->getBody(); + $result = Bootstrap::getObjectManager()->get(Json::class)->unserialize($body); + $this->assertEquals('Upload type can not be determined.', $result['error']); + $this->assertEquals(0, $result['errorcode']); + } } From 54986eddee7fc6311a03978640a17e3b9085c02f Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 16 Jul 2019 13:44:56 -0500 Subject: [PATCH 0201/2437] MC-17700: Downloadable Product links --- .../Mftf/Test/SearchEntityResultsTest.xml | 2 + ...ownloadableProductFromShoppingCartTest.xml | 2 + ...OnePageCheckoutWithAllProductTypesTest.xml | 2 + ...dDownloadableProductToShoppingCartTest.xml | 2 + ...oadableProductFromMiniShoppingCartTest.xml | 2 + .../StorefrontClearAllCompareProductsTest.xml | 2 + .../Console/Command/DomainsAddCommand.php | 118 ++++++++++++++ .../Console/Command/DomainsRemoveCommand.php | 120 ++++++++++++++ .../Console/Command/DomainsShowCommand.php | 79 +++++++++ .../Model/Link/ContentValidator.php | 29 +++- .../Model/Sample/ContentValidator.php | 24 ++- .../Model/Url/DomainValidator.php | 124 ++++++++++++++ .../Patch/Data/AddDownloadableHostsConfig.php | 151 ++++++++++++++++++ ...AddDefaultImageDownloadableProductTest.xml | 2 + ...AddDefaultVideoDownloadableProductTest.xml | 7 +- ...bleProductAndAssignItToCustomStoreTest.xml | 2 + ...wnloadableProductWithCustomOptionsTest.xml | 2 + ...loadableProductWithDefaultSetLinksTest.xml | 2 + ...eDownloadableProductWithGroupPriceTest.xml | 2 + ...nCreateDownloadableProductWithLinkTest.xml | 2 + ...DownloadableProductWithManageStockTest.xml | 2 + ...oadableProductWithOutOfStockStatusTest.xml | 2 + ...ownloadableProductWithSpecialPriceTest.xml | 2 + ...teDownloadableProductWithTierPriceText.xml | 6 + ...ductWithoutFillingQuantityAndStockTest.xml | 2 + ...wnloadableProductWithoutTaxClassIdTest.xml | 2 + .../AdminDeleteDownloadableProductTest.xml | 2 + ...oveDefaultImageDownloadableProductTest.xml | 2 + ...oveDefaultVideoDownloadableProductTest.xml | 6 + ...ceCatalogSearchDownloadableProductTest.xml | 20 +++ ...loadableProductFromGuestToCustomerTest.xml | 2 + ...ductsListWidgetDownloadableProductTest.xml | 7 + .../Unit/Model/Link/ContentValidatorTest.php | 13 ++ app/code/Magento/Downloadable/etc/di.xml | 9 ++ app/code/Magento/Downloadable/i18n/en_US.csv | 1 + .../Api/ProductRepositoryInterfaceTest.php | 48 +++++- .../Api/ProductRepositoryTest.php | 18 +++ .../Mtf/Util/Command/Cli/EnvWhitelist.php | 40 +++++ .../ProductTypeSwitchingOnUpdateTest.php | 15 +- .../AddProductsToShoppingCartEntityTest.php | 16 +- ...ePageCheckoutOfflinePaymentMethodsTest.php | 29 ++++ ...eProductFromMiniShoppingCartEntityTest.php | 24 ++- .../TestCase/PrintOrderFrontendGuestTest.php | 18 ++- .../AddProductToWishlistEntityTest.php | 27 +++- ...ProductInCustomerWishlistOnBackendTest.php | 27 +++- ...ProductInCustomerWishlistOnBackendTest.php | 27 +++- ...ote_with_downloadable_product_rollback.php | 8 + .../Model/Url/DomainValidatorTest.php | 93 +++++++++++ ...able_product_with_files_and_sample_url.php | 12 +- ...uct_with_files_and_sample_url_rollback.php | 11 ++ .../_files/product_downloadable.php | 25 ++- .../_files/product_downloadable_rollback.php | 24 ++- .../product_downloadable_with_files.php | 21 ++- 53 files changed, 1191 insertions(+), 46 deletions(-) create mode 100644 app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php create mode 100644 app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php create mode 100644 app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php create mode 100644 app/code/Magento/Downloadable/Model/Url/DomainValidator.php create mode 100644 app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php create mode 100644 dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/EnvWhitelist.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_downloadable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 19db201e91f40..f98e715963d2e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -368,6 +368,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> @@ -379,6 +380,7 @@ <after> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml index 1a51e1e02fe86..1eee2d510e7bc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create downloadable product --> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink"> @@ -32,6 +33,7 @@ <after> <!-- Delete downloadable product --> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Add downloadable product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 3ec73aec580d5..56d1b97974bbc 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="_defaultCategory" stepKey="createCategory"/> @@ -109,6 +110,7 @@ <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Add Simple Product to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index 7a4655bb19ce3..734cedd2c1a3a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -18,6 +18,7 @@ </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> <createData entity="downloadableLink1" stepKey="addDownloadableLink1"> @@ -32,6 +33,7 @@ <after> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!-- Open Downloadable Product page --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index 461139b6d4b3f..ed2e2c62f54b3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -19,6 +19,7 @@ </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"/> @@ -31,6 +32,7 @@ <after> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!-- Open Downloadable Product page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index 2b88657c6ca2b..e27650ff251db 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -20,6 +20,7 @@ </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create Simple Customer --> <createData entity="Simple_US_Customer_CA" stepKey="createSimpleCustomer1"/> @@ -94,6 +95,7 @@ <deleteData createDataKey="createBundleProduct1" stepKey="deleteBundleProduct1"/> <deleteData createDataKey="createGroupedProduct1" stepKey="deleteGroupedProduct1"/> <deleteData createDataKey="createDownloadableProduct1" stepKey="deleteDownloadableProduct1"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer1"> diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php new file mode 100644 index 0000000000000..8158296678dea --- /dev/null +++ b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php @@ -0,0 +1,118 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Downloadable\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; + +use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; +use Magento\Downloadable\Model\Url\DomainValidator; +use Magento\Framework\Config\File\ConfigFilePool; + +/** + * Class DomainsAddCommand + * + * Command for adding downloadable domain to the whitelist + */ +class DomainsAddCommand extends Command +{ + /** + * Name of domains input argument + */ + const INPUT_KEY_DOMAINS = 'domains'; + + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @var DomainValidator + */ + private $domainValidator; + + /** + * DomainsAddCommand constructor. + * + * @param ConfigWriter $configWriter + * @param DomainValidator $domainValidator + */ + public function __construct( + ConfigWriter $configWriter, + DomainValidator $domainValidator + ) { + $this->configWriter = $configWriter; + $this->domainValidator = $domainValidator; + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $description = 'Add domains to the downloadable domains whitelist'; + + $this->setName('downloadable:domains:add') + ->setDescription($description) + ->setDefinition( + [ + new InputArgument( + self::INPUT_KEY_DOMAINS, + InputArgument::IS_ARRAY, + 'Domains name' + ) + ] + ); + parent::configure(); + } + + /** + * @inheritdoc + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { + $newDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); + $newDomains = array_filter(array_map('trim', $newDomains), 'strlen'); + + $whitelist = $this->domainValidator->getEnvDomainWhitelist() ?: []; + foreach ($newDomains as $newDomain) { + if (in_array($newDomain, $whitelist)) { + $output->writeln( + "$newDomain is already in the whitelist" + ); + continue; + } else { + array_push($whitelist, $newDomain); + $output->writeln( + "$newDomain was added to the whitelist" + ); + } + } + + $this->configWriter->saveConfig( + [ + ConfigFilePool::APP_ENV => [ + $this->domainValidator::PARAM_DOWNLOADABLE_DOMAINS => $whitelist + ] + ] + ); + } + + } catch (\Exception $e) { + $output->writeln('<error>' . $e->getMessage() . '</error>'); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln($e->getTraceAsString()); + } + return; + } + } +} diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php new file mode 100644 index 0000000000000..1fa02ae6359b4 --- /dev/null +++ b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php @@ -0,0 +1,120 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Downloadable\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputArgument; + +use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; +use Magento\Downloadable\Model\Url\DomainValidator; +use Magento\Framework\Config\File\ConfigFilePool; + +/** + * Class DomainsRemoveCommand + * + * Command for removing downloadable domain from the whitelist + */ +class DomainsRemoveCommand extends Command +{ + /** + * Name of domains input argument + */ + const INPUT_KEY_DOMAINS = 'domains'; + + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @var DomainValidator + */ + private $domainValidator; + + /** + * DomainsRemoveCommand constructor. + * + * @param ConfigWriter $configWriter + * @param DomainValidator $domainValidator + */ + public function __construct( + ConfigWriter $configWriter, + DomainValidator $domainValidator + ) { + $this->configWriter = $configWriter; + $this->domainValidator = $domainValidator; + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $description = 'Remove domains from the downloadable domains whitelist'; + + $this->setName('downloadable:domains:remove') + ->setDescription($description) + ->setDefinition( + [ + new InputArgument( + self::INPUT_KEY_DOMAINS, + InputArgument::IS_ARRAY, + 'Domain names' + ) + ] + ); + parent::configure(); + } + + /** + * @inheritdoc + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { + $removeDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); + $removeDomains = array_filter(array_map('trim', $removeDomains), 'strlen'); + + $whitelist = $this->domainValidator->getEnvDomainWhitelist() ?: []; + foreach ($removeDomains as $removeDomain) { + if (in_array($removeDomain, $whitelist)) { + $index = array_search($removeDomain, $whitelist); + unset($whitelist[$index]); + $output->writeln( + "$removeDomain was removed from the whitelist" + ); + continue; + } else { + $output->writeln( + "$removeDomain is absent in the whitelist" + ); + } + } + + $this->configWriter->saveConfig( + [ + ConfigFilePool::APP_ENV => [ + $this->domainValidator::PARAM_DOWNLOADABLE_DOMAINS => $whitelist + ] + ], + true + ); + } + + } catch (\Exception $e) { + $output->writeln('<error>' . $e->getMessage() . '</error>'); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln($e->getTraceAsString()); + } + return; + } + } +} diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php new file mode 100644 index 0000000000000..70e031eb3e410 --- /dev/null +++ b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php @@ -0,0 +1,79 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Downloadable\Console\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputInterface; + +use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; +use Magento\Downloadable\Model\Url\DomainValidator; + +/** + * Class DomainsAddCommand + * + * Command for listing allowed downloadable domains + */ +class DomainsShowCommand extends Command +{ + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @var DomainValidator + */ + private $domainValidator; + + /** + * DomainsShowCommand constructor. + * + * @param ConfigWriter $configWriter + * @param DomainValidator $domainValidator + */ + public function __construct( + ConfigWriter $configWriter, + DomainValidator $domainValidator + ) { + $this->configWriter = $configWriter; + $this->domainValidator = $domainValidator; + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $description = 'Display downloadable domains whitelist'; + + $this->setName('downloadable:domains:show') + ->setDescription($description); + parent::configure(); + } + + /** + * @inheritdoc + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + $whitelist = implode("\n", $this->domainValidator->getEnvDomainWhitelist() ?: []); + $output->writeln( + "Downloadable domains whitelist:\n$whitelist" + ); + + } catch (\Exception $e) { + $output->writeln('<error>' . $e->getMessage() . '</error>'); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln($e->getTraceAsString()); + } + return; + } + } +} diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php index 8497bf7de6592..08345c8079ddc 100644 --- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php @@ -12,12 +12,23 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Url\Validator as UrlValidator; +use Magento\Downloadable\Model\Url\DomainValidator; /** * Class to validate Link Content. */ class ContentValidator { + /** + * @var DomainValidator + */ + private $domainValidator; + + /** + * @var File + */ + private $fileHelper; + /** * @var FileContentValidator */ @@ -28,24 +39,22 @@ class ContentValidator */ protected $urlValidator; - /** - * @var File - */ - private $fileHelper; - /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator * @param File|null $fileHelper + * @param DomainValidator|null $domainValidator */ public function __construct( FileContentValidator $fileContentValidator, UrlValidator $urlValidator, - File $fileHelper = null + File $fileHelper = null, + DomainValidator $domainValidator = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); + $this->domainValidator = $domainValidator ?? ObjectManager::getInstance()->get(DomainValidator::class); } /** @@ -91,7 +100,9 @@ public function isValid(LinkInterface $link, $validateLinkContent = true, $valid protected function validateLinkResource(LinkInterface $link) { if ($link->getLinkType() === 'url') { - if (!$this->urlValidator->isValid($link->getLinkUrl())) { + if (!$this->urlValidator->isValid($link->getLinkUrl()) + || !$this->domainValidator->isValid($link->getLinkUrl()) + ) { throw new InputException(__('Link URL must have valid format.')); } } elseif ($link->getLinkFileContent()) { @@ -113,7 +124,9 @@ protected function validateLinkResource(LinkInterface $link) protected function validateSampleResource(LinkInterface $link) { if ($link->getSampleType() === 'url') { - if (!$this->urlValidator->isValid($link->getSampleUrl())) { + if (!$this->urlValidator->isValid($link->getSampleUrl()) + || !$this->domainValidator->isValid($link->getSampleUrl()) + ) { throw new InputException(__('Sample URL must have valid format.')); } } elseif ($link->getSampleFileContent()) { diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php index 7348b04793a8f..7b8c65580f160 100644 --- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php @@ -12,12 +12,23 @@ use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Url\Validator as UrlValidator; +use Magento\Downloadable\Model\Url\DomainValidator; /** * Class to validate Sample Content. */ class ContentValidator { + /** + * @var File + */ + private $fileHelper; + + /** + * @var DomainValidator + */ + private $domainValidator; + /** * @var UrlValidator */ @@ -28,24 +39,22 @@ class ContentValidator */ protected $fileContentValidator; - /** - * @var File - */ - private $fileHelper; - /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator * @param File|null $fileHelper + * @param DomainValidator|null $domainValidator */ public function __construct( FileContentValidator $fileContentValidator, UrlValidator $urlValidator, - File $fileHelper = null + File $fileHelper = null, + DomainValidator $domainValidator = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); + $this->domainValidator = $domainValidator ?? ObjectManager::getInstance()->get(DomainValidator::class); } /** @@ -79,7 +88,8 @@ public function isValid(SampleInterface $sample, $validateSampleContent = true) protected function validateSampleResource(SampleInterface $sample) { if ($sample->getSampleType() === 'url') { - if (!$this->urlValidator->isValid($sample->getSampleUrl())) { + if (!$this->urlValidator->isValid($sample->getSampleUrl()) + || !$this->domainValidator->isValid($sample->getSampleUrl())) { throw new InputException(__('Sample URL must have valid format.')); } } elseif ($sample->getSampleFileContent()) { diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php new file mode 100644 index 0000000000000..5cdd954edb15f --- /dev/null +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Model\Url; + +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Validator\Ip as IpValidator; +use Zend\Uri\Uri as UriHandler; + +/** + * Class is responsible for checking if downloadable product link domain is allowed. + */ +class DomainValidator extends \Zend_Validate_Abstract +{ + /** + * Invalid host message key + */ + const INVALID_HOST = 'invalidHost'; + + /** + * Path to the allowed domains in the deployment config + */ + const PARAM_DOWNLOADABLE_DOMAINS = 'downloadable_domains'; + + /** + * @var IpValidator + */ + private $ipValidator; + + /** + * @var UriHandler + */ + private $uriHandler; + + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** + * @param DeploymentConfig $deploymentConfig + * @param IpValidator $ipValidator + * @param UriHandler $uriHandler + */ + public function __construct( + DeploymentConfig $deploymentConfig, + IpValidator $ipValidator, + UriHandler $uriHandler + ) { + $this->deploymentConfig = $deploymentConfig; + $this->ipValidator = $ipValidator; + $this->uriHandler = $uriHandler; + + $this->initMessageTemplates(); + } + + /** + * Validate url input. + * + * Assert parsed host of $value is contained within environment whitelist + * + * @param string $value + * @return bool + */ + public function isValid($value): bool + { + $host = $this->getHost($value); + + $isIpAddress = $this->ipValidator->isValid($host); + $isValid = !$isIpAddress && in_array($host, $this->getEnvDomainWhitelist()); + + if (!$isValid) { + $this->_error(self::INVALID_HOST, $host); + } + + return $isValid; + } + + /** + * Get environment whitelist + * + * @return array + */ + public function getEnvDomainWhitelist(): array + { + return array_map('strtolower', $this->deploymentConfig->get(self::PARAM_DOWNLOADABLE_DOMAINS) ?? []); + } + + /** + * Extract host from url + * + * @param string $url + * @return string + */ + private function getHost($url): string + { + $host = $this->uriHandler->parse($url)->getHost(); + + if ($host === null) { + return ''; + } + + // ipv6 hosts are brace-delimited in url; they are removed here for subsequent validation + return trim($host, '[] '); + } + + /** + * Initialize message templates with translating + * + * @return void + */ + private function initMessageTemplates() + { + if (!$this->_messageTemplates) { + $this->_messageTemplates = [ + self::INVALID_HOST => __('Host "%value%" is not allowed.'), + ]; + } + } +} diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php new file mode 100644 index 0000000000000..d63d28256809e --- /dev/null +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Setup\Patch\Data; + +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Zend\Uri\Uri as UriHandler; +use Magento\Framework\Url\ScopeResolverInterface; +use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; +use Magento\Downloadable\Model\Url\DomainValidator; +use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Framework\Setup\ModuleDataSetupInterface; + +/** + * Adding base url as allowed downloadable domain. + */ +class AddDownloadableHostsConfig implements DataPatchInterface +{ + /** + * @var UriHandler + */ + private $uriHandler; + + /** + * @var ScopeResolverInterface + */ + private $scopeResolver; + + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var array + */ + private $whitelist = []; + + /** + * AddDownloadableHostsConfig constructor. + * + * @param UriHandler $uriHandler + * @param ScopeResolverInterface $scopeResolver + * @param ConfigWriter $configWriter + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct( + UriHandler $uriHandler, + ScopeResolverInterface $scopeResolver, + ConfigWriter $configWriter, + ModuleDataSetupInterface $moduleDataSetup + ) { + $this->uriHandler = $uriHandler; + $this->scopeResolver = $scopeResolver; + $this->configWriter = $configWriter; + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * @inheritdoc + */ + public function apply() + { + if ($this->moduleDataSetup->tableExists('downloadable_link')) { + $select = $this->moduleDataSetup->getConnection() + ->select() + ->from( + $this->moduleDataSetup->getTable('downloadable_link'), + ['link_url'] + )->where('link_type = ?', 'url'); + + foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { + $this->addHost($link['link_url']); + } + + $select = $this->moduleDataSetup->getConnection() + ->select() + ->from( + $this->moduleDataSetup->getTable('downloadable_link'), + ['sample_url'] + )->where('sample_type = ?', 'url'); + + foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { + $this->addHost($link['sample_url']); + } + } + + if ($this->moduleDataSetup->tableExists('downloadable_sample')) { + $select = $this->moduleDataSetup->getConnection() + ->select() + ->from( + $this->moduleDataSetup->getTable('downloadable_sample'), + ['sample_url'] + )->where('sample_type = ?', 'url'); + + foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { + $this->addHost($link['sample_url']); + } + } + + foreach ($this->scopeResolver->getScopes() as $scope) { + $this->addHost($scope->getBaseUrl()); + } + + $this->configWriter->saveConfig( + [ + ConfigFilePool::APP_ENV => [ + DomainValidator::PARAM_DOWNLOADABLE_DOMAINS => array_unique($this->whitelist) + ] + ] + ); + } + + /** + * Add host to whitelist + * + * @param string $url + */ + private function addHost($url) + { + $host = $this->uriHandler->parse($url)->getHost(); + if ($host && !in_array($host, $this->whitelist)) { + $this->whitelist[] = $host; + } + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml index 3d779740849c5..15e64b9bbd35f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml @@ -19,9 +19,11 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </after> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml index a7acdfded29b6..4bbd815e8db01 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -18,7 +18,12 @@ <testCaseId value="MC-114"/> <group value="Downloadable"/> </annotations> - + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> <!-- Create a downloadable product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index 5d7e4518525f7..fc638633fbf3f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -25,6 +25,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> </before> <after> <!-- Delete category --> @@ -40,6 +41,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create store view --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml index 0ae2c1254be01..f45e5f1a47e43 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create Downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index eadefabb8bbcb..d9b4e8087cbc9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml index 8bd4305e6b358..afac869d69c3f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml index d8bd641e84e55..8b38f91f8719c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -27,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml index 3efd4b8ab276f..081230760e1d4 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml index 07f7c40bb3560..a815d838c29d9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create Downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml index 275e72b2ec8cb..f0cc503f74c4d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml index ca4e560506ad0..f6455766a0907 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml @@ -19,6 +19,12 @@ <group value="Downloadable"/> <group value="mtf_migrated"/> </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> <remove keyForRemoval="addCustomerGroupPrice"/> <!-- Add tier price to product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml index f326a047c32b5..6e29fe13d85e6 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml index 8e33a082d0ba2..757ac7958f5cd 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml @@ -20,6 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> @@ -37,6 +38,7 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml index d3c2d6e5d71a4..83e0c587c078e 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml @@ -18,6 +18,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="DownloadableProductWithTwoLink" stepKey="createDownloadableProduct"> @@ -33,6 +34,7 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteDownloadableProductFilteredBySkuAndName"> <argument name="product" value="$$createDownloadableProduct$$"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 3ee6cef47738b..3597c12e82df0 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -19,9 +19,11 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </after> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index d8bbbb2b4d62b..bc929cd3a68c7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -18,6 +18,12 @@ <testCaseId value="MC-207"/> <group value="Downloadable"/> </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> <!-- Create a downloadable product --> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml index 66177b6875dd9..19477fb804848 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml @@ -19,6 +19,7 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -27,6 +28,9 @@ <requiredEntity createDataKey="product"/> </createData> </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> </test> <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> <annotations> @@ -39,6 +43,7 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -47,6 +52,9 @@ <requiredEntity createDataKey="product"/> </createData> </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> </test> <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> <annotations> @@ -59,6 +67,7 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -67,6 +76,9 @@ <requiredEntity createDataKey="product"/> </createData> </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> </test> <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> <annotations> @@ -79,6 +91,7 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -87,6 +100,9 @@ <requiredEntity createDataKey="product"/> </createData> </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> </test> <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> <annotations> @@ -99,6 +115,7 @@ <group value="Downloadable"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -107,5 +124,8 @@ <requiredEntity createDataKey="product"/> </createData> </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> </test> </tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml index b960d15b2fdf1..25dfe1adcb7c8 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-16011"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> <magentoCLI command="config:set {{EnableGuestCheckoutWithDownloadableItems.path}} {{EnableGuestCheckoutWithDownloadableItems.value}}" stepKey="enableGuestCheckoutWithDownloadableItems" /> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> @@ -31,6 +32,7 @@ <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!--Step 1: Go to Storefront as Guest--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml index 4864d11c884bc..9fed2d68c98ca 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -20,6 +20,13 @@ <group value="WYSIWYGDisabled"/> </annotations> + <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + </before> + <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + </after> + <!-- A Cms page containing the New Products Widget gets created here via extends --> <!-- Create a Downloadable product to appear in the widget --> diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php index 771fbc37e5e13..152e3699f9691 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php @@ -28,6 +28,11 @@ class ContentValidatorTest extends \PHPUnit\Framework\TestCase */ protected $urlValidatorMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $domainValidatorMock; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -52,6 +57,7 @@ protected function setUp() $this->fileValidatorMock = $this->createMock(\Magento\Downloadable\Model\File\ContentValidator::class); $this->urlValidatorMock = $this->createMock(\Magento\Framework\Url\Validator::class); + $this->domainValidatorMock = $this->createMock(\Magento\Downloadable\Model\Url\DomainValidator::class); $this->linkFileMock = $this->createMock(\Magento\Downloadable\Api\Data\File\ContentInterface::class); $this->sampleFileMock = $this->createMock(\Magento\Downloadable\Api\Data\File\ContentInterface::class); $this->fileMock = $this->createMock(File::class); @@ -62,6 +68,7 @@ protected function setUp() 'fileContentValidator' => $this->fileValidatorMock, 'urlValidator' => $this->urlValidatorMock, 'fileHelper' => $this->fileMock, + 'domainValidator' => $this->domainValidatorMock, ] ); } @@ -83,6 +90,7 @@ public function testIsValid() ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $linkMock = $this->getLinkMock($linkData); $this->assertTrue($this->validator->isValid($linkMock)); } @@ -103,6 +111,7 @@ public function testIsValidSkipLinkContent() ]; $this->fileValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true)); $linkMock = $this->getLinkMock($linkData); $this->assertTrue($this->validator->isValid($linkMock, false)); } @@ -123,6 +132,7 @@ public function testIsValidSkipSampleContent() ]; $this->fileValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true)); $linkMock = $this->getLinkMock($linkData); $this->assertTrue($this->validator->isValid($linkMock, true, false)); } @@ -146,6 +156,7 @@ public function testIsValidThrowsExceptionIfSortOrderIsInvalid($sortOrder) ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); } @@ -181,6 +192,7 @@ public function testIsValidThrowsExceptionIfPriceIsInvalid($price) ]; $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); } @@ -214,6 +226,7 @@ public function testIsValidThrowsExceptionIfNumberOfDownloadsIsInvalid($numberOf 'sample_type' => 'file', ]; $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); + $this->domainValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true)); $contentMock = $this->getLinkMock($linkContentData); $this->validator->isValid($contentMock); diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml index 4e9b0b55afb0b..8ee05029333b8 100644 --- a/app/code/Magento/Downloadable/etc/di.xml +++ b/app/code/Magento/Downloadable/etc/di.xml @@ -164,4 +164,13 @@ <argument name="connectionName" xsi:type="string">indexer</argument> </arguments> </type> + <type name="Magento\Framework\Console\CommandListInterface"> + <arguments> + <argument name="commands" xsi:type="array"> + <item name="addDomainsCommand" xsi:type="object">Magento\Downloadable\Console\Command\DomainsAddCommand</item> + <item name="removeDomainsCommand" xsi:type="object">Magento\Downloadable\Console\Command\DomainsRemoveCommand</item> + <item name="showDomainsCommand" xsi:type="object">Magento\Downloadable\Console\Command\DomainsShowCommand</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Downloadable/i18n/en_US.csv b/app/code/Magento/Downloadable/i18n/en_US.csv index 87427bf483966..6158f1c579722 100644 --- a/app/code/Magento/Downloadable/i18n/en_US.csv +++ b/app/code/Magento/Downloadable/i18n/en_US.csv @@ -118,3 +118,4 @@ Downloads,Downloads "Use Content-Disposition","Use Content-Disposition" "Disable Guest Checkout if Cart Contains Downloadable Items","Disable Guest Checkout if Cart Contains Downloadable Items" "Guest checkout will only work with shareable.","Guest checkout will only work with shareable." +"Host ""%value"" is not allowed.","Host ""%value"" is not allowed." diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 3e935e1d7ae9b..8c82f2ed84538 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -9,6 +9,8 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; +use Magento\Downloadable\Console\Command\DomainsAddCommand; +use Magento\Downloadable\Console\Command\DomainsRemoveCommand; use Magento\Downloadable\Model\Link; use Magento\Store\Model\Store; use Magento\Store\Model\Website; @@ -22,6 +24,7 @@ use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Webapi\Exception as HTTPExceptionCodes; +use Symfony\Component\Console\Tester\CommandTester; /** * @magentoAppIsolation enabled @@ -55,6 +58,34 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ], ]; + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var DomainsAddCommand $domainsAddCommand */ + $domainsAddCommand = $objectManager->get(DomainsAddCommand::class); + $command = new CommandTester($domainsAddCommand); + $command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['example.com']]); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + parent::tearDown(); + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var DomainsRemoveCommand $domainsRemoveCommand */ + $domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); + $command = new CommandTester($domainsRemoveCommand); + $command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['example.com']]); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_related.php */ @@ -607,6 +638,7 @@ public function testProductOptions() public function testProductWithMediaGallery() { $testImagePath = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test_image.jpg'; + // @codingStandardsIgnoreLine $encodedImage = base64_encode(file_get_contents($testImagePath)); //create a product with media gallery $filename1 = 'tiny1' . time() . '.jpg'; @@ -731,11 +763,11 @@ public function testUpdateWithExtensionAttributes(): void protected function updateProduct($product) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { - if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' - && !is_array($product['custom_attributes'][$i]['value']) + foreach ($product['custom_attributes'] as &$customAttribute) { + if ($customAttribute['attribute_code'] == 'category_ids' + && !is_array($customAttribute['value']) ) { - $product['custom_attributes'][$i]['value'] = [""]; + $customAttribute['value'] = [""]; } } } @@ -1152,11 +1184,11 @@ protected function getSimpleProductData($productData = []) protected function saveProduct($product, $storeCode = null) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { - if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' - && !is_array($product['custom_attributes'][$i]['value']) + foreach ($product['custom_attributes'] as &$customAttribute) { + if ($customAttribute['attribute_code'] == 'category_ids' + && !is_array($customAttribute['value']) ) { - $product['custom_attributes'][$i]['value'] = [""]; + $customAttribute['value'] = [""]; } } } diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php index cf99b34207124..0415835f2dcb9 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -7,8 +7,11 @@ namespace Magento\Downloadable\Api; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Downloadable\Console\Command\DomainsAddCommand; +use Magento\Downloadable\Console\Command\DomainsRemoveCommand; use Magento\Framework\Api\ExtensibleDataInterface; use Magento\TestFramework\TestCase\WebapiAbstract; +use Symfony\Component\Console\Tester\CommandTester; /** * Class ProductRepositoryTest for testing ProductRepository interface with Downloadable Product @@ -27,7 +30,15 @@ class ProductRepositoryTest extends WebapiAbstract protected function setUp() { + parent::setUp(); $this->testImagePath = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test_image.jpg'; + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + /** @var DomainsAddCommand $domainsAddCommand */ + $domainsAddCommand = $objectManager->get(DomainsAddCommand::class); + $command = new CommandTester($domainsAddCommand); + $command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['www.example.com']]); } /** @@ -37,6 +48,13 @@ public function tearDown() { $this->deleteProductBySku(self::PRODUCT_SKU); parent::tearDown(); + + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + + /** @var DomainsRemoveCommand $domainsRemoveCommand */ + $domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); + $command = new CommandTester($domainsRemoveCommand); + $command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['www.example.com']]); } protected function getLinkData() diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/EnvWhitelist.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/EnvWhitelist.php new file mode 100644 index 0000000000000..294cc32e8f712 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/EnvWhitelist.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Mtf\Util\Command\Cli; + +use Magento\Mtf\Util\Command\Cli; + +/** + * Adding and removing domain to whitelist for test execution. + */ +class EnvWhitelist extends Cli +{ + /** + * Parameter domain add command. + */ + const PARAM_DOMAINS = 'downloadable:domains'; + + /** + * Add host to the whitelist. + * + * @param string $host + */ + public function addHost($host) + { + parent::execute(EnvWhitelist::PARAM_DOMAINS . ':add ' . $host); + } + + /** + * Remove host from the whitelist. + * + * @param string $host + */ + public function removeHost($host) + { + parent::execute(EnvWhitelist::PARAM_DOMAINS . ':remove ' . $host); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php index 90cd6bdb76328..b97accbf87a93 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/ProductTypeSwitchingOnUpdateTest.php @@ -12,6 +12,7 @@ use Magento\Downloadable\Test\Block\Adminhtml\Catalog\Product\Edit\Section\Downloadable; use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Test Creation for ProductTypeSwitchingOnUpdating @@ -60,22 +61,32 @@ class ProductTypeSwitchingOnUpdateTest extends Injectable */ protected $fixtureFactory; + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Injection data. * * @param CatalogProductIndex $catalogProductIndex * @param CatalogProductEdit $catalogProductEdit * @param FixtureFactory $fixtureFactory + * @param EnvWhitelist $envWhitelist * @return void */ public function __inject( CatalogProductIndex $catalogProductIndex, CatalogProductEdit $catalogProductEdit, - FixtureFactory $fixtureFactory + FixtureFactory $fixtureFactory, + EnvWhitelist $envWhitelist ) { $this->catalogProductIndex = $catalogProductIndex; $this->catalogProductEdit = $catalogProductEdit; $this->fixtureFactory = $fixtureFactory; + $this->envWhitelist = $envWhitelist; } /** @@ -89,6 +100,7 @@ public function __inject( public function test($productOrigin, $product, $actionName) { // Preconditions + $this->envWhitelist->addHost('example.com'); list($fixtureClass, $dataset) = explode('::', $productOrigin); $productOrigin = $this->fixtureFactory->createByCode(trim($fixtureClass), ['dataset' => trim($dataset)]); $productOrigin->persist(); @@ -144,5 +156,6 @@ protected function clearDownloadableData() $downloadableInfoTab = $this->catalogProductEdit->getProductForm()->getSection('downloadable_information'); $downloadableInfoTab->getDownloadableBlock('Links')->clearDownloadableData(); $downloadableInfoTab->setIsDownloadable('No'); + $this->envWhitelist->removeHost('example.com'); } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php index 7d6bd93180230..fba5a2b062343 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/AddProductsToShoppingCartEntityTest.php @@ -14,6 +14,7 @@ use Magento\Mtf\TestCase\Injectable; use Magento\Mtf\TestStep\TestStepFactory; use Magento\Mtf\Util\Command\Cli\Cache; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Preconditions: @@ -99,6 +100,13 @@ class AddProductsToShoppingCartEntityTest extends Injectable */ private $cache; + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Prepare test data. * @@ -108,6 +116,7 @@ class AddProductsToShoppingCartEntityTest extends Injectable * @param CheckoutCart $cartPage * @param TestStepFactory $testStepFactory * @param Cache $cache + * @param EnvWhitelist $envWhitelist * @return void */ public function __prepare( @@ -116,7 +125,8 @@ public function __prepare( CatalogProductView $catalogProductView, CheckoutCart $cartPage, TestStepFactory $testStepFactory, - Cache $cache + Cache $cache, + EnvWhitelist $envWhitelist ) { $this->browser = $browser; $this->fixtureFactory = $fixtureFactory; @@ -124,6 +134,7 @@ public function __prepare( $this->cartPage = $cartPage; $this->testStepFactory = $testStepFactory; $this->cache = $cache; + $this->envWhitelist = $envWhitelist; } /** @@ -146,6 +157,7 @@ public function test( // Preconditions $this->configData = $configData; $this->flushCache = $flushCache; + $this->envWhitelist->addHost('example.com'); $this->testStepFactory->create( \Magento\Config\Test\TestStep\SetupConfigurationStep::class, @@ -224,7 +236,7 @@ public function tearDown() $_ENV['app_frontend_url'] = preg_replace('/(http[s]?)/', 'http', $_ENV['app_frontend_url']); $this->cache->flush(); } - + $this->envWhitelist->removeHost('example.com'); $this->testStepFactory->create( \Magento\Config\Test\TestStep\SetupConfigurationStep::class, ['configData' => $this->configData, 'rollback' => true, 'flushCache' => $this->flushCache] diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.php index 54f59b03ef81d..6f5512b2e8293 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/OnePageCheckoutOfflinePaymentMethodsTest.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\TestCase; use Magento\Mtf\TestCase\Scenario; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Preconditions: @@ -43,6 +44,23 @@ class OnePageCheckoutOfflinePaymentMethodsTest extends Scenario const SEVERITY = 'S0'; /* end tags */ + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + + /** + * Perform needed injections + * + * @param EnvWhitelist $envWhitelist + */ + public function __inject(EnvWhitelist $envWhitelist) + { + $this->envWhitelist = $envWhitelist; + } + /** * Runs one page checkout test. * @@ -50,6 +68,17 @@ class OnePageCheckoutOfflinePaymentMethodsTest extends Scenario */ public function test() { + $this->envWhitelist->addHost('example.com'); $this->executeScenario(); } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php index af267cfa30ec1..36b4f4b3eb39b 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/UpdateProductFromMiniShoppingCartEntityTest.php @@ -12,6 +12,7 @@ use Magento\Mtf\Fixture\FixtureInterface; use Magento\Mtf\TestCase\Injectable; use Magento\Customer\Test\Fixture\Customer; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Preconditions: @@ -58,22 +59,32 @@ class UpdateProductFromMiniShoppingCartEntityTest extends Injectable */ protected $fixtureFactory; + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Inject data. * * @param CmsIndex $cmsIndex * @param CatalogProductView $catalogProductView * @param FixtureFactory $fixtureFactory + * @param EnvWhitelist $envWhitelist * @return void */ public function __inject( CmsIndex $cmsIndex, CatalogProductView $catalogProductView, - FixtureFactory $fixtureFactory + FixtureFactory $fixtureFactory, + EnvWhitelist $envWhitelist ) { $this->cmsIndex = $cmsIndex; $this->catalogProductView = $catalogProductView; $this->fixtureFactory = $fixtureFactory; + $this->envWhitelist = $envWhitelist; } /** @@ -97,6 +108,7 @@ public function test( Customer $customer = null ) { // Preconditions: + $this->envWhitelist->addHost('example.com'); if ($customer !== null) { $customer->persist(); } @@ -162,4 +174,14 @@ protected function addToCart(FixtureInterface $product) ); $addToCartStep->run(); } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/PrintOrderFrontendGuestTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/PrintOrderFrontendGuestTest.php index 01e43defde814..9eb13734531d9 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/PrintOrderFrontendGuestTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/PrintOrderFrontendGuestTest.php @@ -8,6 +8,7 @@ use Magento\Mtf\Client\BrowserInterface; use Magento\Mtf\TestCase\Scenario; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Preconditions: @@ -41,14 +42,25 @@ class PrintOrderFrontendGuestTest extends Scenario */ protected $browser; + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Prepare data. * * @param BrowserInterface $browser + * @param EnvWhitelist $envWhitelist */ - public function __prepare(BrowserInterface $browser) - { + public function __prepare( + BrowserInterface $browser, + EnvWhitelist $envWhitelist + ) { $this->browser = $browser; + $this->envWhitelist = $envWhitelist; } /** @@ -58,6 +70,7 @@ public function __prepare(BrowserInterface $browser) */ public function test() { + $this->envWhitelist->addHost('example.com'); $this->executeScenario(); } @@ -68,6 +81,7 @@ public function test() */ public function tearDown() { + $this->envWhitelist->removeHost('example.com'); $this->browser->closeWindow(); } } diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php index 9c5ffb9dd8013..c12b2d0991224 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/AddProductToWishlistEntityTest.php @@ -7,6 +7,7 @@ namespace Magento\Wishlist\Test\TestCase; use Magento\Customer\Test\Fixture\Customer; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Test Flow: @@ -30,15 +31,26 @@ class AddProductToWishlistEntityTest extends AbstractWishlistTest const MVP = 'no'; /* end tags */ + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Prepare data for test * * @param Customer $customer + * @param EnvWhitelist $envWhitelist * @return array */ - public function __prepare(Customer $customer) - { + public function __prepare( + Customer $customer, + EnvWhitelist $envWhitelist + ) { $customer->persist(); + $this->envWhitelist = $envWhitelist; return ['customer' => $customer]; } @@ -53,6 +65,7 @@ public function __prepare(Customer $customer) */ public function test(Customer $customer, $product, $configure = true) { + $this->envWhitelist->addHost('example.com'); $product = $this->createProducts($product)[0]; // Steps: @@ -61,4 +74,14 @@ public function test(Customer $customer, $product, $configure = true) return ['product' => $product]; } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ConfigureProductInCustomerWishlistOnBackendTest.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ConfigureProductInCustomerWishlistOnBackendTest.php index ee3bf77a1aa0d..27c60281660bb 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ConfigureProductInCustomerWishlistOnBackendTest.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ConfigureProductInCustomerWishlistOnBackendTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Test\Fixture\Customer; use Magento\Customer\Test\Page\Adminhtml\CustomerIndex; use Magento\Customer\Test\Page\Adminhtml\CustomerIndexEdit; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Preconditions: @@ -35,15 +36,26 @@ class ConfigureProductInCustomerWishlistOnBackendTest extends AbstractWishlistTe const MVP = 'no'; /* end tags */ + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Create customer. * * @param Customer $customer + * @param EnvWhitelist $envWhitelist * @return array */ - public function __prepare(Customer $customer) - { + public function __prepare( + Customer $customer, + EnvWhitelist $envWhitelist + ) { $customer->persist(); + $this->envWhitelist = $envWhitelist; return ['customer' => $customer]; } @@ -64,6 +76,7 @@ public function test( CustomerIndexEdit $customerIndexEdit ) { // Preconditions + $this->envWhitelist->addHost('example.com'); $product = $this->createProducts($product)[0]; $this->loginCustomer($customer); $this->addToWishlist([$product]); @@ -80,4 +93,14 @@ public function test( return['product' => $product]; } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ViewProductInCustomerWishlistOnBackendTest.php b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ViewProductInCustomerWishlistOnBackendTest.php index f81f87d5b6227..1bba73cdf5e9f 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ViewProductInCustomerWishlistOnBackendTest.php +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/ViewProductInCustomerWishlistOnBackendTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Test\Fixture\Customer; use Magento\Customer\Test\Page\Adminhtml\CustomerIndex; use Magento\Customer\Test\Page\Adminhtml\CustomerIndexEdit; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Test Flow: @@ -34,15 +35,26 @@ class ViewProductInCustomerWishlistOnBackendTest extends AbstractWishlistTest const MVP = 'no'; /* end tags */ + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Prepare customer for test. * * @param Customer $customer + * @param EnvWhitelist $envWhitelist * @return array */ - public function __prepare(Customer $customer) - { + public function __prepare( + Customer $customer, + EnvWhitelist $envWhitelist + ) { $customer->persist(); + $this->envWhitelist = $envWhitelist; return ['customer' => $customer]; } @@ -63,6 +75,7 @@ public function test( CustomerIndexEdit $customerIndexEdit ) { // Preconditions + $this->envWhitelist->addHost('example.com'); $product = $this->createProducts($product)[0]; $this->loginCustomer($customer); $this->addToWishlist([$product], true); @@ -74,4 +87,14 @@ public function test( return['product' => $product]; } + + /** + * Clean data after running test. + * + * @return void + */ + public function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_downloadable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_downloadable_product_rollback.php new file mode 100644 index 0000000000000..4048c672037b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_downloadable_product_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreLine +require __DIR__ . '/../../../Magento/Downloadable/_files/product_downloadable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php new file mode 100644 index 0000000000000..81ce83fc694ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Downloadable\Model\Url; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\DeploymentConfig; + +/** + * Test for Magento\Downloadable\Model\Url\DomainValidator + */ +class DomainValidatorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DomainValidator + */ + private $model; + + /** + * @var DeploymentConfig|\PHPUnit_Framework_MockObject_MockObject + */ + private $deploymentConfig; + + protected function setUp() + { + $this->deploymentConfig = $this->createPartialMock( + DeploymentConfig::class, + ['get'] + ); + + $objectManager = Bootstrap::getObjectManager(); + + $this->model = $objectManager->create( + DomainValidator::class, + ['deploymentConfig' => $this->deploymentConfig] + ); + } + + /** + * @param string $urlInput + * @param array $envDomainWhitelist + * @param bool $isValid + * + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoConfigFixture current_store web/unsecure/base_url http://example.com/ + * @magentoConfigFixture current_store web/secure/base_url https://secure.example.com/ + * @magentoConfigFixture fixture_second_store_store web/unsecure/base_url http://example2.com/ + * @magentoConfigFixture fixture_second_store_store web/secure/base_url https://secure.example2.com/ + * @dataProvider isValidDataProvider + */ + public function testIsValid(string $urlInput, array $envDomainWhitelist, bool $isValid) + { + $this->deploymentConfig + ->expects($this->any()) + ->method('get') + ->with(DomainValidator::PARAM_DOWNLOADABLE_DOMAINS) + ->willReturn($envDomainWhitelist); + + $this->assertEquals( + $isValid, + $this->model->isValid($urlInput), + 'Failed asserting is ' . ($isValid ? 'valid' : 'not valid') . ': ' . $urlInput . + PHP_EOL . + 'Domain whitelist: ' . implode(', ', $envDomainWhitelist) + ); + } + + public function isValidDataProvider() + { + return [ + ['http://example.com', ['example.co'], false], + [' http://example.com ', ['example.com'], false], + ['http://example.com', ['example.com'], true], + ['https://example.com', ['example.com'], true], + ['https://example.com/downloadable.pdf', ['example.com'], true], + ['https://example.com:8080/downloadable.pdf', ['example.com'], true], + ['http://secure.example.com', ['secure.example.com'], true], + ['https://secure.example.com', ['secure.example.com'], true], + ['https://ultra.secure.example.com', ['secure.example.com'], false], + ['http://example2.com', ['example2.com'], true], + ['https://example2.com', ['example2.com'], true], + ['http://subdomain.example2.com', ['example2.com'], false], + ['https://adobe.com', ['adobe.com'], true], + ['https://subdomain.adobe.com', ['adobe.com'], false], + ['https://ADOBE.COm', ['adobe.com'], true], + ['https://adobe.com', ['ADOBE.COm'], true], + ['http://127.0.0.1', ['127.0.0.1'], false], + ['http://[::1]', ['::1'], false], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php index 9c0b328fc1664..df6d717ed1012 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php @@ -3,10 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); + +use Magento\Downloadable\Console\Command\DomainsAddCommand; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +/** @var DomainsAddCommand $domainsAddCommand */ +$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); +$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); +$command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['example.com', 'sampleurl.com']]); + /** * @var \Magento\Catalog\Model\Product $product */ @@ -74,6 +80,7 @@ */ $sampleContent = $objectManager->create(\Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class)->create(); $sampleContent->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $sampleContent->setName('jellyfish_1_3.jpg'); @@ -92,10 +99,10 @@ */ $content = $objectManager->create(\Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class)->create(); $content->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $content->setName('jellyfish_2_4.jpg'); -//$content->setName(''); $sampleLink->setLinkFileContent($content); $links[] = $sampleLink; @@ -146,6 +153,7 @@ \Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class )->create(); $content->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $content->setName('jellyfish_1_4.jpg'); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php index 9ad910eed8739..dbaf4ea6f67b3 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php @@ -3,4 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Downloadable\Console\Command\DomainsRemoveCommand; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DomainsRemoveCommand $domainsRemoveCommand */ +$domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); +$command = new \Symfony\Component\Console\Tester\CommandTester($domainsRemoveCommand); +$command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['sampleurl.com']]); + +// @codingStandardsIgnoreLine require __DIR__ . '/product_downloadable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php index 19cf449912b66..56277a75cd801 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php @@ -3,10 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -/** - * @var \Magento\Catalog\Model\Product $product - */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + +use Magento\Downloadable\Console\Command\DomainsAddCommand; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DomainsAddCommand $domainsAddCommand */ +$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); +$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); +$command->execute( + [ + DomainsAddCommand::INPUT_KEY_DOMAINS => [ + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' + ] + ] +); + +/** @var \Magento\Catalog\Model\Product $product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); $product ->setTypeId(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) ->setId(1) diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php index 996fbb01d72c4..22619d25ee4ec 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php @@ -3,23 +3,41 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\Downloadable\Console\Command\DomainsRemoveCommand; use Magento\Framework\Exception\NoSuchEntityException; \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DomainsRemoveCommand $domainsRemoveCommand */ +$domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); +$command = new \Symfony\Component\Console\Tester\CommandTester($domainsRemoveCommand); +$command->execute( + [ + DomainsRemoveCommand::INPUT_KEY_DOMAINS => [ + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' + ] + ] +); + /** @var \Magento\Framework\Registry $registry */ -$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry = $objectManager->get(\Magento\Framework\Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ -$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() +$productRepository = $objectManager ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); try { $product = $productRepository->get('downloadable-product', false, null, true); $productRepository->delete($product); -} catch (NoSuchEntityException $e) { +} catch (NoSuchEntityException $e) { // @codingStandardsIgnoreLine } $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index 86aa61a99e1e8..47eb6c450e9ec 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -4,8 +4,25 @@ * See COPYING.txt for license details. */ +use Magento\Downloadable\Console\Command\DomainsAddCommand; + \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var DomainsAddCommand $domainsAddCommand */ +$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); +$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); +$command->execute( + [ + DomainsAddCommand::INPUT_KEY_DOMAINS => [ + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' + ] + ] +); + /** * @var \Magento\Catalog\Model\Product $product */ @@ -81,10 +98,10 @@ */ $content = $objectManager->create(\Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class)->create(); $content->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $content->setName('jellyfish_2_4.jpg'); -//$content->setName(''); $link->setLinkFileContent($content); /** @@ -92,6 +109,7 @@ */ $sampleContent = $objectManager->create(\Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class)->create(); $sampleContent->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $sampleContent->setName('jellyfish_1_3.jpg'); @@ -136,6 +154,7 @@ \Magento\Downloadable\Api\Data\File\ContentInterfaceFactory::class )->create(); $content->setFileData( + // @codingStandardsIgnoreLine base64_encode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . 'test_image.jpg')) ); $content->setName('jellyfish_1_4.jpg'); From a4a636212042681b97b3a3713ea11811ee7b8c25 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 17 Jul 2019 08:52:54 +0300 Subject: [PATCH 0202/2437] MC-18099: Changes in Downloadable product upload controller --- .../Adminhtml/Downloadable/File/Upload.php | 6 ++-- .../Checkout/Test/Block/Cart/Sidebar/Item.php | 18 ++++++++++ .../Adminhtml/Downloadable/FileTest.php | 35 ++++++++++++++----- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php index c8f7a4ff4a507..a7c32eed8bb15 100644 --- a/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php +++ b/app/code/Magento/Downloadable/Controller/Adminhtml/Downloadable/File/Upload.php @@ -81,11 +81,11 @@ public function execute() try { $type = $this->getRequest()->getParam('type'); $tmpPath = ''; - if ($type == 'samples') { + if ($type === 'samples') { $tmpPath = $this->_sample->getBaseTmpPath(); - } elseif ($type == 'links') { + } elseif ($type === 'links') { $tmpPath = $this->_link->getBaseTmpPath(); - } elseif ($type == 'link_samples') { + } elseif ($type === 'link_samples') { $tmpPath = $this->_link->getBaseSampleTmpPath(); } else { throw new LocalizedException(__('Upload type can not be determined.')); diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar/Item.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar/Item.php index c00687d91c1ee..038c411768969 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar/Item.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Sidebar/Item.php @@ -62,6 +62,7 @@ class Item extends Sidebar */ public function removeItemFromMiniCart() { + $this->waitForDeleteButtonVisible(); $this->_rootElement->find($this->removeItem)->click(); $element = $this->browser->find($this->confirmModal); /** @var \Magento\Ui\Test\Block\Adminhtml\Modal $modal */ @@ -70,6 +71,23 @@ public function removeItemFromMiniCart() $modal->waitModalWindowToDisappear(); } + /** + * Wait for Delete button is visible in the block. + * + * @return bool|null + */ + private function waitForDeleteButtonVisible() + { + $rootElement = $this->_rootElement; + $deleteButtonSelector = $this->removeItem; + return $rootElement->waitUntil( + function () use ($rootElement, $deleteButtonSelector) { + $element = $rootElement->find($deleteButtonSelector); + return $element->isVisible() ? true : null; + } + ); + } + /** * Click "Edit item" button. * diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php index ac35fa9a23b1d..20e36e6594638 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php @@ -7,36 +7,53 @@ namespace Magento\Downloadable\Controller\Adminhtml\Downloadable; use Magento\Framework\Serialize\Serializer\Json; -use Magento\TestFramework\Helper\Bootstrap; /** * Magento\Downloadable\Controller\Adminhtml\Downloadable\File * * @magentoAppArea adminhtml - * - * phpcs:disable Magento2.Functions.DiscouragedFunction - * phpcs:disable Magento2.Security.Superglobal */ class FileTest extends \Magento\TestFramework\TestCase\AbstractBackendController { + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->jsonSerializer = $this->_objectManager->get(Json::class); + } + /** * @inheritdoc */ protected function tearDown() { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $filePath = dirname(__DIR__) . '/_files/sample.tmp'; + // phpcs:ignore Magento2.Functions.DiscouragedFunction if (is_file($filePath)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction unlink($filePath); } } public function testUploadAction() { + // phpcs:ignore Magento2.Functions.DiscouragedFunction copy(dirname(__DIR__) . '/_files/sample.txt', dirname(__DIR__) . '/_files/sample.tmp'); + // phpcs:ignore Magento2.Security.Superglobal $_FILES = [ 'samples' => [ 'name' => 'sample.txt', 'type' => 'text/plain', + // phpcs:ignore Magento2.Functions.DiscouragedFunction 'tmp_name' => dirname(__DIR__) . '/_files/sample.tmp', 'error' => 0, 'size' => 0, @@ -46,7 +63,7 @@ public function testUploadAction() $this->getRequest()->setMethod('POST'); $this->dispatch('backend/admin/downloadable_file/upload/type/samples'); $body = $this->getResponse()->getBody(); - $result = Bootstrap::getObjectManager()->get(Json::class)->unserialize($body); + $result = $this->jsonSerializer->unserialize($body); $this->assertEquals(0, $result['error']); } @@ -58,9 +75,11 @@ public function testUploadAction() */ public function testUploadProhibitedExtensions($fileName) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $path = dirname(__DIR__) . '/_files/'; + // phpcs:ignore Magento2.Functions.DiscouragedFunction copy($path . 'sample.txt', $path . 'sample.tmp'); - + // phpcs:ignore Magento2.Security.Superglobal $_FILES = [ 'samples' => [ 'name' => $fileName, @@ -74,7 +93,7 @@ public function testUploadProhibitedExtensions($fileName) $this->getRequest()->setMethod('POST'); $this->dispatch('backend/admin/downloadable_file/upload/type/samples'); $body = $this->getResponse()->getBody(); - $result = Bootstrap::getObjectManager()->get(Json::class)->unserialize($body); + $result = $this->jsonSerializer->unserialize($body); self::assertArrayHasKey('errorcode', $result); self::assertEquals(0, $result['errorcode']); @@ -113,7 +132,7 @@ public function testUploadWrongUploadType(): void $this->getRequest()->setMethod('POST'); $this->dispatch('backend/admin/downloadable_file/upload'); $body = $this->getResponse()->getBody(); - $result = Bootstrap::getObjectManager()->get(Json::class)->unserialize($body); + $result = $this->jsonSerializer->unserialize($body); $this->assertEquals('Upload type can not be determined.', $result['error']); $this->assertEquals(0, $result['errorcode']); } From 88dd934c0a18875c3caf700f2c999e1938085915 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 09:51:31 -0500 Subject: [PATCH 0203/2437] MC-17700: Downloadable Product links --- .../Magento/Catalog/Api/ProductRepositoryInterfaceTest.php | 4 ++-- .../Magento/Downloadable/Model/Url/DomainValidatorTest.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 8c82f2ed84538..b9afaf1518b8c 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -65,7 +65,7 @@ protected function setUp() { parent::setUp(); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var DomainsAddCommand $domainsAddCommand */ $domainsAddCommand = $objectManager->get(DomainsAddCommand::class); $command = new CommandTester($domainsAddCommand); @@ -79,7 +79,7 @@ protected function tearDown() { parent::tearDown(); - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager = Bootstrap::getObjectManager(); /** @var DomainsRemoveCommand $domainsRemoveCommand */ $domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); $command = new CommandTester($domainsRemoveCommand); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php index 81ce83fc694ba..970b8add3e52f 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php @@ -53,7 +53,6 @@ protected function setUp() public function testIsValid(string $urlInput, array $envDomainWhitelist, bool $isValid) { $this->deploymentConfig - ->expects($this->any()) ->method('get') ->with(DomainValidator::PARAM_DOWNLOADABLE_DOMAINS) ->willReturn($envDomainWhitelist); From 76b829fcc8928fd60b6548924b00a257821bdbdb Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 10:25:47 -0500 Subject: [PATCH 0204/2437] MC-17700: Downloadable Product links --- .../Magento/Downloadable/Model/Link/ContentValidator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php index 08345c8079ddc..e9ed4920a24bc 100644 --- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php @@ -42,19 +42,19 @@ class ContentValidator /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator + * @param DomainValidator $domainValidator * @param File|null $fileHelper - * @param DomainValidator|null $domainValidator */ public function __construct( FileContentValidator $fileContentValidator, UrlValidator $urlValidator, - File $fileHelper = null, - DomainValidator $domainValidator = null + DomainValidator $domainValidator, + File $fileHelper = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; + $this->domainValidator = $domainValidator; $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); - $this->domainValidator = $domainValidator ?? ObjectManager::getInstance()->get(DomainValidator::class); } /** From f93bdedf6d363733b841d1393f943ea839d48c86 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 11:33:57 -0500 Subject: [PATCH 0205/2437] MC-17700: Downloadable Product links --- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index d63d28256809e..b467a88e1a913 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -76,7 +76,8 @@ public function apply() ->from( $this->moduleDataSetup->getTable('downloadable_link'), ['link_url'] - )->where('link_type = ?', 'url'); + ) + ->where('link_type = ?', 'url'); foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { $this->addHost($link['link_url']); @@ -87,7 +88,8 @@ public function apply() ->from( $this->moduleDataSetup->getTable('downloadable_link'), ['sample_url'] - )->where('sample_type = ?', 'url'); + ) + ->where('sample_type = ?', 'url'); foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { $this->addHost($link['sample_url']); @@ -100,7 +102,8 @@ public function apply() ->from( $this->moduleDataSetup->getTable('downloadable_sample'), ['sample_url'] - )->where('sample_type = ?', 'url'); + ) + ->where('sample_type = ?', 'url'); foreach ($this->moduleDataSetup->getConnection()->fetchAll($select) as $link) { $this->addHost($link['sample_url']); From c855748d577dcd1603a66ead27014f30ae2b1eba Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 11:46:04 -0500 Subject: [PATCH 0206/2437] MC-17700: Downloadable Product links --- .../Downloadable/Model/Sample/ContentValidator.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php index 7b8c65580f160..7c53a228f27c8 100644 --- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php @@ -42,19 +42,19 @@ class ContentValidator /** * @param FileContentValidator $fileContentValidator * @param UrlValidator $urlValidator + * @param DomainValidator $domainValidator * @param File|null $fileHelper - * @param DomainValidator|null $domainValidator */ public function __construct( FileContentValidator $fileContentValidator, UrlValidator $urlValidator, - File $fileHelper = null, - DomainValidator $domainValidator = null + DomainValidator $domainValidator, + File $fileHelper = null ) { $this->fileContentValidator = $fileContentValidator; $this->urlValidator = $urlValidator; + $this->domainValidator = $domainValidator; $this->fileHelper = $fileHelper ?? ObjectManager::getInstance()->get(File::class); - $this->domainValidator = $domainValidator ?? ObjectManager::getInstance()->get(DomainValidator::class); } /** From 74a9c24a7fc90c96365db51cc0263246c2e7ae2e Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 13:43:11 -0500 Subject: [PATCH 0207/2437] MC-17700: Downloadable Product links --- app/code/Magento/Downloadable/Model/Url/DomainValidator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index 5cdd954edb15f..66e4365564d67 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -19,12 +19,12 @@ class DomainValidator extends \Zend_Validate_Abstract /** * Invalid host message key */ - const INVALID_HOST = 'invalidHost'; + private const INVALID_HOST = 'invalidHost'; /** * Path to the allowed domains in the deployment config */ - const PARAM_DOWNLOADABLE_DOMAINS = 'downloadable_domains'; + public const PARAM_DOWNLOADABLE_DOMAINS = 'downloadable_domains'; /** * @var IpValidator From 64427a9d0fdf07b78043c6d1780d69b1493ce919 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 13:48:03 -0500 Subject: [PATCH 0208/2437] MC-17700: Downloadable Product links --- .../Magento/Downloadable/Console/Command/DomainsAddCommand.php | 2 +- .../Downloadable/Console/Command/DomainsRemoveCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php index 8158296678dea..015314b722fe8 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php @@ -25,7 +25,7 @@ class DomainsAddCommand extends Command /** * Name of domains input argument */ - const INPUT_KEY_DOMAINS = 'domains'; + public const INPUT_KEY_DOMAINS = 'domains'; /** * @var ConfigWriter diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php index 1fa02ae6359b4..0ad812d3bd71a 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php @@ -25,7 +25,7 @@ class DomainsRemoveCommand extends Command /** * Name of domains input argument */ - const INPUT_KEY_DOMAINS = 'domains'; + public const INPUT_KEY_DOMAINS = 'domains'; /** * @var ConfigWriter From ff9a9dda4cdc3f499603ab4bbafee95d0c9b6d29 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Wed, 17 Jul 2019 13:57:38 -0500 Subject: [PATCH 0209/2437] MC-17700: Downloadable Product links - CR changes --- .../Api/DomainManagerInterface.php | 36 +++++++ .../Console/Command/DomainsAddCommand.php | 52 +++------ .../Console/Command/DomainsRemoveCommand.php | 52 +++------ .../Console/Command/DomainsShowCommand.php | 26 ++--- .../Downloadable/Model/DomainManager.php | 100 ++++++++++++++++++ .../Model/Url/DomainValidator.php | 24 ++--- .../Patch/Data/AddDownloadableHostsConfig.php | 26 ++--- app/code/Magento/Downloadable/etc/di.xml | 1 + 8 files changed, 185 insertions(+), 132 deletions(-) create mode 100644 app/code/Magento/Downloadable/Api/DomainManagerInterface.php create mode 100644 app/code/Magento/Downloadable/Model/DomainManager.php diff --git a/app/code/Magento/Downloadable/Api/DomainManagerInterface.php b/app/code/Magento/Downloadable/Api/DomainManagerInterface.php new file mode 100644 index 0000000000000..9174d81621b15 --- /dev/null +++ b/app/code/Magento/Downloadable/Api/DomainManagerInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Downloadable\Api; + +/** + * Interface DomainManagerInterface + * Manage downloadable domains whitelist in the environment config. + */ +interface DomainManagerInterface +{ + /** + * Get the whitelist. + * + * @return array + */ + public function getEnvDomainWhitelist(); + + /** + * Add host to the whitelist. + * + * @param array $hosts + * @return void + */ + public function addEnvDomains($hosts); + + /** + * Remove host from the whitelist. + * + * @param array $hosts + * @return void + */ + public function removeEnvDomains($hosts); +} diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php index 015314b722fe8..e230d24b2ca73 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php @@ -10,10 +10,8 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; -use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; -use Magento\Downloadable\Model\Url\DomainValidator; -use Magento\Framework\Config\File\ConfigFilePool; /** * Class DomainsAddCommand @@ -28,27 +26,18 @@ class DomainsAddCommand extends Command public const INPUT_KEY_DOMAINS = 'domains'; /** - * @var ConfigWriter + * @var DomainManager */ - private $configWriter; - - /** - * @var DomainValidator - */ - private $domainValidator; + private $domainManager; /** * DomainsAddCommand constructor. - * - * @param ConfigWriter $configWriter - * @param DomainValidator $domainValidator + * @param DomainManager $domainManager */ public function __construct( - ConfigWriter $configWriter, - DomainValidator $domainValidator + DomainManager $domainManager ) { - $this->configWriter = $configWriter; - $this->domainValidator = $domainValidator; + $this->domainManager = $domainManager; parent::__construct(); } @@ -80,33 +69,18 @@ protected function execute(InputInterface $input, OutputInterface $output) { try { if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { + $whitelistBefore = $this->domainManager->getEnvDomainWhitelist(); $newDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); $newDomains = array_filter(array_map('trim', $newDomains), 'strlen'); - $whitelist = $this->domainValidator->getEnvDomainWhitelist() ?: []; - foreach ($newDomains as $newDomain) { - if (in_array($newDomain, $whitelist)) { - $output->writeln( - "$newDomain is already in the whitelist" - ); - continue; - } else { - array_push($whitelist, $newDomain); - $output->writeln( - "$newDomain was added to the whitelist" - ); - } - } + $this->domainManager->addEnvDomains($newDomains); - $this->configWriter->saveConfig( - [ - ConfigFilePool::APP_ENV => [ - $this->domainValidator::PARAM_DOWNLOADABLE_DOMAINS => $whitelist - ] - ] - ); + foreach (array_diff($this->domainManager->getEnvDomainWhitelist(), $whitelistBefore) as $newHost) { + $output->writeln( + $newHost . ' was added to the whitelist.' + ); + } } - } catch (\Exception $e) { $output->writeln('<error>' . $e->getMessage() . '</error>'); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php index 0ad812d3bd71a..74c0803e3d9a5 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php @@ -10,10 +10,8 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; -use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; -use Magento\Downloadable\Model\Url\DomainValidator; -use Magento\Framework\Config\File\ConfigFilePool; /** * Class DomainsRemoveCommand @@ -28,27 +26,19 @@ class DomainsRemoveCommand extends Command public const INPUT_KEY_DOMAINS = 'domains'; /** - * @var ConfigWriter + * @var DomainManager */ - private $configWriter; - - /** - * @var DomainValidator - */ - private $domainValidator; + private $domainManager; /** * DomainsRemoveCommand constructor. * - * @param ConfigWriter $configWriter - * @param DomainValidator $domainValidator + * @param DomainManager $domainManager */ public function __construct( - ConfigWriter $configWriter, - DomainValidator $domainValidator + DomainManager $domainManager ) { - $this->configWriter = $configWriter; - $this->domainValidator = $domainValidator; + $this->domainManager = $domainManager; parent::__construct(); } @@ -80,35 +70,17 @@ protected function execute(InputInterface $input, OutputInterface $output) { try { if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { + $whitelistBefore = $this->domainManager->getEnvDomainWhitelist(); $removeDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); $removeDomains = array_filter(array_map('trim', $removeDomains), 'strlen'); + $this->domainManager->removeEnvDomains($removeDomains); - $whitelist = $this->domainValidator->getEnvDomainWhitelist() ?: []; - foreach ($removeDomains as $removeDomain) { - if (in_array($removeDomain, $whitelist)) { - $index = array_search($removeDomain, $whitelist); - unset($whitelist[$index]); - $output->writeln( - "$removeDomain was removed from the whitelist" - ); - continue; - } else { - $output->writeln( - "$removeDomain is absent in the whitelist" - ); - } + foreach (array_diff($whitelistBefore, $this->domainManager->getEnvDomainWhitelist()) as $removedHost) { + $output->writeln( + $removedHost . ' was removed from the whitelist.' + ); } - - $this->configWriter->saveConfig( - [ - ConfigFilePool::APP_ENV => [ - $this->domainValidator::PARAM_DOWNLOADABLE_DOMAINS => $whitelist - ] - ], - true - ); } - } catch (\Exception $e) { $output->writeln('<error>' . $e->getMessage() . '</error>'); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php index 70e031eb3e410..dbeb3c7fdd2eb 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php @@ -9,9 +9,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputInterface; - -use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; -use Magento\Downloadable\Model\Url\DomainValidator; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; /** * Class DomainsAddCommand @@ -21,27 +19,18 @@ class DomainsShowCommand extends Command { /** - * @var ConfigWriter + * @var DomainManager */ - private $configWriter; - - /** - * @var DomainValidator - */ - private $domainValidator; + private $domainManager; /** * DomainsShowCommand constructor. - * - * @param ConfigWriter $configWriter - * @param DomainValidator $domainValidator + * @param DomainManager $domainManager */ public function __construct( - ConfigWriter $configWriter, - DomainValidator $domainValidator + DomainManager $domainManager ) { - $this->configWriter = $configWriter; - $this->domainValidator = $domainValidator; + $this->domainManager = $domainManager; parent::__construct(); } @@ -63,11 +52,10 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { try { - $whitelist = implode("\n", $this->domainValidator->getEnvDomainWhitelist() ?: []); + $whitelist = implode("\n", $this->domainManager->getEnvDomainWhitelist()); $output->writeln( "Downloadable domains whitelist:\n$whitelist" ); - } catch (\Exception $e) { $output->writeln('<error>' . $e->getMessage() . '</error>'); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php new file mode 100644 index 0000000000000..77abfa24e43c2 --- /dev/null +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Model; + +use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; +use Magento\Downloadable\Api\DomainManagerInterface; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\File\ConfigFilePool; + +/** + * Class DomainManager + * Manage downloadable domains whitelist in the environment config. + */ +class DomainManager implements DomainManagerInterface +{ + /** + * Path to the allowed domains in the deployment config + */ + private const PARAM_DOWNLOADABLE_DOMAINS = 'downloadable_domains'; + + /** + * @var ConfigWriter + */ + private $configWriter; + + /** + * @var DeploymentConfig + */ + private $deploymentConfig; + + /** + * DomainManager constructor. + * + * @param ConfigWriter $configWriter + * @param DeploymentConfig $deploymentConfig + */ + public function __construct( + ConfigWriter $configWriter, + DeploymentConfig $deploymentConfig + ) { + $this->configWriter = $configWriter; + $this->deploymentConfig = $deploymentConfig; + } + + /** + * @inheritdoc + */ + public function getEnvDomainWhitelist(): array + { + return array_map('strtolower', $this->deploymentConfig->get(self::PARAM_DOWNLOADABLE_DOMAINS) ?? []); + } + + /** + * @inheritdoc + */ + public function addEnvDomains($hosts) { + $whitelist = $this->getEnvDomainWhitelist(); + foreach (array_map('strtolower', $hosts) as $host) { + if (!in_array($host, $whitelist)) { + array_push($whitelist, $host); + } + } + + $this->configWriter->saveConfig( + [ + ConfigFilePool::APP_ENV => [ + self::PARAM_DOWNLOADABLE_DOMAINS => $whitelist + ] + ], + true + ); + } + + /** + * @inheritdoc + */ + public function removeEnvDomains($hosts) { + $whitelist = $this->getEnvDomainWhitelist(); + foreach (array_map('strtolower', $hosts) as $host) { + if (in_array($host, $whitelist)) { + $index = array_search($host, $whitelist); + unset($whitelist[$index]); + } + } + + $this->configWriter->saveConfig( + [ + ConfigFilePool::APP_ENV => [ + self::PARAM_DOWNLOADABLE_DOMAINS => $whitelist + ] + ], + true + ); + } +} diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index 66e4365564d67..2d118cd062f5f 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -7,7 +7,7 @@ namespace Magento\Downloadable\Model\Url; -use Magento\Framework\App\DeploymentConfig; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Validator\Ip as IpValidator; use Zend\Uri\Uri as UriHandler; @@ -37,21 +37,21 @@ class DomainValidator extends \Zend_Validate_Abstract private $uriHandler; /** - * @var DeploymentConfig + * @var DomainManager */ - private $deploymentConfig; + private $domainManager; /** - * @param DeploymentConfig $deploymentConfig + * @param DomainManager $domainManager * @param IpValidator $ipValidator * @param UriHandler $uriHandler */ public function __construct( - DeploymentConfig $deploymentConfig, + DomainManager $domainManager, IpValidator $ipValidator, UriHandler $uriHandler ) { - $this->deploymentConfig = $deploymentConfig; + $this->domainManager = $domainManager; $this->ipValidator = $ipValidator; $this->uriHandler = $uriHandler; @@ -71,7 +71,7 @@ public function isValid($value): bool $host = $this->getHost($value); $isIpAddress = $this->ipValidator->isValid($host); - $isValid = !$isIpAddress && in_array($host, $this->getEnvDomainWhitelist()); + $isValid = !$isIpAddress && in_array($host, $this->domainManager->getEnvDomainWhitelist()); if (!$isValid) { $this->_error(self::INVALID_HOST, $host); @@ -80,16 +80,6 @@ public function isValid($value): bool return $isValid; } - /** - * Get environment whitelist - * - * @return array - */ - public function getEnvDomainWhitelist(): array - { - return array_map('strtolower', $this->deploymentConfig->get(self::PARAM_DOWNLOADABLE_DOMAINS) ?? []); - } - /** * Extract host from url * diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index b467a88e1a913..8bddfb232b331 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -10,9 +10,7 @@ use Magento\Framework\Setup\Patch\DataPatchInterface; use Zend\Uri\Uri as UriHandler; use Magento\Framework\Url\ScopeResolverInterface; -use Magento\Framework\App\DeploymentConfig\Writer as ConfigWriter; -use Magento\Downloadable\Model\Url\DomainValidator; -use Magento\Framework\Config\File\ConfigFilePool; +use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Setup\ModuleDataSetupInterface; /** @@ -31,14 +29,14 @@ class AddDownloadableHostsConfig implements DataPatchInterface private $scopeResolver; /** - * @var ConfigWriter + * @var ModuleDataSetupInterface */ - private $configWriter; + private $moduleDataSetup; /** - * @var ModuleDataSetupInterface + * @var DomainManager */ - private $moduleDataSetup; + private $domainManager; /** * @var array @@ -50,18 +48,18 @@ class AddDownloadableHostsConfig implements DataPatchInterface * * @param UriHandler $uriHandler * @param ScopeResolverInterface $scopeResolver - * @param ConfigWriter $configWriter + * @param DomainManager $domainManager * @param ModuleDataSetupInterface $moduleDataSetup */ public function __construct( UriHandler $uriHandler, ScopeResolverInterface $scopeResolver, - ConfigWriter $configWriter, + DomainManager $domainManager, ModuleDataSetupInterface $moduleDataSetup ) { $this->uriHandler = $uriHandler; $this->scopeResolver = $scopeResolver; - $this->configWriter = $configWriter; + $this->domainManager = $domainManager; $this->moduleDataSetup = $moduleDataSetup; } @@ -114,13 +112,7 @@ public function apply() $this->addHost($scope->getBaseUrl()); } - $this->configWriter->saveConfig( - [ - ConfigFilePool::APP_ENV => [ - DomainValidator::PARAM_DOWNLOADABLE_DOMAINS => array_unique($this->whitelist) - ] - ] - ); + $this->domainManager->addEnvDomains(array_unique($this->whitelist)); } /** diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml index 8ee05029333b8..a932e5598f8ae 100644 --- a/app/code/Magento/Downloadable/etc/di.xml +++ b/app/code/Magento/Downloadable/etc/di.xml @@ -92,6 +92,7 @@ <preference for="Magento\Downloadable\Api\Data\File\ContentUploaderInterface" type="Magento\Downloadable\Model\File\ContentUploader" /> <preference for="Magento\Downloadable\Model\Product\TypeHandler\TypeHandlerInterface" type="Magento\Downloadable\Model\Product\TypeHandler\TypeHandler" /> <preference for="Magento\Downloadable\Api\Data\DownloadableOptionInterface" type="Magento\Downloadable\Model\DownloadableOption" /> + <preference for="Magento\Downloadable\Api\DomainManagerInterface" type="Magento\Downloadable\Model\DomainManager"/> <type name="Magento\Framework\EntityManager\Operation\ExtensionPool"> <arguments> <argument name="extensionActions" xsi:type="array"> From 4d92c81a066ed0bb00ecdadf992267820150e6f8 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 14:45:12 -0500 Subject: [PATCH 0210/2437] MC-17700: Downloadable Product links --- .../Model/Url/DomainValidator.php | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index 2d118cd062f5f..3d44d073af207 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -14,13 +14,8 @@ /** * Class is responsible for checking if downloadable product link domain is allowed. */ -class DomainValidator extends \Zend_Validate_Abstract +class DomainValidator { - /** - * Invalid host message key - */ - private const INVALID_HOST = 'invalidHost'; - /** * Path to the allowed domains in the deployment config */ @@ -54,8 +49,6 @@ public function __construct( $this->domainManager = $domainManager; $this->ipValidator = $ipValidator; $this->uriHandler = $uriHandler; - - $this->initMessageTemplates(); } /** @@ -73,10 +66,6 @@ public function isValid($value): bool $isIpAddress = $this->ipValidator->isValid($host); $isValid = !$isIpAddress && in_array($host, $this->domainManager->getEnvDomainWhitelist()); - if (!$isValid) { - $this->_error(self::INVALID_HOST, $host); - } - return $isValid; } @@ -97,18 +86,4 @@ private function getHost($url): string // ipv6 hosts are brace-delimited in url; they are removed here for subsequent validation return trim($host, '[] '); } - - /** - * Initialize message templates with translating - * - * @return void - */ - private function initMessageTemplates() - { - if (!$this->_messageTemplates) { - $this->_messageTemplates = [ - self::INVALID_HOST => __('Host "%value%" is not allowed.'), - ]; - } - } } From 1a51a17a4f70961151def3799b1aa53295bb2c03 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 14:59:33 -0500 Subject: [PATCH 0211/2437] MC-17700: Downloadable Product links --- app/code/Magento/Downloadable/Model/DomainManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php index 77abfa24e43c2..f343fa2e24703 100644 --- a/app/code/Magento/Downloadable/Model/DomainManager.php +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -58,7 +58,8 @@ public function getEnvDomainWhitelist(): array /** * @inheritdoc */ - public function addEnvDomains($hosts) { + public function addEnvDomains($hosts) + { $whitelist = $this->getEnvDomainWhitelist(); foreach (array_map('strtolower', $hosts) as $host) { if (!in_array($host, $whitelist)) { From eafcff6c7a584455f1c08e9439804fca5e25a112 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 15:00:04 -0500 Subject: [PATCH 0212/2437] MC-17700: Downloadable Product links --- app/code/Magento/Downloadable/Model/DomainManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php index f343fa2e24703..b3f9cce23cacb 100644 --- a/app/code/Magento/Downloadable/Model/DomainManager.php +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -80,7 +80,8 @@ public function addEnvDomains($hosts) /** * @inheritdoc */ - public function removeEnvDomains($hosts) { + public function removeEnvDomains($hosts) + { $whitelist = $this->getEnvDomainWhitelist(); foreach (array_map('strtolower', $hosts) as $host) { if (in_array($host, $whitelist)) { From 30cfd33b96dc124989cfd38f24811e0ed3a6079b Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 17 Jul 2019 15:14:41 -0500 Subject: [PATCH 0213/2437] MC-17700: Downloadable Product links --- .../Downloadable/Model/Url/DomainValidatorTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php index 970b8add3e52f..86b5bf3ed05cf 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Model/Url/DomainValidatorTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Downloadable\Model\Url; +use Magento\Downloadable\Model\DomainManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\DeploymentConfig; @@ -25,16 +26,21 @@ class DomainValidatorTest extends \PHPUnit\Framework\TestCase protected function setUp() { + $objectManager = Bootstrap::getObjectManager(); + $this->deploymentConfig = $this->createPartialMock( DeploymentConfig::class, ['get'] ); - $objectManager = Bootstrap::getObjectManager(); + $domainManager = $objectManager->create( + DomainManager::class, + ['deploymentConfig' => $this->deploymentConfig] + ); $this->model = $objectManager->create( DomainValidator::class, - ['deploymentConfig' => $this->deploymentConfig] + ['domainManager' => $domainManager] ); } From 2763ce536d0597782d2da64b565c3ddb1bf220ea Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 18 Jul 2019 14:40:32 +0300 Subject: [PATCH 0214/2437] MC-18099: Changes in Downloadable product upload controller --- .../Adminhtml/Downloadable/FileTest.php | 28 ++++++--- .../Magento/Framework/File/Uploader.php | 57 ++++++++++++++++++- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php index 20e36e6594638..6333d60da3cfe 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/Controller/Adminhtml/Downloadable/FileTest.php @@ -117,23 +117,35 @@ public function extensionsDataProvider() } /** + * @dataProvider uploadWrongUploadTypeDataProvider * @return void */ - public function testUploadWrongUploadType(): void + public function testUploadWrongUploadType($postData): void { - $postData = [ - 'type' => [ - 'tmp_name' => 'test.txt', - 'name' => 'result.txt', - ], - ]; $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod('POST'); + $this->dispatch('backend/admin/downloadable_file/upload'); + $body = $this->getResponse()->getBody(); $result = $this->jsonSerializer->unserialize($body); $this->assertEquals('Upload type can not be determined.', $result['error']); $this->assertEquals(0, $result['errorcode']); } + + public function uploadWrongUploadTypeDataProvider(): array + { + return [ + [ + ['type' => 'test'], + ], + [ + [ + 'type' => [ + 'type1' => 'test', + ], + ], + ], + ]; + } } diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index af19c619ae68f..a6ad3096ff209 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\File; +use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Validation\ValidationException; @@ -14,6 +15,8 @@ * ATTENTION! This class must be used like abstract class and must added * validation by protected file extension list to extended class * + * @SuppressWarnings(PHPMD.TooManyFields) + * * @api */ class Uploader @@ -160,17 +163,27 @@ class Uploader */ protected $_result; + /** + * @var DirectoryList + */ + private $directoryList; + /** * Init upload * * @param string|array $fileId * @param \Magento\Framework\File\Mime|null $fileMime + * @param DirectoryList|null $directoryList * @throws \DomainException */ public function __construct( $fileId, - Mime $fileMime = null + Mime $fileMime = null, + DirectoryList $directoryList = null ) { + $this->directoryList= $directoryList ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(DirectoryList::class); + $this->_setUploadFileId($fileId); if (!file_exists($this->_file['tmp_name'])) { $code = empty($this->_file['tmp_name']) ? self::TMP_NAME_EMPTY : 0; @@ -550,7 +563,7 @@ private function _getMimeType() */ private function _setUploadFileId($fileId) { - if (is_array($fileId)) { + if (is_array($fileId) && $this->isValidFileId($fileId)) { $this->_uploadType = self::MULTIPLE_STYLE; $this->_file = $fileId; } else { @@ -584,6 +597,46 @@ private function _setUploadFileId($fileId) } } + /** + * Check if $fileId has correct 'tmp_name' field. + * + * @param array $fileId + * @return bool + * @throws \InvalidArgumentException + */ + private function isValidFileId(array $fileId): bool + { + $isValid = false; + if (isset($fileId['tmp_name'])) { + $tmpName = trim($fileId['tmp_name']); + + if (mb_strpos($tmpName, '..') === false) { + $allowedFolders = [ + sys_get_temp_dir(), + $this->directoryList->getPath(DirectoryList::MEDIA), + $this->directoryList->getPath(DirectoryList::VAR_DIR), + $this->directoryList->getPath(DirectoryList::TMP), + $this->directoryList->getPath(DirectoryList::UPLOAD), + ]; + + foreach ($allowedFolders as $allowedFolder) { + if (stripos($tmpName, $allowedFolder) !== false) { + $isValid = true; + break; + } + } + } + } + + if (!$isValid) { + throw new \InvalidArgumentException( + 'Invalid parameter given. A valid $fileId[tmp_name] is expected.' + ); + } + + return $isValid; + } + /** * Create destination folder * From a76a120b60dec8b0664cec98df515ddd3f9feb72 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 18 Jul 2019 10:26:30 -0500 Subject: [PATCH 0215/2437] MC-17700: Downloadable Product links --- .../Downloadable/Api/LinkRepositoryTest.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index 0eb3da755c5f0..7fdfc71227e7c 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -470,6 +470,58 @@ public function testCreateThrowsExceptionIfLinkUrlHasWrongFormat() $this->_webApiCall($this->createServiceInfo, $requestData); } + /** + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + * @expectedException \Exception + * @expectedExceptionMessage Link URL must have valid format. + */ + public function testCreateThrowsExceptionIfLinkUrlUsesDomainNotInWhitelist() + { + $requestData = [ + 'isGlobalScopeContent' => false, + 'sku' => 'downloadable-product', + 'link' => [ + 'title' => 'Link Title', + 'sort_order' => 1, + 'price' => 10, + 'is_shareable' => 1, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://notAnywhereInEnv.com/', + 'sample_type' => 'url', + 'sample_url' => 'http://www.example.com/', + ], + ]; + + $this->_webApiCall($this->createServiceInfo, $requestData); + } + + /** + * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php + * @expectedException \Exception + * @expectedExceptionMessage Sample URL must have valid format. + */ + public function testCreateThrowsExceptionIfSampleUrlUsesDomainNotInWhitelist() + { + $requestData = [ + 'isGlobalScopeContent' => false, + 'sku' => 'downloadable-product', + 'link' => [ + 'title' => 'Link Title', + 'sort_order' => 1, + 'price' => 10, + 'is_shareable' => 1, + 'number_of_downloads' => 100, + 'link_type' => 'url', + 'link_url' => 'http://example.com/', + 'sample_type' => 'url', + 'sample_url' => 'http://www.notAnywhereInEnv.com/', + ], + ]; + + $this->_webApiCall($this->createServiceInfo, $requestData); + } + /** * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php * @expectedException \Exception From 8e94e71c71c15215593d17dc9ca81747c6f2fdd4 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 18 Jul 2019 12:27:44 -0500 Subject: [PATCH 0216/2437] MC-17700: Downloadable Product links --- .../AdminCreateDownloadableProductWithDefaultSetLinksTest.xml | 2 +- .../Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index d9b4e8087cbc9..2fcc87b71bbb4 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateDownloadableProductWithLinkTest"> + <test name="AdminCreateDownloadableProductWithDefaultSetLinksTest"> <annotations> <features value="Catalog"/> <stories value="Create Downloadable Product"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml index 8b38f91f8719c..e43b8f94c7a3d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateDownloadableProductWithDefaultSetLinksTest"> + <test name="AdminCreateDownloadableProductWithLinkTest"> <annotations> <features value="Catalog"/> <stories value="Create Downloadable Product"/> From 7c199167a89cc0c35ff20bf28aad5771f4ebd17a Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 18 Jul 2019 14:25:05 -0500 Subject: [PATCH 0217/2437] MC-18125: Email template improvement --- lib/internal/Magento/Framework/Filter/Template.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index 4881a16b055e7..c330e4d130094 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -79,7 +79,19 @@ class Template implements \Zend_Filter_Interface 'gettemplateprocessor', 'vardirective', 'delete', - 'getdatausingmethod' + 'getdatausingmethod', + '__destruct', + '__call', + '__callStatic', + '__set', + '__unset', + '__sleep', + '__wakeup', + '__invoke', + '__set_state', + '__debugInfo', + '___callParent', + '___callPlugins' ]; /** From 03510d2b455f60c9a2839a1f75dd768a2cf08c1c Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Thu, 18 Jul 2019 14:32:05 -0500 Subject: [PATCH 0218/2437] MC-17700: Downloadable Product links - CR changes --- .../Downloadable/Api/DomainManagerInterface.php | 8 ++++---- .../Downloadable/Console/Command/DomainsAddCommand.php | 6 +++--- .../Console/Command/DomainsRemoveCommand.php | 6 +++--- .../Console/Command/DomainsShowCommand.php | 2 +- app/code/Magento/Downloadable/Model/DomainManager.php | 10 +++++----- .../Magento/Downloadable/Model/Url/DomainValidator.php | 2 +- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Downloadable/Api/DomainManagerInterface.php b/app/code/Magento/Downloadable/Api/DomainManagerInterface.php index 9174d81621b15..ca98f18e36c33 100644 --- a/app/code/Magento/Downloadable/Api/DomainManagerInterface.php +++ b/app/code/Magento/Downloadable/Api/DomainManagerInterface.php @@ -7,7 +7,7 @@ /** * Interface DomainManagerInterface - * Manage downloadable domains whitelist in the environment config. + * Manage downloadable domains whitelist. */ interface DomainManagerInterface { @@ -16,7 +16,7 @@ interface DomainManagerInterface * * @return array */ - public function getEnvDomainWhitelist(); + public function getDomains(): array; /** * Add host to the whitelist. @@ -24,7 +24,7 @@ public function getEnvDomainWhitelist(); * @param array $hosts * @return void */ - public function addEnvDomains($hosts); + public function addDomains(array $hosts): void; /** * Remove host from the whitelist. @@ -32,5 +32,5 @@ public function addEnvDomains($hosts); * @param array $hosts * @return void */ - public function removeEnvDomains($hosts); + public function removeDomains(array $hosts): void; } diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php index e230d24b2ca73..285bf38eb45b0 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php @@ -69,13 +69,13 @@ protected function execute(InputInterface $input, OutputInterface $output) { try { if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { - $whitelistBefore = $this->domainManager->getEnvDomainWhitelist(); + $whitelistBefore = $this->domainManager->getDomains(); $newDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); $newDomains = array_filter(array_map('trim', $newDomains), 'strlen'); - $this->domainManager->addEnvDomains($newDomains); + $this->domainManager->addDomains($newDomains); - foreach (array_diff($this->domainManager->getEnvDomainWhitelist(), $whitelistBefore) as $newHost) { + foreach (array_diff($this->domainManager->getDomains(), $whitelistBefore) as $newHost) { $output->writeln( $newHost . ' was added to the whitelist.' ); diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php index 74c0803e3d9a5..936126c7a21f7 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php @@ -70,12 +70,12 @@ protected function execute(InputInterface $input, OutputInterface $output) { try { if ($input->getArgument(self::INPUT_KEY_DOMAINS)) { - $whitelistBefore = $this->domainManager->getEnvDomainWhitelist(); + $whitelistBefore = $this->domainManager->getDomains(); $removeDomains = $input->getArgument(self::INPUT_KEY_DOMAINS); $removeDomains = array_filter(array_map('trim', $removeDomains), 'strlen'); - $this->domainManager->removeEnvDomains($removeDomains); + $this->domainManager->removeDomains($removeDomains); - foreach (array_diff($whitelistBefore, $this->domainManager->getEnvDomainWhitelist()) as $removedHost) { + foreach (array_diff($whitelistBefore, $this->domainManager->getDomains()) as $removedHost) { $output->writeln( $removedHost . ' was removed from the whitelist.' ); diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php index dbeb3c7fdd2eb..a2a705373bb7e 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php @@ -52,7 +52,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { try { - $whitelist = implode("\n", $this->domainManager->getEnvDomainWhitelist()); + $whitelist = implode("\n", $this->domainManager->getDomains()); $output->writeln( "Downloadable domains whitelist:\n$whitelist" ); diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php index b3f9cce23cacb..3c1c295b296bf 100644 --- a/app/code/Magento/Downloadable/Model/DomainManager.php +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -50,7 +50,7 @@ public function __construct( /** * @inheritdoc */ - public function getEnvDomainWhitelist(): array + public function getDomains(): array { return array_map('strtolower', $this->deploymentConfig->get(self::PARAM_DOWNLOADABLE_DOMAINS) ?? []); } @@ -58,9 +58,9 @@ public function getEnvDomainWhitelist(): array /** * @inheritdoc */ - public function addEnvDomains($hosts) + public function addDomains(array $hosts): void { - $whitelist = $this->getEnvDomainWhitelist(); + $whitelist = $this->getDomains(); foreach (array_map('strtolower', $hosts) as $host) { if (!in_array($host, $whitelist)) { array_push($whitelist, $host); @@ -80,9 +80,9 @@ public function addEnvDomains($hosts) /** * @inheritdoc */ - public function removeEnvDomains($hosts) + public function removeDomains(array $hosts): void { - $whitelist = $this->getEnvDomainWhitelist(); + $whitelist = $this->getDomains(); foreach (array_map('strtolower', $hosts) as $host) { if (in_array($host, $whitelist)) { $index = array_search($host, $whitelist); diff --git a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php index 3d44d073af207..cab7fb134ea33 100644 --- a/app/code/Magento/Downloadable/Model/Url/DomainValidator.php +++ b/app/code/Magento/Downloadable/Model/Url/DomainValidator.php @@ -64,7 +64,7 @@ public function isValid($value): bool $host = $this->getHost($value); $isIpAddress = $this->ipValidator->isValid($host); - $isValid = !$isIpAddress && in_array($host, $this->domainManager->getEnvDomainWhitelist()); + $isValid = !$isIpAddress && in_array($host, $this->domainManager->getDomains()); return $isValid; } diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 8bddfb232b331..a6534fa1717f1 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -112,7 +112,7 @@ public function apply() $this->addHost($scope->getBaseUrl()); } - $this->domainManager->addEnvDomains(array_unique($this->whitelist)); + $this->domainManager->addDomains(array_unique($this->whitelist)); } /** From 38182ec65b7d6da2158adb5b11fbb2a9f2404ca9 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 18 Jul 2019 15:14:37 -0500 Subject: [PATCH 0219/2437] MC-17700: Downloadable Product links --- .../AdminDownloadableProductActionGroup.xml | 17 ++++---- ...bleProductWithInvalidDomainLinkUrlTest.xml | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml index 363911daa41ed..91114e54cb421 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml @@ -40,17 +40,18 @@ <actionGroup name="addDownloadableProductLink"> <arguments> <argument name="link" defaultValue="downloadableLink"/> + <argument name="index" type="string" defaultValue="1"/> </arguments> <click selector="{{AdminProductDownloadableSection.linksAddLinkButton}}" stepKey="clickLinkAddLinkButton"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput('1')}}" stepKey="fillDownloadableLinkTitle"/> - <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput('1')}}" stepKey="fillDownloadableLinkPrice"/> - <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector('1')}}" stepKey="selectDownloadableLinkFileType"/> - <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector('1')}}" stepKey="selectDownloadableLinkSampleType"/> - <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector('1')}}" stepKey="selectDownloadableLinkShareable"/> - <checkOption selector="{{AdminProductDownloadableSection.addLinkIsUnlimitedDownloads('1')}}" stepKey="checkDownloadableLinkUnlimited"/> - <fillField userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUrlInput('1')}}" stepKey="fillDownloadableLinkFileUrl"/> - <attachFile userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUploadFile('1')}}" stepKey="attachDownloadableLinkUploadSample"/> + <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput(index)}}" stepKey="fillDownloadableLinkTitle"/> + <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput(index)}}" stepKey="fillDownloadableLinkPrice"/> + <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector(index)}}" stepKey="selectDownloadableLinkFileType"/> + <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector(index)}}" stepKey="selectDownloadableLinkSampleType"/> + <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector(index)}}" stepKey="selectDownloadableLinkShareable"/> + <checkOption selector="{{AdminProductDownloadableSection.addLinkIsUnlimitedDownloads(index)}}" stepKey="checkDownloadableLinkUnlimited"/> + <fillField userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUrlInput(index)}}" stepKey="fillDownloadableLinkFileUrl"/> + <attachFile userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUploadFile(index)}}" stepKey="attachDownloadableLinkUploadSample"/> </actionGroup> <!--Add a downloadable sample file--> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml new file mode 100644 index 0000000000000..0291b0bb3110c --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest" extends="AdminCreateDownloadableProductWithLinkTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Downloadable Product"/> + <title value="Create Downloadable Product with invalid domain link url"/> + <description value="Admin should not be able to create downloadable product with invalid domain link url"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-18282"/> + <group value="Downloadable"/> + <group value="AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest"/> + </annotations> + <before> + <remove keyForRemoval="addDownloadableDomain" /> + </before> + <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink"> + <argument name="link" value="downloadableLink"/> + <argument name="index" value="0"/> + </actionGroup> + <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="saveProduct"/> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="Link URL must have valid format." stepKey="seeLinkUrlInvalidMessage" after="saveProduct" /> + <magentoCLI stepKey="addDownloadableDomain2" command="downloadable:domains:add static.magento.com" after="seeLinkUrlInvalidMessage" /> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable" after="addDownloadableDomain2"/> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkIsLinksPurchasedSeparately" after="checkIsDownloadable"/> + <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLinkAgain" after="checkIsLinksPurchasedSeparately"> + <argument name="link" value="downloadableLink"/> + <argument name="index" value="0"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProductAfterAddingDomainToWhitelist" after="addDownloadableProductLinkAgain" /> + </test> +</tests> From 2453496dfc827e29f4e9d6f2db050085dcf445d2 Mon Sep 17 00:00:00 2001 From: Ievgen Kolesov <ikolesov@magento.com> Date: Thu, 18 Jul 2019 16:33:53 -0500 Subject: [PATCH 0220/2437] MC-17700: Downloadable Product links - CR changes --- .../Console/Command/DomainsShowCommand.php | 2 +- .../Api/ProductRepositoryInterfaceTest.php | 18 +++++++----------- .../Api/ProductRepositoryTest.php | 17 ++++++----------- ...able_product_with_files_and_sample_url.php | 9 ++++----- ...uct_with_files_and_sample_url_rollback.php | 9 ++++----- .../_files/product_downloadable.php | 19 ++++++++----------- .../_files/product_downloadable_rollback.php | 19 ++++++++----------- .../product_downloadable_with_files.php | 19 ++++++++----------- 8 files changed, 46 insertions(+), 66 deletions(-) diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php index a2a705373bb7e..eb4488353a096 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsShowCommand.php @@ -12,7 +12,7 @@ use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; /** - * Class DomainsAddCommand + * Class DomainsShowCommand * * Command for listing allowed downloadable domains */ diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index b9afaf1518b8c..2772e80490278 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -9,8 +9,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; -use Magento\Downloadable\Console\Command\DomainsAddCommand; -use Magento\Downloadable\Console\Command\DomainsRemoveCommand; +use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Downloadable\Model\Link; use Magento\Store\Model\Store; use Magento\Store\Model\Website; @@ -24,7 +23,6 @@ use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Webapi\Exception as HTTPExceptionCodes; -use Symfony\Component\Console\Tester\CommandTester; /** * @magentoAppIsolation enabled @@ -66,10 +64,9 @@ protected function setUp() parent::setUp(); $objectManager = Bootstrap::getObjectManager(); - /** @var DomainsAddCommand $domainsAddCommand */ - $domainsAddCommand = $objectManager->get(DomainsAddCommand::class); - $command = new CommandTester($domainsAddCommand); - $command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['example.com']]); + /** @var DomainManagerInterface $domainManager */ + $domainManager = $objectManager->get(DomainManagerInterface::class); + $domainManager->addDomains(['example.com']); } /** @@ -80,10 +77,9 @@ protected function tearDown() parent::tearDown(); $objectManager = Bootstrap::getObjectManager(); - /** @var DomainsRemoveCommand $domainsRemoveCommand */ - $domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); - $command = new CommandTester($domainsRemoveCommand); - $command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['example.com']]); + /** @var DomainManagerInterface $domainManager */ + $domainManager = $objectManager->get(DomainManagerInterface::class); + $domainManager->removeDomains(['example.com']); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php index 0415835f2dcb9..c2393d0a5ad2d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php @@ -7,11 +7,8 @@ namespace Magento\Downloadable\Api; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Downloadable\Console\Command\DomainsAddCommand; -use Magento\Downloadable\Console\Command\DomainsRemoveCommand; use Magento\Framework\Api\ExtensibleDataInterface; use Magento\TestFramework\TestCase\WebapiAbstract; -use Symfony\Component\Console\Tester\CommandTester; /** * Class ProductRepositoryTest for testing ProductRepository interface with Downloadable Product @@ -35,10 +32,9 @@ protected function setUp() $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var DomainsAddCommand $domainsAddCommand */ - $domainsAddCommand = $objectManager->get(DomainsAddCommand::class); - $command = new CommandTester($domainsAddCommand); - $command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['www.example.com']]); + /** @var DomainManagerInterface $domainManager */ + $domainManager = $objectManager->get(DomainManagerInterface::class); + $domainManager->addDomains(['www.example.com']); } /** @@ -51,10 +47,9 @@ public function tearDown() $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var DomainsRemoveCommand $domainsRemoveCommand */ - $domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); - $command = new CommandTester($domainsRemoveCommand); - $command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['www.example.com']]); + /** @var DomainManagerInterface $domainManager */ + $domainManager = $objectManager->get(DomainManagerInterface::class); + $domainManager->removeDomains(['www.example.com']); } protected function getLinkData() diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php index df6d717ed1012..e312d973aeb17 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url.php @@ -4,14 +4,13 @@ * See COPYING.txt for license details. */ -use Magento\Downloadable\Console\Command\DomainsAddCommand; +use Magento\Downloadable\Api\DomainManagerInterface; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var DomainsAddCommand $domainsAddCommand */ -$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); -$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); -$command->execute([DomainsAddCommand::INPUT_KEY_DOMAINS => ['example.com', 'sampleurl.com']]); +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->addDomains(['example.com', 'sampleurl.com']); /** * @var \Magento\Catalog\Model\Product $product diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php index dbaf4ea6f67b3..48d6966fb90df 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/downloadable_product_with_files_and_sample_url_rollback.php @@ -4,14 +4,13 @@ * See COPYING.txt for license details. */ -use Magento\Downloadable\Console\Command\DomainsRemoveCommand; +use Magento\Downloadable\Api\DomainManagerInterface; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var DomainsRemoveCommand $domainsRemoveCommand */ -$domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); -$command = new \Symfony\Component\Console\Tester\CommandTester($domainsRemoveCommand); -$command->execute([DomainsRemoveCommand::INPUT_KEY_DOMAINS => ['sampleurl.com']]); +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->removeDomains(['sampleurl.com']); // @codingStandardsIgnoreLine require __DIR__ . '/product_downloadable_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php index 56277a75cd801..25344ea447d9a 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable.php @@ -4,21 +4,18 @@ * See COPYING.txt for license details. */ -use Magento\Downloadable\Console\Command\DomainsAddCommand; +use Magento\Downloadable\Api\DomainManagerInterface; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var DomainsAddCommand $domainsAddCommand */ -$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); -$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); -$command->execute( +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->addDomains( [ - DomainsAddCommand::INPUT_KEY_DOMAINS => [ - 'example.com', - 'www.example.com', - 'www.sample.example.com', - 'google.com' - ] + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' ] ); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php index 22619d25ee4ec..9a2e1c74fcd33 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_rollback.php @@ -4,24 +4,21 @@ * See COPYING.txt for license details. */ -use Magento\Downloadable\Console\Command\DomainsRemoveCommand; +use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Framework\Exception\NoSuchEntityException; \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var DomainsRemoveCommand $domainsRemoveCommand */ -$domainsRemoveCommand = $objectManager->get(DomainsRemoveCommand::class); -$command = new \Symfony\Component\Console\Tester\CommandTester($domainsRemoveCommand); -$command->execute( +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->removeDomains( [ - DomainsRemoveCommand::INPUT_KEY_DOMAINS => [ - 'example.com', - 'www.example.com', - 'www.sample.example.com', - 'google.com' - ] + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' ] ); diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php index 47eb6c450e9ec..a6c58c586ea16 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_files.php @@ -4,22 +4,19 @@ * See COPYING.txt for license details. */ -use Magento\Downloadable\Console\Command\DomainsAddCommand; +use Magento\Downloadable\Api\DomainManagerInterface; \Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var DomainsAddCommand $domainsAddCommand */ -$domainsAddCommand = $objectManager->get(DomainsAddCommand::class); -$command = new \Symfony\Component\Console\Tester\CommandTester($domainsAddCommand); -$command->execute( +/** @var DomainManagerInterface $domainManager */ +$domainManager = $objectManager->get(DomainManagerInterface::class); +$domainManager->addDomains( [ - DomainsAddCommand::INPUT_KEY_DOMAINS => [ - 'example.com', - 'www.example.com', - 'www.sample.example.com', - 'google.com' - ] + 'example.com', + 'www.example.com', + 'www.sample.example.com', + 'google.com' ] ); From 4293a7e85f466be422d37a195c5d258f4ae8d810 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 18 Jul 2019 17:27:00 -0500 Subject: [PATCH 0221/2437] MC-17700: Downloadable Product links --- ...minCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index 0291b0bb3110c..d75c666cb0cb7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -10,14 +10,12 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest" extends="AdminCreateDownloadableProductWithLinkTest"> <annotations> - <features value="Catalog"/> <stories value="Create Downloadable Product"/> <title value="Create Downloadable Product with invalid domain link url"/> <description value="Admin should not be able to create downloadable product with invalid domain link url"/> <severity value="CRITICAL"/> <testCaseId value="MC-18282"/> <group value="Downloadable"/> - <group value="AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest"/> </annotations> <before> <remove keyForRemoval="addDownloadableDomain" /> From 241271e8417c4264d44682169aa2032e955d6942 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 19 Jul 2019 12:01:10 +0300 Subject: [PATCH 0222/2437] MC-18099: Changes in Downloadable product upload controller --- .../Magento/Framework/File/UploaderTest.php | 105 ++++++++++++++++++ .../Magento/Framework/File/Uploader.php | 28 +++-- 2 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php new file mode 100644 index 0000000000000..952df02822a37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\File; + +use Magento\Framework\App\Filesystem\DirectoryList; + +/** + * Test for \Magento\Framework\File\Uploader + */ +class UploaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\MediaStorage\Model\File\UploaderFactory + */ + private $uploaderFactory; + + /** + * @var \Magento\Framework\Filesystem + */ + private $filesystem; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->uploaderFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\MediaStorage\Model\File\UploaderFactory::class); + + $this->filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Filesystem::class); + } + + /** + * @return void + */ + public function testUploadFileFromAllowedFolder(): void + { + $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP); + + $fileName = 'text.txt'; + $tmpDir = 'tmp'; + $filePath = $tmpDirectory->getAbsolutePath($fileName); + $file = fopen($filePath, "wb"); + fwrite($file, 'just a text'); + + $type = [ + 'tmp_name' => $filePath, + 'name' => $fileName, + ]; + + $uploader = $this->uploaderFactory->create(['fileId' => $type]); + $uploader->save($mediaDirectory->getAbsolutePath($tmpDir)); + + $this->assertTrue(is_file($mediaDirectory->getAbsolutePath($tmpDir . DIRECTORY_SEPARATOR . $fileName))); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid parameter given. A valid $fileId[tmp_name] is expected. + * + * @return void + */ + public function testUploadFileFromNotAllowedFolder(): void + { + $fileName = 'text.txt'; + $tmpDir = 'tmp'; + $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::LOG); + $directoryPath = $tmpDirectory->getAbsolutePath() . $tmpDir; + if (!is_dir($directoryPath)) { + mkdir($directoryPath, 0777, true); + } + $filePath = $directoryPath . DIRECTORY_SEPARATOR . $fileName; + $file = fopen($filePath, "wb"); + fwrite($file, 'just a text'); + + $type = [ + 'tmp_name' => $filePath, + 'name' => $fileName, + ]; + + $this->uploaderFactory->create(['fileId' => $type]); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + + $tmpDir = 'tmp'; + $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $mediaDirectory->delete($tmpDir); + + $logDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::LOG); + $logDirectory->delete($tmpDir); + } +} diff --git a/lib/internal/Magento/Framework/File/Uploader.php b/lib/internal/Magento/Framework/File/Uploader.php index a6ad3096ff209..f9b41709ec7c8 100644 --- a/lib/internal/Magento/Framework/File/Uploader.php +++ b/lib/internal/Magento/Framework/File/Uploader.php @@ -563,7 +563,8 @@ private function _getMimeType() */ private function _setUploadFileId($fileId) { - if (is_array($fileId) && $this->isValidFileId($fileId)) { + if (is_array($fileId)) { + $this->validateFileId($fileId); $this->_uploadType = self::MULTIPLE_STYLE; $this->_file = $fileId; } else { @@ -598,19 +599,19 @@ private function _setUploadFileId($fileId) } /** - * Check if $fileId has correct 'tmp_name' field. + * Validates explicitly given uploaded file data. * * @param array $fileId - * @return bool + * @return void * @throws \InvalidArgumentException */ - private function isValidFileId(array $fileId): bool + private function validateFileId(array $fileId): void { $isValid = false; if (isset($fileId['tmp_name'])) { $tmpName = trim($fileId['tmp_name']); - if (mb_strpos($tmpName, '..') === false) { + if (preg_match('/\.\.(\\\|\/)/', $tmpName) !== 1) { $allowedFolders = [ sys_get_temp_dir(), $this->directoryList->getPath(DirectoryList::MEDIA), @@ -619,22 +620,31 @@ private function isValidFileId(array $fileId): bool $this->directoryList->getPath(DirectoryList::UPLOAD), ]; + $disallowedFolders = [ + $this->directoryList->getPath(DirectoryList::LOG), + ]; + foreach ($allowedFolders as $allowedFolder) { - if (stripos($tmpName, $allowedFolder) !== false) { + if (stripos($tmpName, $allowedFolder) === 0) { $isValid = true; break; } } + + foreach ($disallowedFolders as $disallowedFolder) { + if (stripos($tmpName, $disallowedFolder) === 0) { + $isValid = false; + break; + } + } } } if (!$isValid) { throw new \InvalidArgumentException( - 'Invalid parameter given. A valid $fileId[tmp_name] is expected.' + __('Invalid parameter given. A valid $fileId[tmp_name] is expected.') ); } - - return $isValid; } /** From 1647eb46ece170b89e058c69a8a5f95ff2d02de6 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 19 Jul 2019 14:08:02 +0300 Subject: [PATCH 0223/2437] MC-18099: Changes in Downloadable product upload controller --- .../Magento/Framework/File/UploaderTest.php | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php index 952df02822a37..e38c6a5e81c06 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php @@ -47,8 +47,8 @@ public function testUploadFileFromAllowedFolder(): void $fileName = 'text.txt'; $tmpDir = 'tmp'; $filePath = $tmpDirectory->getAbsolutePath($fileName); - $file = fopen($filePath, "wb"); - fwrite($file, 'just a text'); + + $tmpDirectory->writeFile($fileName, 'just a text'); $type = [ 'tmp_name' => $filePath, @@ -58,7 +58,7 @@ public function testUploadFileFromAllowedFolder(): void $uploader = $this->uploaderFactory->create(['fileId' => $type]); $uploader->save($mediaDirectory->getAbsolutePath($tmpDir)); - $this->assertTrue(is_file($mediaDirectory->getAbsolutePath($tmpDir . DIRECTORY_SEPARATOR . $fileName))); + $this->assertTrue($mediaDirectory->isFile($tmpDir . DIRECTORY_SEPARATOR . $fileName)); } /** @@ -72,13 +72,9 @@ public function testUploadFileFromNotAllowedFolder(): void $fileName = 'text.txt'; $tmpDir = 'tmp'; $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::LOG); - $directoryPath = $tmpDirectory->getAbsolutePath() . $tmpDir; - if (!is_dir($directoryPath)) { - mkdir($directoryPath, 0777, true); - } - $filePath = $directoryPath . DIRECTORY_SEPARATOR . $fileName; - $file = fopen($filePath, "wb"); - fwrite($file, 'just a text'); + $filePath = $tmpDirectory->getAbsolutePath() . $tmpDir . DIRECTORY_SEPARATOR . $fileName; + + $tmpDirectory->writeFile($tmpDir . DIRECTORY_SEPARATOR . $fileName, 'just a text'); $type = [ 'tmp_name' => $filePath, @@ -91,15 +87,17 @@ public function testUploadFileFromNotAllowedFolder(): void /** * @inheritdoc */ - protected function tearDown() + public static function tearDownAfterClass() { - parent::tearDown(); + parent::tearDownAfterClass(); + $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Framework\Filesystem::class); $tmpDir = 'tmp'; - $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); $mediaDirectory->delete($tmpDir); - $logDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::LOG); + $logDirectory = $filesystem->getDirectoryWrite(DirectoryList::LOG); $logDirectory->delete($tmpDir); } } From 6989a910db2c297beb23bfc7629662dd3bab59d5 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 19 Jul 2019 15:43:10 +0300 Subject: [PATCH 0224/2437] MC-18099: Changes in Downloadable product upload controller --- .../testsuite/Magento/Framework/File/UploaderTest.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php index e38c6a5e81c06..15e52f5b17565 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/File/UploaderTest.php @@ -87,17 +87,15 @@ public function testUploadFileFromNotAllowedFolder(): void /** * @inheritdoc */ - public static function tearDownAfterClass() + protected function tearDown() { - parent::tearDownAfterClass(); - $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get(\Magento\Framework\Filesystem::class); + parent::tearDown(); $tmpDir = 'tmp'; - $mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); $mediaDirectory->delete($tmpDir); - $logDirectory = $filesystem->getDirectoryWrite(DirectoryList::LOG); + $logDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::LOG); $logDirectory->delete($tmpDir); } } From 90772dd77c57d7afed71e1af4d0e5869d0c60210 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 19 Jul 2019 08:42:56 -0500 Subject: [PATCH 0225/2437] MC-17700: Downloadable Product links --- .../Magento/Downloadable/Console/Command/DomainsAddCommand.php | 1 - .../Downloadable/Console/Command/DomainsRemoveCommand.php | 1 - app/code/Magento/Downloadable/Model/DomainManager.php | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php index 285bf38eb45b0..76d9a13f70f1f 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsAddCommand.php @@ -12,7 +12,6 @@ use Symfony\Component\Console\Input\InputArgument; use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; - /** * Class DomainsAddCommand * diff --git a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php index 936126c7a21f7..a30e99a24859c 100644 --- a/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php +++ b/app/code/Magento/Downloadable/Console/Command/DomainsRemoveCommand.php @@ -12,7 +12,6 @@ use Symfony\Component\Console\Input\InputArgument; use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; - /** * Class DomainsRemoveCommand * diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php index 3c1c295b296bf..e4c5948fa9cd9 100644 --- a/app/code/Magento/Downloadable/Model/DomainManager.php +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -14,6 +14,7 @@ /** * Class DomainManager + * * Manage downloadable domains whitelist in the environment config. */ class DomainManager implements DomainManagerInterface From a2a47a5e9eb81ad98dd3a1bb54530124cd730455 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 19 Jul 2019 09:35:01 -0500 Subject: [PATCH 0226/2437] MC-17700: Downloadable Product links --- ...inCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index d75c666cb0cb7..5aa00e3ef48ea 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -34,5 +34,8 @@ <argument name="index" value="0"/> </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveProductAfterAddingDomainToWhitelist" after="addDownloadableProductLinkAgain" /> + <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" stepKey="scrollToLinks"/> + <click selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" stepKey="selectProductLink"/> + <see selector="{{CheckoutCartProductSection.ProductPriceByName(DownloadableProduct.name)}}" userInput="$52.99" stepKey="assertProductPriceInCart"/> </test> </tests> From 411523e00466128e9b59666064275015318fd0e6 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 19 Jul 2019 14:43:09 -0500 Subject: [PATCH 0227/2437] MC-17700: Downloadable Product links --- .../Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml index 91114e54cb421..e60e6f6887d09 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml @@ -52,6 +52,7 @@ <checkOption selector="{{AdminProductDownloadableSection.addLinkIsUnlimitedDownloads(index)}}" stepKey="checkDownloadableLinkUnlimited"/> <fillField userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUrlInput(index)}}" stepKey="fillDownloadableLinkFileUrl"/> <attachFile userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUploadFile(index)}}" stepKey="attachDownloadableLinkUploadSample"/> + <waitForPageLoad stepKey="waitForPageLoadAfterFillingOutForm" /> </actionGroup> <!--Add a downloadable sample file--> From b2a112e34df2be7080e30bed2cd47f2362b6b7f9 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sun, 21 Jul 2019 09:27:27 +0300 Subject: [PATCH 0228/2437] Reformatting the code and adjusting the name of ActionGroup --- .../ActionGroup/AssertAdminProductStockStatusActionGroup.xml | 5 +++-- .../ActionGroup/NavigateToCustomerAccountPageActionGroup.xml | 2 +- ...ml => StorefrontCustomerElementNotVisibleActionGroup.xml} | 2 +- .../Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml | 4 ++-- .../Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{AssertStorefrontCustomerElementNotVisibleActionGroup.xml => StorefrontCustomerElementNotVisibleActionGroup.xml} (87%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml index 56c088887cd4b..887845b1b51a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertAdminProductStockStatusActionGroup"> <arguments> <argument name="productId" type="string"/> @@ -14,6 +14,7 @@ </arguments> <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProductEditPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <seeOptionIsSelected selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{stockStatus}}" stepKey="checkProductStatus" /> + <seeOptionIsSelected selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{stockStatus}}" + stepKey="checkProductStatus"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml index ce7b023f1d57d..51ce4a6f3b2ac 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="NavigateToCustomerAccountPageActionGroup"> <arguments> - <argument name="page" type="string" /> + <argument name="page" type="string"/> </arguments> <amOnPage url="{{page}}" stepKey="amOnTheCustomerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml similarity index 87% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml index d6d66f9f2b708..7b77b7ecd0787 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomerElementNotVisibleActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontCustomerElementNotVisibleActionGroup"> + <actionGroup name="StorefrontCustomerElementNotVisibleActionGroup"> <arguments> <argument name="selector" type="string"/> </arguments> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 757550efcf83a..741d146ceb406 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -34,7 +34,7 @@ <argument name="product" value="$$simpleProduct$$"/> <argument name="productQty" value="{{SimpleProduct_25.quantity}}"/> </actionGroup> - <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod" /> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod"/> <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> @@ -44,7 +44,7 @@ <actionGroup ref="NavigateToCustomerAccountPageActionGroup" stepKey="goToOrderHistoryPage"> <argument name="page" value="{{StorefrontCustomerOrdersHistoryPage.url}}"/> </actionGroup> - <actionGroup ref="AssertStorefrontCustomerElementNotVisibleActionGroup" stepKey="checkReorderButton"> + <actionGroup ref="StorefrontCustomerElementNotVisibleActionGroup" stepKey="checkReorderButton"> <argument name="selector" value="{{StorefrontCustomerOrderViewSection.reorder}}"/> </actionGroup> <after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml index 041fed644f859..ebfe808ec63ac 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -34,7 +34,7 @@ <argument name="product" value="$$simpleProduct$$"/> <argument name="productQty" value="25"/> </actionGroup> - <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod" /> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod"/> <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> From 526d2d217a80a74d23b633c88ef76ce2108bf362 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 22 Jul 2019 07:28:34 +0300 Subject: [PATCH 0229/2437] Refactoring ActionGroups --- .../StorefrontCustomerElementNotVisibleActionGroup.xml | 7 ++----- ...ontNavigateToCustomerOrdersHistoryPageActionGroup.xml} | 7 ++----- .../Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml | 8 ++------ 3 files changed, 6 insertions(+), 16 deletions(-) rename app/code/Magento/Customer/Test/Mftf/ActionGroup/{NavigateToCustomerAccountPageActionGroup.xml => StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml} (64%) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml index 7b77b7ecd0787..12a0b8f47c5fa 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml @@ -7,10 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCustomerElementNotVisibleActionGroup"> - <arguments> - <argument name="selector" type="string"/> - </arguments> - <dontSeeElement selector="{{selector}}" stepKey="assertNotVisibleElement"/> + <actionGroup name="StorefrontCustomerReorderButtonNotVisibleActionGroup"> + <dontSeeElement selector="{{StorefrontCustomerOrderViewSection.reorder}}" stepKey="assertNotVisibleElement"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml similarity index 64% rename from app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml index 51ce4a6f3b2ac..40a79b87b01a9 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateToCustomerAccountPageActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml @@ -7,11 +7,8 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="NavigateToCustomerAccountPageActionGroup"> - <arguments> - <argument name="page" type="string"/> - </arguments> - <amOnPage url="{{page}}" stepKey="amOnTheCustomerPage"/> + <actionGroup name="StorefrontNavigateToCustomerOrdersHistoryPageActionGroup"> + <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="amOnTheCustomerPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 741d146ceb406..21e2c7593c38a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -41,12 +41,8 @@ <actionGroup ref="LoginToStorefrontActionGroup" stepKey="frontendCustomerLogIn"> <argument name="Customer" value="$$simpleCustomer$$"/> </actionGroup> - <actionGroup ref="NavigateToCustomerAccountPageActionGroup" stepKey="goToOrderHistoryPage"> - <argument name="page" value="{{StorefrontCustomerOrdersHistoryPage.url}}"/> - </actionGroup> - <actionGroup ref="StorefrontCustomerElementNotVisibleActionGroup" stepKey="checkReorderButton"> - <argument name="selector" value="{{StorefrontCustomerOrderViewSection.reorder}}"/> - </actionGroup> + <actionGroup ref="StorefrontNavigateToCustomerOrdersHistoryPageActionGroup" stepKey="goToOrderHistoryPage"/> + <actionGroup ref="StorefrontCustomerReorderButtonNotVisibleActionGroup" stepKey="checkReorderButton"/> <after> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> <actionGroup ref="logout" stepKey="adminLogout"/> From 9bd092973395e74b098da1f603ded1c4dbc5cf77 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 22 Jul 2019 10:09:15 +0300 Subject: [PATCH 0230/2437] Disabling the payment method after testing --- .../Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml | 1 + .../Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 21e2c7593c38a..4599fa3e0831a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -46,6 +46,7 @@ <after> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> <actionGroup ref="logout" stepKey="adminLogout"/> + <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> </after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml index ebfe808ec63ac..bca87cbae77e7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -44,6 +44,7 @@ </actionGroup> <after> <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> </after> From 611b6c00511112b83d7d961b87093e8acbed8cd3 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 22 Jul 2019 14:48:23 +0300 Subject: [PATCH 0231/2437] Adding the test title --- .../Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml index 4599fa3e0831a..6ac7be5757a76 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateOrderAndCheckTheReorderTest"> <annotations> - <title value="v"/> + <title value="'Reorder' button is not visible for customer if ordered item is out of stock"/> <stories value="MAGETWO-63924: 'Reorder' button is not visible for customer if ordered item is out of stock"/> <description value="'Reorder' button is not visible for customer if ordered item is out of stock"/> <features value="Sales"/> From f03348b6eb4f2cbaa120f03d0b967fc829d00d7a Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 22 Jul 2019 10:48:50 -0500 Subject: [PATCH 0232/2437] MC-17700: Downloadable Product links --- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index a6534fa1717f1..b42d126a9c30a 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -112,7 +112,7 @@ public function apply() $this->addHost($scope->getBaseUrl()); } - $this->domainManager->addDomains(array_unique($this->whitelist)); + $this->domainManager->addDomains($this->whitelist); } /** From f57f4d2e845f90a83d7aee8508753fda6f81f956 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 22 Jul 2019 11:35:54 -0500 Subject: [PATCH 0233/2437] MC-17700: Downloadable Product links --- ...dminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index 5aa00e3ef48ea..ea7dc8c51388f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -15,6 +15,7 @@ <description value="Admin should not be able to create downloadable product with invalid domain link url"/> <severity value="CRITICAL"/> <testCaseId value="MC-18282"/> + <useCaseId value="MC-17700"/> <group value="Downloadable"/> </annotations> <before> From 61010964f018362057ae164d2c4cf509a68fe284 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 22 Jul 2019 14:11:16 -0500 Subject: [PATCH 0234/2437] MC-17700: Downloadable Product links --- .../Downloadable/Model/Link/ContentValidator.php | 16 ++++++++++------ .../Model/Sample/ContentValidator.php | 7 +++++-- ...adableProductWithInvalidDomainLinkUrlTest.xml | 2 +- app/code/Magento/Downloadable/i18n/en_US.csv | 3 ++- .../Downloadable/Api/LinkRepositoryTest.php | 4 ++-- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php index e9ed4920a24bc..28d61986a4f56 100644 --- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php @@ -100,11 +100,13 @@ public function isValid(LinkInterface $link, $validateLinkContent = true, $valid protected function validateLinkResource(LinkInterface $link) { if ($link->getLinkType() === 'url') { - if (!$this->urlValidator->isValid($link->getLinkUrl()) - || !$this->domainValidator->isValid($link->getLinkUrl()) - ) { + if (!$this->urlValidator->isValid($link->getLinkUrl())) { throw new InputException(__('Link URL must have valid format.')); } + + if (!$this->domainValidator->isValid($link->getLinkUrl())) { + throw new InputException(__('Link URL\'s domain is not in list of downloadable_domains in env.php.')); + } } elseif ($link->getLinkFileContent()) { if (!$this->fileContentValidator->isValid($link->getLinkFileContent())) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); @@ -124,11 +126,13 @@ protected function validateLinkResource(LinkInterface $link) protected function validateSampleResource(LinkInterface $link) { if ($link->getSampleType() === 'url') { - if (!$this->urlValidator->isValid($link->getSampleUrl()) - || !$this->domainValidator->isValid($link->getSampleUrl()) - ) { + if (!$this->urlValidator->isValid($link->getSampleUrl())) { throw new InputException(__('Sample URL must have valid format.')); } + + if (!$this->domainValidator->isValid($link->getSampleUrl())) { + throw new InputException(__('Sample URL\'s domain is not in list of downloadable_domains in env.php.')); + } } elseif ($link->getSampleFileContent()) { if (!$this->fileContentValidator->isValid($link->getSampleFileContent())) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php index 7c53a228f27c8..697878017ee8b 100644 --- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php +++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php @@ -88,10 +88,13 @@ public function isValid(SampleInterface $sample, $validateSampleContent = true) protected function validateSampleResource(SampleInterface $sample) { if ($sample->getSampleType() === 'url') { - if (!$this->urlValidator->isValid($sample->getSampleUrl()) - || !$this->domainValidator->isValid($sample->getSampleUrl())) { + if (!$this->urlValidator->isValid($sample->getSampleUrl())) { throw new InputException(__('Sample URL must have valid format.')); } + + if (!$this->domainValidator->isValid($sample->getSampleUrl())) { + throw new InputException(__('Sample URL\'s domain is not in list of downloadable_domains in env.php.')); + } } elseif ($sample->getSampleFileContent()) { if (!$this->fileContentValidator->isValid($sample->getSampleFileContent())) { throw new InputException(__('Provided file content must be valid base64 encoded data.')); diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index ea7dc8c51388f..4eb6b82b65918 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -26,7 +26,7 @@ <argument name="index" value="0"/> </actionGroup> <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="saveProduct"/> - <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="Link URL must have valid format." stepKey="seeLinkUrlInvalidMessage" after="saveProduct" /> + <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="Link URL's domain is not in list of downloadable_domains in env.php." stepKey="seeLinkUrlInvalidMessage" after="saveProduct" /> <magentoCLI stepKey="addDownloadableDomain2" command="downloadable:domains:add static.magento.com" after="seeLinkUrlInvalidMessage" /> <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable" after="addDownloadableDomain2"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkIsLinksPurchasedSeparately" after="checkIsDownloadable"/> diff --git a/app/code/Magento/Downloadable/i18n/en_US.csv b/app/code/Magento/Downloadable/i18n/en_US.csv index 6158f1c579722..d5607d7f536ce 100644 --- a/app/code/Magento/Downloadable/i18n/en_US.csv +++ b/app/code/Magento/Downloadable/i18n/en_US.csv @@ -118,4 +118,5 @@ Downloads,Downloads "Use Content-Disposition","Use Content-Disposition" "Disable Guest Checkout if Cart Contains Downloadable Items","Disable Guest Checkout if Cart Contains Downloadable Items" "Guest checkout will only work with shareable.","Guest checkout will only work with shareable." -"Host ""%value"" is not allowed.","Host ""%value"" is not allowed." +"Link URL's domain is not in list of downloadable_domains in env.php.","Link URL's domain is not in list of downloadable_domains in env.php." +"Sample URL's domain is not in list of downloadable_domains in env.php.","Link URL's domain is not in list of downloadable_domains in env.php." diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php index 7fdfc71227e7c..3a24aab30cb65 100644 --- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php @@ -473,7 +473,7 @@ public function testCreateThrowsExceptionIfLinkUrlHasWrongFormat() /** * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php * @expectedException \Exception - * @expectedExceptionMessage Link URL must have valid format. + * @expectedExceptionMessage Link URL's domain is not in list of downloadable_domains in env.php. */ public function testCreateThrowsExceptionIfLinkUrlUsesDomainNotInWhitelist() { @@ -499,7 +499,7 @@ public function testCreateThrowsExceptionIfLinkUrlUsesDomainNotInWhitelist() /** * @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable.php * @expectedException \Exception - * @expectedExceptionMessage Sample URL must have valid format. + * @expectedExceptionMessage Sample URL's domain is not in list of downloadable_domains in env.php. */ public function testCreateThrowsExceptionIfSampleUrlUsesDomainNotInWhitelist() { From 7375716d7ddd8269a0bff29d86bc67c5c6815dd4 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 22 Jul 2019 15:47:46 -0500 Subject: [PATCH 0235/2437] MC-15298: Allow admin to opt out of admin analytics tracking --- .../Magento/AdminAnalytics/Block/Metadata.php | 84 +++++++++++ app/code/Magento/AdminAnalytics/README.md | 1 + .../SelectAdminUsageSettingActionGroup.xml | 19 +++ .../Test/Mftf/Section/AdminHeaderSection.xml | 15 ++ .../Mftf/Section/AdminUsageConfigSection.xml | 16 ++ .../Test/Mftf/Test/TrackingScriptTest.xml | 137 ++++++++++++++++++ app/code/Magento/AdminAnalytics/composer.json | 26 ++++ .../AdminAnalytics/etc/adminhtml/system.xml | 21 +++ .../Magento/AdminAnalytics/etc/config.xml | 18 +++ .../Magento/AdminAnalytics/etc/module.xml | 10 ++ .../Magento/AdminAnalytics/registration.php | 9 ++ .../view/adminhtml/layout/default.xml | 19 +++ .../view/adminhtml/templates/tracking.phtml | 15 ++ 13 files changed, 390 insertions(+) create mode 100644 app/code/Magento/AdminAnalytics/Block/Metadata.php create mode 100644 app/code/Magento/AdminAnalytics/README.md create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/SelectAdminUsageSettingActionGroup.xml create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminHeaderSection.xml create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml create mode 100644 app/code/Magento/AdminAnalytics/composer.json create mode 100644 app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml create mode 100644 app/code/Magento/AdminAnalytics/etc/config.xml create mode 100644 app/code/Magento/AdminAnalytics/etc/module.xml create mode 100644 app/code/Magento/AdminAnalytics/registration.php create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml diff --git a/app/code/Magento/AdminAnalytics/Block/Metadata.php b/app/code/Magento/AdminAnalytics/Block/Metadata.php new file mode 100644 index 0000000000000..e89d09ab100eb --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Block/Metadata.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Block; + +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Backend\Model\Auth\Session; +use Magento\Framework\App\State; + +/** + * Gets user version and mode + * + * @api + */ +class Metadata extends Template +{ + /** + * @var State + */ + private $appState; + + /** + * @var Session + */ + private $authSession; + + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @param Context $context + * @param ProductMetadataInterface $productMetadata + * @param Session $authSession + * @param State $appState + * @param array $data + */ + public function __construct( + Context $context, + ProductMetadataInterface $productMetadata, + Session $authSession, + State $appState, + array $data = [] + ) { + $this->productMetadata = $productMetadata; + $this->authSession = $authSession; + $this->appState = $appState; + parent::__construct($context, $data); + } + + /** + * Get product version + * + * @return string + */ + public function getMagentoVersion() :string + { + return $this->productMetadata->getVersion(); + } + + /** + * Get current user id (hash generated from email) + * + * @return string + */ + public function getCurrentUser() :string + { + return hash('sha512', 'ADMIN_USER' . $this->authSession->getUser()->getEmail()); + } + /** + * Get Magento mode + * + * @return string + */ + public function getMode() :string + { + return $this->appState->getMode(); + } +} diff --git a/app/code/Magento/AdminAnalytics/README.md b/app/code/Magento/AdminAnalytics/README.md new file mode 100644 index 0000000000000..1280e0fcef10f --- /dev/null +++ b/app/code/Magento/AdminAnalytics/README.md @@ -0,0 +1 @@ +The purpose of Magento\AdminAnalytics module is gather information on which features the users uses and sends it to adobe analytics \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/SelectAdminUsageSettingActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/SelectAdminUsageSettingActionGroup.xml new file mode 100644 index 0000000000000..3b302fe5be18b --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/SelectAdminUsageSettingActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectAdminUsageSetting"> + <arguments> + <argument name="adminUsageValue" type="string" defaultValue="0"/> + </arguments> + <conditionalClick selector="{{AdminUsageConfigSection.adminUsageHeader}}" dependentSelector="{{AdminUsageConfigSection.adminUsageOptions}}" visible="false" stepKey="clickOnAdminUsageHeader"/> + <selectOption selector="{{AdminUsageConfigSection.adminUsageOptions}}" userInput="{{adminUsageValue}}" stepKey="selectOption"/> + <click selector="{{AdminNewStoreGroupActionsSection.saveButton}}" stepKey="clickSaveButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminHeaderSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminHeaderSection.xml new file mode 100644 index 0000000000000..cc9f495a60026 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminHeaderSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminHeaderSection"> + <element name="adminTrackingScript" type="text" selector="script[src*='//assets.adobedtm.com/launch']"/> + <element name="adminTrackingMetaData" type="text" selector="//*script[contains('adminAnalyticsMetadata')]"/> + </section> +</sections> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml new file mode 100644 index 0000000000000..ddb89f0bfb72c --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUsageConfigSection"> + + <element name="adminUsageHeader" type="text" selector="#admin_usage-head"/> + <element name="adminUsageOptions" type="select" selector="#admin_usage_enabled"/> + </section> +</sections> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml new file mode 100644 index 0000000000000..da0d5041a6e34 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="TrackingScriptTest"> + <annotations> + <features value="Backend"/> + <stories value="Checks to see if the tracking script is in the dom of admin and if setting is turned to no it checks if the tracking script in the dom was removed"/> + <title value="Checks to see if the tracking script is in the dom of admin and if setting is turned to no it checks if the tracking script in the dom was removed"/> + <description value="Checks to see if the tracking script is in the dom of admin and if setting is turned to no it checks if the tracking script in the dom was removed"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-18192"/> + <group value="backend"/> + <group value="login"/> + </annotations> + + <!-- Logging in Magento admin and checking for tracking script in dashboard --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + + <!-- Navigating to advance settings --> + <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettings"/> + <waitForPageLoad stepKey="waitForAdvanceSettings"/> + + <!-- Changing usage setting to Yes --> + <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToYes"> + <argument name="adminUsageValue" value="1"/> + </actionGroup> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrl"/> + + <!-- Checking for tracking script in salesOrderPage --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPage"/> + <waitForPageLoad stepKey="waitForSalesOrderPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSalesOrder"/> + + <!-- Checking for tracking script in catalogProductsPage --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPage"/> + <waitForPageLoad stepKey="waitForCatalogProductsPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCatalogProducts"/> + + <!-- Checking for tracking script in customersAllCustomersPage --> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPage"/> + <waitForPageLoad stepKey="waitForCustomersAllCustomersPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCustomersAllCustomers"/> + + <!-- Checking for tracking script in marketingCatalogPriceRulePage --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePage"/> + <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlMarketingCatalogPriceRule"/> + + <!-- Checking for tracking script in contentBlocksPage --> + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPage"/> + <waitForPageLoad stepKey="waitForContentBlocksPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlContentBlocks"/> + + <!-- Checking for tracking script in reportsSearchTermsPage --> + <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPage"/> + <waitForPageLoad stepKey="waitForSearchTermsPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlReportsSearchTerms"/> + + <!-- Checking for tracking script in storesAllStoresPage --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPage"/> + <waitForPageLoad stepKey="waitForStoresAllStoresPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlStoresAllStores"/> + + <!-- Checking for tracking script in systemImportPage --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPage"/> + <waitForPageLoad stepKey="waitForSystemImportPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSystemImport"/> + + <!-- Checking for tracking script in findPartnersAndExtensionsPage --> + <amOnPage url="{{FindPartnersAndExtensionsPage.url}}" stepKey="goToFindPartnersAndExtensionsPage"/> + <waitForPageLoad stepKey="waitForFindPartnersAndExtensionsPage"/> + <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlFindPartnersAndExtensions"/> + + <!-- Navigating to advance settings --> + <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettingsAgain"/> + <waitForPageLoad stepKey="waitForAdvanceSettingsAgain"/> + + <!-- Changing usage setting back to No --> + <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToNo"> + <argument name="adminUsageValue" value="0"/> + </actionGroup> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlTest"/> + + <!-- Checking for removed tracking script in salesOrderPage --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPageAgain"/> + <waitForPageLoad stepKey="waitForSalesOrderPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSalesOrderTest"/> + + <!-- Checking for removed tracking script in catalogProductsPage --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPageAgain"/> + <waitForPageLoad stepKey="waitForCatalogProductsPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCatalogProductsTest"/> + + <!-- Checking for removed tracking script in customersAllCustomersPage --> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPageAgain"/> + <waitForPageLoad stepKey="waitForCustomersAllCustomersPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCustomersAllCustomersTest"/> + + <!-- Checking for removed tracking script in marketingCatalogPriceRulePage --> + <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePageAgain"/> + <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlMarketingCatalogTest"/> + + <!-- Checking for removed tracking script in contentBlocksPage --> + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPageAgain"/> + <waitForPageLoad stepKey="waitForContentBlocksPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlContentBlocksTest"/> + + <!-- Checking for removed tracking script in reportsSearchTermsPage --> + <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPageAgain"/> + <waitForPageLoad stepKey="waitForSearchTermsPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlReportsSearchTest"/> + + <!-- Checking for removed tracking script in storesAllStoresPage --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPageAgain"/> + <waitForPageLoad stepKey="waitForStoresAllStoresPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlStoresAllStoresTest"/> + + <!-- Checking for removed tracking script in systemImportPage --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPageAgain"/> + <waitForPageLoad stepKey="waitForSystemImportPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSystemsImportTest"/> + + <!-- Checking for removed tracking script in findPartnersAndExtensionsPage --> + <amOnPage url="{{FindPartnersAndExtensionsPage.url}}" stepKey="goToFindPartnersAndExtensionsPageAgain"/> + <waitForPageLoad stepKey="waitForFindPartnersAndExtensionsPageAgain"/> + <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlFindPartnersTest"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/composer.json b/app/code/Magento/AdminAnalytics/composer.json new file mode 100644 index 0000000000000..085d258c72b57 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-admin-analytics", + "description": "N/A", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-backend": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\AdminAnalytics\\": "" + } + } +} + diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..97372e1cca087 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="admin"> + <group id="usage" translate="label" type="text" sortOrder="2000" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Admin Usage</label> + <field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Enable Admin Usage Tracking</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>Allow Magento to track admin usage in order to improve the quality and user experience.</comment> + </field> + </group> + </section> + </system> +</config> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/etc/config.xml b/app/code/Magento/AdminAnalytics/etc/config.xml new file mode 100644 index 0000000000000..85b6b6879f083 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/config.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <admin> + <usage> + <enabled> + 0 + </enabled> + </usage> + </admin> + </default> +</config> diff --git a/app/code/Magento/AdminAnalytics/etc/module.xml b/app/code/Magento/AdminAnalytics/etc/module.xml new file mode 100644 index 0000000000000..f0990b114e25f --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_AdminAnalytics"/> +</config> diff --git a/app/code/Magento/AdminAnalytics/registration.php b/app/code/Magento/AdminAnalytics/registration.php new file mode 100644 index 0000000000000..65c9955d396a8 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use \Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminAnalytics', __DIR__); diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml new file mode 100644 index 0000000000000..8ec8ac3bfaf54 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" > + <body> + <referenceContainer name="header"> + <block class="Magento\AdminAnalytics\Block\Metadata" name="tracking" as="tracking" template="Magento_AdminAnalytics::tracking.phtml" ifconfig="admin/usage/enabled"> + <arguments> + <argument name="tracking_url" xsi:type="string">//assets.adobedtm.com/launch-EN30eb7ffa064444f1b8b0368ef38fd3a9.min.js</argument> + </arguments> + </block> + </referenceContainer> + </body> +</page> + diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml new file mode 100644 index 0000000000000..e4acd77b13353 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> + +<script src="<?= $block->escapeUrl($block->getTrackingUrl()) ?>" async></script> +<script> + var adminAnalyticsMetadata = { + "version": "<?= $block->escapeJs($block->getMagentoVersion()) ?>", + "user": "<?= $block->escapeJs($block->getCurrentUser()) ?>", + "mode": "<?= $block->escapeJs($block->getMode()) ?>" + }; +</script> \ No newline at end of file From dc0c2e6d3b7e5958e265572f52d1d458cc0b0665 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 23 Jul 2019 10:16:46 +0300 Subject: [PATCH 0236/2437] Refactoring the Action Groups --- ...p.xml => AssertToolbarTextIsVisibleInCartActionGroup.xml} | 5 ++--- ...frontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml | 3 +-- ...ontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) rename app/code/Magento/Checkout/Test/Mftf/ActionGroup/{AssertTextIsVisibleOnPageActionGroup.xml => AssertToolbarTextIsVisibleInCartActionGroup.xml} (71%) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.xml similarity index 71% rename from app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml rename to app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.xml index 8f1920a28d9f1..6ede2a0dd5388 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertTextIsVisibleOnPageActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.xml @@ -7,12 +7,11 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertTextIsVisibleOnPageActionGroup"> + <actionGroup name="AssertToolbarTextIsVisibleInCartActionGroup"> <arguments> <argument name="text" type="string"/> - <argument name="selector" type="string"/> </arguments> <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="{{text}}" selector="{{selector}}" stepKey="VerifyPageText"/> + <see userInput="{{text}}" selector="{{StorefrontCartToolbarSection.toolbarNumber}}" stepKey="VerifyPageText"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml index 08922c019bdcd..787fedc8469cf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -131,9 +131,8 @@ <deleteData createDataKey="simpleProduct21" stepKey="deleteCartItem21"/> </after> <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> - <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerText"> + <actionGroup ref="AssertToolbarTextIsVisibleInCartActionGroup" stepKey="VerifyPagerText"> <argument name="text" value="Items 1 to 20 of 21 total"/> - <argument name="selector" value="{{StorefrontCartToolbarSection.toolbarNumber}}"/> </actionGroup> <actionGroup ref="StorefrontRemoveCartItemActionGroup" stepKey="removeCartItem" /> <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText2" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml index 2b6c31a941efe..64080c6b6d6a6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -37,9 +37,8 @@ <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> </after> <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> - <actionGroup ref="AssertTextIsVisibleOnPageActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> + <actionGroup ref="AssertToolbarTextIsVisibleInCartActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> <argument name="text" value="Items 1 to 1 of 2 total"/> - <argument name="selector" value="{{StorefrontCartToolbarSection.toolbarNumber}}"/> </actionGroup> </test> </tests> From 114a25dd9c3936d1b80121343df1c9c95dec94bc Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 23 Jul 2019 09:28:33 -0500 Subject: [PATCH 0237/2437] MC-17700: Downloadable Product links --- app/code/Magento/Downloadable/i18n/en_US.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/i18n/en_US.csv b/app/code/Magento/Downloadable/i18n/en_US.csv index d5607d7f536ce..1e96413aa08a1 100644 --- a/app/code/Magento/Downloadable/i18n/en_US.csv +++ b/app/code/Magento/Downloadable/i18n/en_US.csv @@ -119,4 +119,4 @@ Downloads,Downloads "Disable Guest Checkout if Cart Contains Downloadable Items","Disable Guest Checkout if Cart Contains Downloadable Items" "Guest checkout will only work with shareable.","Guest checkout will only work with shareable." "Link URL's domain is not in list of downloadable_domains in env.php.","Link URL's domain is not in list of downloadable_domains in env.php." -"Sample URL's domain is not in list of downloadable_domains in env.php.","Link URL's domain is not in list of downloadable_domains in env.php." +"Sample URL's domain is not in list of downloadable_domains in env.php.","Sample URL's domain is not in list of downloadable_domains in env.php." From 19341f41d6cf385bcbdc41d6d900232fae66c9ed Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 23 Jul 2019 11:12:17 -0500 Subject: [PATCH 0238/2437] MC-17700: Downloadable Product links --- .../Downloadable/Model/DomainManager.php | 4 ++- .../Patch/Data/AddDownloadableHostsConfig.php | 28 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Downloadable/Model/DomainManager.php b/app/code/Magento/Downloadable/Model/DomainManager.php index e4c5948fa9cd9..e1f275902b034 100644 --- a/app/code/Magento/Downloadable/Model/DomainManager.php +++ b/app/code/Magento/Downloadable/Model/DomainManager.php @@ -64,7 +64,7 @@ public function addDomains(array $hosts): void $whitelist = $this->getDomains(); foreach (array_map('strtolower', $hosts) as $host) { if (!in_array($host, $whitelist)) { - array_push($whitelist, $host); + $whitelist[] = $host; } } @@ -91,6 +91,8 @@ public function removeDomains(array $hosts): void } } + $whitelist = array_values($whitelist); // reindex whitelist to prevent non-sequential keying + $this->configWriter->saveConfig( [ ConfigFilePool::APP_ENV => [ diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index b42d126a9c30a..575a6a4d7180b 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -7,11 +7,14 @@ namespace Magento\Downloadable\Setup\Patch\Data; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Store\Model\ScopeInterface; use Zend\Uri\Uri as UriHandler; use Magento\Framework\Url\ScopeResolverInterface; use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Backend\App\Area\FrontNameResolver; /** * Adding base url as allowed downloadable domain. @@ -28,6 +31,11 @@ class AddDownloadableHostsConfig implements DataPatchInterface */ private $scopeResolver; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @var ModuleDataSetupInterface */ @@ -48,17 +56,20 @@ class AddDownloadableHostsConfig implements DataPatchInterface * * @param UriHandler $uriHandler * @param ScopeResolverInterface $scopeResolver + * @param ScopeConfigInterface $scopeConfig * @param DomainManager $domainManager * @param ModuleDataSetupInterface $moduleDataSetup */ public function __construct( UriHandler $uriHandler, ScopeResolverInterface $scopeResolver, + ScopeConfigInterface $scopeConfig, DomainManager $domainManager, ModuleDataSetupInterface $moduleDataSetup ) { $this->uriHandler = $uriHandler; $this->scopeResolver = $scopeResolver; + $this->scopeConfig = $scopeConfig; $this->domainManager = $domainManager; $this->moduleDataSetup = $moduleDataSetup; } @@ -68,6 +79,19 @@ public function __construct( */ public function apply() { + foreach ($this->scopeResolver->getScopes() as $scope) { + $this->addHost($scope->getBaseUrl()); + } + + $customAdminUrl = $this->scopeConfig->getValue( + FrontNameResolver::XML_PATH_CUSTOM_ADMIN_URL, + ScopeInterface::SCOPE_STORE + ); + + if ($customAdminUrl) { + $this->addHost($customAdminUrl); + } + if ($this->moduleDataSetup->tableExists('downloadable_link')) { $select = $this->moduleDataSetup->getConnection() ->select() @@ -108,10 +132,6 @@ public function apply() } } - foreach ($this->scopeResolver->getScopes() as $scope) { - $this->addHost($scope->getBaseUrl()); - } - $this->domainManager->addDomains($this->whitelist); } From 52a1316151042ad68bc6cd1b15da7f4abe4dc499 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 23 Jul 2019 15:20:47 -0500 Subject: [PATCH 0239/2437] MC-17700: Downloadable Product links --- .../Import/Product/Type/Downloadable.php | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index e03964bd2c386..48ff56184a3ac 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -8,6 +8,7 @@ namespace Magento\DownloadableImportExport\Model\Import\Product\Type; use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\Downloadable\Model\Url\DomainValidator; use Magento\Framework\EntityManager\MetadataPool; use \Magento\Store\Model\Store; @@ -101,6 +102,8 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ const ERROR_COLS_IS_EMPTY = 'emptyOptions'; + private const ERROR_NOT_IN_DOMAIN_WHITELIST = 'notInDomainWhitelist'; + /** * Validation failure message template definitions * @@ -111,7 +114,8 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ self::ERROR_GROUP_TITLE_NOT_FOUND => 'Group titles not found for downloadable products', self::ERROR_OPTION_NO_TITLE => 'Option no title', self::ERROR_MOVE_FILE => 'Error move file', - self::ERROR_COLS_IS_EMPTY => 'Missing sample and links data for the downloadable product' + self::ERROR_COLS_IS_EMPTY => 'Missing sample and links data for the downloadable product', + self::ERROR_NOT_IN_DOMAIN_WHITELIST => 'Link URL' ]; /** @@ -244,6 +248,11 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ */ protected $downloadableHelper; + /** + * @var DomainValidator + */ + private $domainValidator; + /** * Downloadable constructor * @@ -262,12 +271,14 @@ public function __construct( array $params, \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper, \Magento\DownloadableImportExport\Helper\Data $downloadableHelper, + DomainValidator $domainValidator, MetadataPool $metadataPool = null ) { parent::__construct($attrSetColFac, $prodAttrColFac, $resource, $params, $metadataPool); $this->parameters = $this->_entityModel->getParameters(); $this->_resource = $resource; $this->uploaderHelper = $uploaderHelper; + $this->domainValidator = $domainValidator; $this->downloadableHelper = $downloadableHelper; } @@ -345,7 +356,20 @@ protected function isRowValidSample(array $rowData) } if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES]) && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '') { - $result = $this->isTitle($this->prepareSampleData($rowData[self::COL_DOWNLOADABLE_SAMPLES])); + $sampleData = $this->prepareSampleData($rowData[static::COL_DOWNLOADABLE_SAMPLES]); + + $result = $this->isTitle($sampleData); + foreach ($sampleData as $link) { + if (isset($link['sample_type']) && + $link['sample_type'] === 'url' && + isset($link['sample_url']) && + strlen($link['sample_url']) && + !$this->domainValidator->isValid($link['sample_url']) + ) { + $this->_entityModel->addRowError(static::ERROR_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; + } + } } return $result; } @@ -369,7 +393,20 @@ protected function isRowValidLink(array $rowData) if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) && $rowData[self::COL_DOWNLOADABLE_LINKS] != '' ) { - $result = $this->isTitle($this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS])); + $linkData = $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]); + $result = $this->isTitle($linkData); + + foreach ($linkData as $link) { + if (isset($link['link_type']) && + $link['link_type'] === 'url' && + isset($link['link_url']) && + strlen($link['link_url']) && + !$this->domainValidator->isValid($link['link_url']) + ) { + $this->_entityModel->addRowError(static::ERROR_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; + } + } } return $result; } From 96a3f238f979af9fb01b4e4bb2c1715db2de00d5 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 23 Jul 2019 16:12:28 -0500 Subject: [PATCH 0240/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/Downloadable.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index 48ff56184a3ac..db4747c01b448 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -16,6 +16,7 @@ * Class Downloadable * * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { @@ -102,7 +103,9 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ const ERROR_COLS_IS_EMPTY = 'emptyOptions'; - private const ERROR_NOT_IN_DOMAIN_WHITELIST = 'notInDomainWhitelist'; + private const ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST = 'linkUrlNotInDomainWhitelist'; + + private const ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST = 'sampleUrlNotInDomainWhitelist'; /** * Validation failure message template definitions @@ -115,7 +118,10 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ self::ERROR_OPTION_NO_TITLE => 'Option no title', self::ERROR_MOVE_FILE => 'Error move file', self::ERROR_COLS_IS_EMPTY => 'Missing sample and links data for the downloadable product', - self::ERROR_NOT_IN_DOMAIN_WHITELIST => 'Link URL' + self::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST => + 'Link URL\'s domain is not in list of downloadable_domains in env.php.', + self::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST => + 'Sample URL\'s domain is not in list of downloadable_domains in env.php.' ]; /** @@ -344,6 +350,7 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) * * @param array $rowData * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function isRowValidSample(array $rowData) { @@ -366,7 +373,7 @@ protected function isRowValidSample(array $rowData) strlen($link['sample_url']) && !$this->domainValidator->isValid($link['sample_url']) ) { - $this->_entityModel->addRowError(static::ERROR_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $this->_entityModel->addRowError(static::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); $result = true; } } @@ -379,6 +386,7 @@ protected function isRowValidSample(array $rowData) * * @param array $rowData * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function isRowValidLink(array $rowData) { @@ -403,7 +411,7 @@ protected function isRowValidLink(array $rowData) strlen($link['link_url']) && !$this->domainValidator->isValid($link['link_url']) ) { - $this->_entityModel->addRowError(static::ERROR_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $this->_entityModel->addRowError(static::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); $result = true; } } From be264871ab9c90510e0552885b4e1e6eabfe361d Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Tue, 23 Jul 2019 21:27:59 -0400 Subject: [PATCH 0241/2437] Add test updating bundle cart item quantity --- .../Bundle/AddBundleProductToCartTest.php | 61 +++++++++++++++++++ .../_files/quote_with_bundle_and_options.php | 10 ++- ...quote_with_bundle_and_options_rollback.php | 2 +- 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php index 21fd88519d22e..826083b0b3378 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -144,6 +144,67 @@ public function testAddBundleProductToCart() $this->assertEquals(1, $value['quantity']); } + /** + * @magentoApiDataFixture Magento/Bundle/_files/quote_with_bundle_and_options.php + * @dataProvider dataProviderTestUpdateBundleItemQuantity + */ + public function testUpdateBundleItemQuantity(int $quantity) + { + $this->quoteResource->load( + $this->quote, + 'test_cart_with_bundle_and_options', + 'reserved_order_id' + ); + + $item = current($this->quote->getAllVisibleItems()); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $mutation = <<<QUERY +mutation { + updateCartItems( + input: { + cart_id: "{$maskedQuoteId}" + cart_items: { + cart_item_id: {$item->getId()} + quantity: {$quantity} + } + } + ) { + cart { + items { + id + quantity + product { + sku + } + } + } + } +} +QUERY; + + $response = $this->graphQlMutation($mutation); + + $this->assertArrayHasKey('updateCartItems', $response); + $this->assertArrayHasKey('cart', $response['updateCartItems']); + $cart = $response['updateCartItems']['cart']; + if ($quantity === 0) { + $this->assertCount(0, $cart['items']); + return; + } + + $bundleItem = current($cart['items']); + $this->assertEquals($quantity, $bundleItem['quantity']); + } + + public function dataProviderTestUpdateBundleItemQuantity(): array + { + return [ + [2], + [0], + ]; + } + /** * @magentoApiDataFixture Magento/Bundle/_files/product_1.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php index 03949115ea62c..c79e943ba4be3 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php @@ -8,7 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; -require __DIR__ . 'product_with_multiple_options.php'; +require __DIR__ . '/product_with_multiple_options.php'; $objectManager = Bootstrap::getObjectManager(); @@ -49,6 +49,14 @@ $cart->getQuote()->setReservedOrderId('test_cart_with_bundle_and_options'); $cart->save(); +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($cart->getQuote()->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); + /** @var $objectManager \Magento\TestFramework\ObjectManager */ $objectManager = Bootstrap::getObjectManager(); $objectManager->removeSharedInstance(\Magento\Checkout\Model\Session::class); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php index d32d6fab33319..591aec9190f9f 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php @@ -22,7 +22,7 @@ $quoteIdMask = $objectManager->create(\Magento\Quote\Model\QuoteIdMask::class); $quoteIdMask->delete($quote->getId()); -require __DIR__ . 'product_with_multiple_options_rollback.php'; +require __DIR__ . '/product_with_multiple_options_rollback.php'; $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); From a90961f48324307984e53a851cb4f8502706b071 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 24 Jul 2019 16:20:16 +0300 Subject: [PATCH 0242/2437] [WIP] issue-310-code-refactoring () --- ...ertUserRoleRestrictedAccessActionGroup.xml | 1 - .../AdminNavigateToUserRolesActionGroup.xml | 16 -------- .../AdminOpenAdminUsersPageActionGroup.xml} | 6 +-- .../AdminOpenUserEditPageActionGroup.xml | 23 +++++------ .../User/Test/Mftf/Data/UserRoleData.xml | 2 +- .../Mftf/Test/AdminUpdateUserRoleTest.xml | 39 ++++++++++--------- .../TestCase/UpdateAdminUserEntityTest.xml | 1 + 7 files changed, 38 insertions(+), 50 deletions(-) delete mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml rename app/code/Magento/{Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml => User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml} (62%) diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml index b98731e117e1f..4d9166751ee15 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml @@ -9,7 +9,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertUserRoleRestrictedAccessActionGroup"> - <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml deleted file mode 100644 index d86bc7d11dbbf..0000000000000 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminNavigateToUserRolesActionGroup.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminNavigateToUserRolesActionGroup"> - <click selector="#menu-magento-backend-system" stepKey="clickOnSystemIcon"/> - <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> - <click selector="//span[contains(text(), 'User Roles')]" stepKey="clickToSelectUserRoles"/> - <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml similarity index 62% rename from app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml rename to app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml index 2d451875c6358..e24bb67aa2d2d 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserSuccessSaveMessageActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml @@ -5,10 +5,10 @@ * See COPYING.txt for license details. */ --> - <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertUserSuccessSaveMessageActionGroup"> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the user." stepKey="seeAdminUserInGrid"/> + <actionGroup name="AdminOpenAdminUsersPageActionGroup"> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <waitForPageLoad stepKey="waitForAdminUsersPageLoad"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml index a32027a6ac182..55247e9f6178f 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml @@ -9,14 +9,15 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenUserEditPageActionGroup"> <arguments> - <argument name="user" type="entity"/> - </arguments> - <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> - <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> - <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> - <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> - </actionGroup> -</actionGroups> \ No newline at end of file + <argument name="user" type="entity"/> + </arguments> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> + <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> + <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> + + </actionGroup> + </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 3f437e4c0ad8f..8e46f314110a7 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -25,7 +25,7 @@ <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> <entity name="salesRole" type="role"> - <data key="name" unique="suffix">Sales</data> + <data key="name" unique="suffix">Sales Role</data> <data key="resourceAccess">Custom</data> <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails']</data> </entity> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml index 3e7bb3e1889ef..0d4bebcc97372 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml @@ -18,7 +18,7 @@ </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> </before> <after> <actionGroup ref="logout" stepKey="logOut"/> @@ -27,49 +27,52 @@ <!--Create New User--> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> - <argument name="user" value="NewAdminUser" /> + <argument name="user" value="NewAdminUser"/> </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - <!--Create New Role--> - <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="openUserRolesGrig"/> <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> <argument name="role" value="salesRole"/> </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRole"/> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> <!--Assign new role--> <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"> <argument name="role" value="salesRole"/> </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> - <actionGroup ref="AssertUserSuccessSaveMessageActionGroup" stepKey="seeSuccessSaveUserMessage"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You saved the user."/> + </actionGroup> + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> <argument name="user" value="NewAdminUser"/> </actionGroup> - - <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> + <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> <argument name="adminUser" value="NewAdminUser"/> </actionGroup> - <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> - <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> + <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> - <!--Delete new User--> + <!--Delete new User--> <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> <argument name="user" value="NewAdminUser"/> </actionGroup> - <!--Delete Role--> - <actionGroup ref="AdminNavigateToUserRolesActionGroup" stepKey="goBackToUserRolesGrig"/> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"/> + <!--Delete new Role--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> + <argument name="roleName" value="{{salesRole.name}}"/> + </actionGroup> - </test> - </tests> \ No newline at end of file + </test> + </tests> \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml index a89d1ede80112..88dc1dc9f6295 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\User\Test\TestCase\UpdateAdminUserEntityTest" summary="Update Admin User" ticketId="MAGETWO-24345"> <variation name="UpdateAdminUserEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="tag" xsi:type="string">severity:S3</data> <data name="initialUser/dataset" xsi:type="string">custom_admin_with_default_role</data> <data name="user/data/role_id/dataset" xsi:type="string">role::role_sales</data> From 707e50167e3ded4e6dfa979b27685c03699b0106 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 09:07:52 -0500 Subject: [PATCH 0243/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/Downloadable.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index db4747c01b448..8f7797ea5d1d7 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -268,6 +268,7 @@ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Typ * @param array $params * @param \Magento\DownloadableImportExport\Helper\Uploader $uploaderHelper * @param \Magento\DownloadableImportExport\Helper\Data $downloadableHelper + * @param DomainValidator $domainValidator * @param MetadataPool $metadataPool */ public function __construct( @@ -874,6 +875,7 @@ protected function parseSampleOption($values) /** * Uploading files into the "downloadable/files" media folder. + * * Return a new file name if the same file is already exists. * * @param string $fileName From 6493df9e3c0167d5489aee91db3b29a1af5c953c Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 09:38:30 -0500 Subject: [PATCH 0244/2437] MC-17700: Downloadable Product links --- .../Import/Product/Type/DownloadableTest.php | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php index 776ec9f990f5e..fe0f0ebf4839c 100644 --- a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php +++ b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php @@ -5,6 +5,7 @@ */ namespace Magento\DownloadableImportExport\Model\Import\Product\Type; +use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Framework\App\Filesystem\DirectoryList; /** @@ -32,6 +33,11 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase */ const TEST_PRODUCT_SAMPLES_GROUP_NAME = 'TEST Import Samples'; + /** + * @var DomainManagerInterface + */ + private $domainManager; + /** * @var \Magento\CatalogImportExport\Model\Import\Product */ @@ -47,6 +53,9 @@ class DownloadableTest extends \PHPUnit\Framework\TestCase */ protected $productMetadata; + /** + * @inheritDoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -56,6 +65,17 @@ protected function setUp() /** @var \Magento\Framework\EntityManager\MetadataPool $metadataPool */ $metadataPool = $this->objectManager->get(\Magento\Framework\EntityManager\MetadataPool::class); $this->productMetadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + + $this->domainManager = $this->objectManager->get(DomainManagerInterface::class); + $this->domainManager->addDomains(['www.google.com', 'www.yahoo.com']); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + $this->domainManager->removeDomains(['www.google.com', 'www.yahoo.com']); } /** @@ -112,7 +132,7 @@ public function testDownloadableImport() $downloadableSamples = $product->getDownloadableSamples(); //TODO: Track Fields: id, link_id, link_file and sample_file) - $expectedLinks= [ + $expectedLinks = [ 'file' => [ 'title' => 'TEST Import Link Title File', 'sort_order' => '78', @@ -154,7 +174,7 @@ public function testDownloadableImport() } //TODO: Track Fields: id, sample_id and sample_file) - $expectedSamples= [ + $expectedSamples = [ 'file' => [ 'title' => 'TEST Import Sample File', 'sort_order' => '178', From 733f55e793da46d5b3fbd9c0c7089a46dc49da64 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 10:33:00 -0500 Subject: [PATCH 0245/2437] MC-17700: Downloadable Product links --- .../Import/Product/Type/Downloadable.php | 102 ++++++++++-------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index 8f7797ea5d1d7..b69b8ddacdfca 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -16,7 +16,6 @@ * Class Downloadable * * @SuppressWarnings(PHPMD.TooManyFields) - * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Downloadable extends \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType { @@ -351,34 +350,34 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) * * @param array $rowData * @return bool - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function isRowValidSample(array $rowData) { - $result = false; - if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES]) - && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '' - && $this->sampleGroupTitle($rowData) == '') { - $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); + $hasSampleLinkData = ( + isset($rowData[self::COL_DOWNLOADABLE_SAMPLES]) && + $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '' + ); + + if (!$hasSampleLinkData) { + return false; + } + + $sampleData = $this->prepareSampleData($rowData[static::COL_DOWNLOADABLE_SAMPLES]); + + if ($this->sampleGroupTitle($rowData) == '') { $result = true; + $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); } - if (isset($rowData[self::COL_DOWNLOADABLE_SAMPLES]) - && $rowData[self::COL_DOWNLOADABLE_SAMPLES] != '') { - $sampleData = $this->prepareSampleData($rowData[static::COL_DOWNLOADABLE_SAMPLES]); - - $result = $this->isTitle($sampleData); - foreach ($sampleData as $link) { - if (isset($link['sample_type']) && - $link['sample_type'] === 'url' && - isset($link['sample_url']) && - strlen($link['sample_url']) && - !$this->domainValidator->isValid($link['sample_url']) - ) { - $this->_entityModel->addRowError(static::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); - $result = true; - } + + $result = $result ?? $this->isTitle($sampleData); + + foreach ($sampleData as $link) { + if ($this->hasDomainNotInWhitelist($link, 'sample_type', 'sample_url')) { + $this->_entityModel->addRowError(static::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; } } + return $result; } @@ -387,36 +386,34 @@ protected function isRowValidSample(array $rowData) * * @param array $rowData * @return bool - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function isRowValidLink(array $rowData) { - $result = false; - if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) && - $rowData[self::COL_DOWNLOADABLE_LINKS] != '' && - $this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == '' - ) { + $hasLinkData = ( + isset($rowData[self::COL_DOWNLOADABLE_LINKS]) && + $rowData[self::COL_DOWNLOADABLE_LINKS] != '' + ); + + if (!$hasLinkData) { + return false; + } + + $linkData = $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]); + + if ($this->linksAdditionalAttributes($rowData, 'group_title', self::DEFAULT_GROUP_TITLE) == '') { $this->_entityModel->addRowError(self::ERROR_GROUP_TITLE_NOT_FOUND, $this->rowNum); $result = true; } - if (isset($rowData[self::COL_DOWNLOADABLE_LINKS]) && - $rowData[self::COL_DOWNLOADABLE_LINKS] != '' - ) { - $linkData = $this->prepareLinkData($rowData[self::COL_DOWNLOADABLE_LINKS]); - $result = $this->isTitle($linkData); - - foreach ($linkData as $link) { - if (isset($link['link_type']) && - $link['link_type'] === 'url' && - isset($link['link_url']) && - strlen($link['link_url']) && - !$this->domainValidator->isValid($link['link_url']) - ) { - $this->_entityModel->addRowError(static::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); - $result = true; - } + + $result = $result ?? $this->isTitle($linkData); + + foreach ($linkData as $link) { + if ($this->hasDomainNotInWhitelist($link, 'link_type', 'link_url')) { + $this->_entityModel->addRowError(static::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; } } + return $result; } @@ -908,4 +905,21 @@ protected function clear() $this->productIds = []; return $this; } + + /** + * @param array $link + * @param string $linkTypeKey + * @param string $linkUrlKey + * @return bool + */ + private function hasDomainNotInWhitelist(array $link, string $linkTypeKey, string $linkUrlKey): bool + { + return ( + isset($link[$linkTypeKey]) && + $link[$linkTypeKey] === 'url' && + isset($link[$linkUrlKey]) && + strlen($link[$linkUrlKey]) && + !$this->domainValidator->isValid($link[$linkUrlKey]) + ); + } } From fb4ad49f8e22339229241c3a654ab12f9b313b7f Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 11:22:05 -0500 Subject: [PATCH 0246/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/Downloadable.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index b69b8ddacdfca..0528f2759c4ee 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -907,6 +907,8 @@ protected function clear() } /** + * Does link contain url not in whitelist? + * * @param array $link * @param string $linkTypeKey * @param string $linkUrlKey From 1a548a201b1b3afca20d1700e3b07f4dd0acd0bd Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 12:52:21 -0500 Subject: [PATCH 0247/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/DownloadableTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php index fe0f0ebf4839c..a3230d61e262c 100644 --- a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php +++ b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php @@ -10,6 +10,7 @@ /** * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DownloadableTest extends \PHPUnit\Framework\TestCase { From a5eb4fc57817fb79c1e8409aaa0d8602bada75c4 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 13:14:02 -0500 Subject: [PATCH 0248/2437] MC-17700: Downloadable Product links --- ...CreateDownloadableProductWithInvalidDomainLinkUrlTest.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index 4eb6b82b65918..f2e4bdfb4890f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -28,7 +28,10 @@ <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="saveProduct"/> <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="Link URL's domain is not in list of downloadable_domains in env.php." stepKey="seeLinkUrlInvalidMessage" after="saveProduct" /> <magentoCLI stepKey="addDownloadableDomain2" command="downloadable:domains:add static.magento.com" after="seeLinkUrlInvalidMessage" /> - <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable" after="addDownloadableDomain2"/> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductFormAgain" after="addDownloadableDomain2"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable" after="fillDownloadableProductFormAgain"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkIsLinksPurchasedSeparately" after="checkIsDownloadable"/> <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLinkAgain" after="checkIsLinksPurchasedSeparately"> <argument name="link" value="downloadableLink"/> From b60fead6de598f78c6811a2d30fdede49d3c773c Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 24 Jul 2019 15:24:58 -0500 Subject: [PATCH 0249/2437] Allow admin to opt out of admin analytics tracking - created modal, database table --- .../Magento/AdminAnalytics/Block/Setting.php | 52 +++++++++ .../Adminhtml/Config/DisableAdminUsage.php | 100 +++++++++++++++++ .../Adminhtml/Config/EnableAdminUsage.php | 100 +++++++++++++++++ .../Adminhtml/Config/MarkUserNotified.php | 95 ++++++++++++++++ .../Model/Condition/CanViewNotification.php | 104 +++++++++++++++++ .../Model/ContentProviderInterface.php | 24 ++++ .../Model/ResourceModel/Viewer/Logger.php | 106 ++++++++++++++++++ .../AdminAnalytics/Model/Viewer/Log.php | 54 +++++++++ .../AdminAnalytics/etc/adminhtml/routes.xml | 14 +++ .../Magento/AdminAnalytics/etc/config.xml | 18 --- .../Magento/AdminAnalytics/etc/db_schema.xml | 30 +++++ .../etc/db_schema_whitelist.json | 15 +++ .../view/adminhtml/layout/default.xml | 2 + .../adminhtml/templates/confirm_popup.phtml | 86 ++++++++++++++ 14 files changed, 782 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/Block/Setting.php create mode 100644 app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php create mode 100644 app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php create mode 100644 app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php create mode 100644 app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php create mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php create mode 100644 app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php create mode 100644 app/code/Magento/AdminAnalytics/Model/Viewer/Log.php create mode 100644 app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml delete mode 100644 app/code/Magento/AdminAnalytics/etc/config.xml create mode 100644 app/code/Magento/AdminAnalytics/etc/db_schema.xml create mode 100644 app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml diff --git a/app/code/Magento/AdminAnalytics/Block/Setting.php b/app/code/Magento/AdminAnalytics/Block/Setting.php new file mode 100644 index 0000000000000..516c854c29337 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Block/Setting.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Block; + +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Config\Model\Config\Factory; + +class Setting extends Template +{ + private $configFactory; + /** + * @param Context $context + * @param Factory $configFactory + * @param array $data + */ + public function __construct( + Context $context, + Factory $configFactory, + array $data = [] + ) { + $this->configFactory = $configFactory; + parent::__construct($context, $data); + } + + /** + * Sets the admin usage's configuration setting to yes + */ + public function enableAdminUsage() + { + $configModel = $this->configFactory->create(); + $configModel->setDataByPath('admin/usage/enabled', 1); + $configModel->save(); + } + + /** + * Sets the admin usage's configuration setting to no + */ + public function disableAdminUsage() + { + $configModel = $this->configFactory->create(); + $configModel->setDataByPath('admin/usage/enabled', 0); + $configModel->save(); + } + + public function showModal() { + return false; + } +} diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php new file mode 100644 index 0000000000000..eced53d960e0e --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; + +use Magento\Backend\App\Action; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Controller\ResultFactory; +use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; +use Magento\Framework\App\ProductMetadataInterface; +use Psr\Log\LoggerInterface; +use Magento\Config\Model\Config\Factory; + +/** + * Controller to record that the current admin user has seen the release notification content + */ +class DisableAdminUsage extends Action +{ + + + private $configFactory; + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var NotificationLogger + */ + private $notificationLogger; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * MarkUserNotified constructor. + * + * @param Action\Context $context + * @param ProductMetadataInterface $productMetadata + * @param NotificationLogger $notificationLogger + * @param LoggerInterface $logger + */ + public function __construct( + Action\Context $context, + ProductMetadataInterface $productMetadata, + NotificationLogger $notificationLogger, + Factory $configFactory, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->configFactory = $configFactory; + $this->productMetadata = $productMetadata; + $this->notificationLogger = $notificationLogger; + $this->logger = $logger; + } + public function disableAdminUsage() + { + $configModel = $this->configFactory->create(); + $configModel->setDataByPath('admin/usage/enabled', 0); + $configModel->save(); + } + + public function markUserNotified() + { + $responseContent = [ + 'success' => $this->notificationLogger->log( + $this->_auth->getUser()->getId(), + $this->productMetadata->getVersion(), + 0 + ), + 'error_message' => '' + ]; + + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + return $resultJson->setData($responseContent); + } + /** + * Log information about the last shown advertisement + * + * @return \Magento\Framework\Controller\ResultInterface + */ + public function execute() + { + $this->disableAdminUsage(); + $this->markUserNotified(); + } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } +} diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php new file mode 100644 index 0000000000000..ea93212dbc2d0 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; + +use Magento\Backend\App\Action; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Controller\ResultFactory; +use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; +use Magento\Framework\App\ProductMetadataInterface; +use Psr\Log\LoggerInterface; +use Magento\Config\Model\Config\Factory; + +/** + * Controller to record that the current admin user has seen the release notification content + */ +class EnableAdminUsage extends Action +{ + + + private $configFactory; + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var NotificationLogger + */ + private $notificationLogger; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * MarkUserNotified constructor. + * + * @param Action\Context $context + * @param ProductMetadataInterface $productMetadata + * @param NotificationLogger $notificationLogger + * @param LoggerInterface $logger + */ + public function __construct( + Action\Context $context, + ProductMetadataInterface $productMetadata, + NotificationLogger $notificationLogger, + Factory $configFactory, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->configFactory = $configFactory; + $this->productMetadata = $productMetadata; + $this->notificationLogger = $notificationLogger; + $this->logger = $logger; + } + public function enableAdminUsage() + { + $configModel = $this->configFactory->create(); + $configModel->setDataByPath('admin/usage/enabled', 1); + $configModel->save(); + } + + public function markUserNotified() + { + $responseContent = [ + 'success' => $this->notificationLogger->log( + $this->_auth->getUser()->getId(), + $this->productMetadata->getVersion(), + 1 + ), + 'error_message' => '' + ]; + + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + return $resultJson->setData($responseContent); + } + /** + * Log information about the last shown advertisement + * + * @return \Magento\Framework\Controller\ResultInterface + */ + public function execute() + { + $this->enableAdminUsage(); + $this->markUserNotified(); + } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } +} diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php new file mode 100644 index 0000000000000..4c252329d07a7 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; + +use Magento\Backend\App\Action; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Controller\ResultFactory; +use Magento\ReleaseNotification\Model\ResourceModel\Viewer\Logger as NotificationLogger; +use Magento\Framework\App\ProductMetadataInterface; +use Psr\Log\LoggerInterface; + +/** + * Controller to record that the current admin user has seen the release notification content + */ +class MarkUserNotified extends Action +{ + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var NotificationLogger + */ + private $notificationLogger; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * MarkUserNotified constructor. + * + * @param Action\Context $context + * @param ProductMetadataInterface $productMetadata + * @param NotificationLogger $notificationLogger + * @param LoggerInterface $logger + */ + public function __construct( + Action\Context $context, + ProductMetadataInterface $productMetadata, + NotificationLogger $notificationLogger, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->productMetadata = $productMetadata; + $this->notificationLogger = $notificationLogger; + $this->logger = $logger; + } + + /** + * Log information about the last shown advertisement + * + * @return \Magento\Framework\Controller\ResultInterface + */ + public function execute() + { + try { + $responseContent = [ + 'success' => $this->notificationLogger->log( + $this->_auth->getUser()->getId(), + $this->productMetadata->getVersion() + ), + 'error_message' => '' + ]; + } catch (LocalizedException $e) { + $this->logger->error($e->getMessage()); + $responseContent = [ + 'success' => false, + 'error_message' => $e->getMessage() + ]; + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $responseContent = [ + 'success' => false, + 'error_message' => __('It is impossible to log user action') + ]; + } + $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); + return $resultJson->setData($responseContent); + } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } +} diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php new file mode 100644 index 0000000000000..31311dc015492 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Model\Condition; + +use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; +use Magento\Backend\Model\Auth\Session; +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\View\Layout\Condition\VisibilityConditionInterface; +use Magento\Framework\App\CacheInterface; + +/** + * Dynamic validator for UI release notification, manage UI component visibility. + * Return true if the logged in user has not seen the notification. + */ +class CanViewNotification implements VisibilityConditionInterface +{ + /** + * Unique condition name. + * + * @var string + */ + private static $conditionName = 'can_view_notification'; + + /** + * Prefix for cache + * + * @var string + */ + private static $cachePrefix = 'release-notification-popup-'; + + /** + * @var Logger + */ + private $viewerLogger; + + /** + * @var Session + */ + private $session; + + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var CacheInterface + */ + private $cacheStorage; + + /** + * CanViewNotification constructor. + * + * @param Logger $viewerLogger + * @param Session $session + * @param ProductMetadataInterface $productMetadata + * @param CacheInterface $cacheStorage + */ + public function __construct( + Logger $viewerLogger, + Session $session, + ProductMetadataInterface $productMetadata, + CacheInterface $cacheStorage + ) { + $this->viewerLogger = $viewerLogger; + $this->session = $session; + $this->productMetadata = $productMetadata; + $this->cacheStorage = $cacheStorage; + } + + /** + * Validate if notification popup can be shown and set the notification flag + * + * @inheritdoc + */ + public function isVisible(array $arguments) + { + $userId = $this->session->getUser()->getId(); + $cacheKey = self::$cachePrefix . $userId; + $value = $this->cacheStorage->load($cacheKey); + if ($value === false) { + $value = version_compare( + $this->viewerLogger->get($userId)->getLastViewVersion(), + $this->productMetadata->getVersion(), + '<' + ); + $this->cacheStorage->save(false, $cacheKey); + } + return (bool)$value; + } + + /** + * Get condition name + * + * @return string + */ + public function getName() + { + return self::$conditionName; + } +} diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php b/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php new file mode 100644 index 0000000000000..ea67be7019e19 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Model; + +/** + * Requests the release notification content data from a defined service + */ +interface ContentProviderInterface +{ + /** + * Retrieves the release notification content data. + * + * @param string $version + * @param string $edition + * @param string $locale + * + * @return string|false + */ + public function getContent($version, $edition, $locale); +} diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php new file mode 100644 index 0000000000000..ac5d7b1f94db4 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\AdminAnalytics\Model\ResourceModel\Viewer; + +use Magento\ReleaseNotification\Model\Viewer\Log; +use Magento\ReleaseNotification\Model\Viewer\LogFactory; +use Magento\Framework\App\ResourceConnection; + +/** + * Release notification viewer log data logger. + * + * Saves and retrieves release notification viewer log data. + */ +class Logger +{ + /** + * Log table name + */ + const LOG_TABLE_NAME = 'admin_usage_viewer_log'; + + /** + * @var Resource + */ + private $resource; + + /** + * @var LogFactory + */ + private $logFactory; + + /** + * Logger constructor. + * @param ResourceConnection $resource + * @param LogFactory $logFactory + */ + public function __construct( + ResourceConnection $resource, + LogFactory $logFactory + ) { + $this->resource = $resource; + $this->logFactory = $logFactory; + } + + /** + * Save (insert new or update existing) log. + * + * @param int $viewerId + * @param string $lastViewVersion + * @param int $isAdminUsageEnabled + * @return bool + */ + public function log(int $viewerId, string $lastViewVersion, int $isAdminUsageEnabled) : bool + { + /** @var \Magento\Framework\DB\Adapter\AdapterInterface $connection */ + $connection = $this->resource->getConnection(ResourceConnection::DEFAULT_CONNECTION); + $connection->insertOnDuplicate( + $this->resource->getTableName(self::LOG_TABLE_NAME), + [ + 'viewer_id' => $viewerId, + 'last_view_version' => $lastViewVersion, + 'is_admin_usage_enabled' => $isAdminUsageEnabled + ], + [ + 'last_view_version', + 'is_admin_usage_enabled' + ] + ); + return true; + } + + /** + * Get log by viewer Id. + * + * @param int $viewerId + * @return Log + */ + public function get(int $viewerId) : Log + { + return $this->logFactory->create(['data' => $this->loadLogData($viewerId)]); + } + + /** + * Load release notification viewer log data by viewer id + * + * @param int $viewerId + * @return array + */ + private function loadLogData(int $viewerId) : array + { + $connection = $this->resource->getConnection(); + $select = $connection->select() + ->from($this->resource->getTableName(self::LOG_TABLE_NAME)) + ->where('viewer_id = ?', $viewerId); + + $data = $connection->fetchRow($select); + if (!$data) { + $data = []; + } + return $data; + } +} diff --git a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php new file mode 100644 index 0000000000000..28621b36d9b79 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Model\Viewer; + +use Magento\Framework\DataObject; + +/** + * Release notification viewer log resource + */ +class Log extends DataObject +{ + /** + * Get log id + * + * @return int + */ + public function getId() + { + return $this->getData('id'); + } + + /** + * Get viewer id + * + * @return int + */ + public function getViewerId() + { + return $this->getData('viewer_id'); + } + + /** + * Get last viewed product version + * + * @return string + */ + public function getLastViewVersion() + { + return $this->getData('last_view_version'); + } + + /** + * Get admin usage enabled + * + * @return int + */ + public function getIsAdminUsageEnabled() + { + return $this->getData('is_admin_usage_enabled'); + } +} diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml new file mode 100644 index 0000000000000..e1024b12bc42d --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> + <router id="admin"> + <route id="adminAnalytics" frontName="adminAnalytics"> + <module name="Magento_AdminAnalytics" /> + </route> + </router> +</config> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/etc/config.xml b/app/code/Magento/AdminAnalytics/etc/config.xml deleted file mode 100644 index 85b6b6879f083..0000000000000 --- a/app/code/Magento/AdminAnalytics/etc/config.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> - <default> - <admin> - <usage> - <enabled> - 0 - </enabled> - </usage> - </admin> - </default> -</config> diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema.xml b/app/code/Magento/AdminAnalytics/etc/db_schema.xml new file mode 100644 index 0000000000000..d640c6af45286 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/db_schema.xml @@ -0,0 +1,30 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> + <table name="admin_usage_viewer_log" resource="default" engine="innodb" + comment="Admin Notification Viewer Log Table"> + <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" + comment="Log ID"/> + <column xsi:type="int" name="viewer_id" padding="10" unsigned="true" nullable="false" identity="false" + comment="Viewer admin user ID"/> + <column xsi:type="varchar" name="last_view_version" nullable="false" length="16" + comment="Viewer last view on product version"/> + <column xsi:type="smallint" name="is_admin_usage_enabled" padding="5" unsigned="true" nullable="false" identity="false" + default="0" comment="Flag if admin usage is enabled"/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="id"/> + </constraint> + <constraint xsi:type="foreign" referenceId="ADMIN_NOTIFICATION_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID" + table="admin_notification_viewer_log" column="viewer_id" referenceTable="admin_user" + referenceColumn="user_id" onDelete="CASCADE"/> + <constraint xsi:type="unique" referenceId="ADMIN_NOTIFICATION_VIEWER_LOG_VIEWER_ID"> + <column name="viewer_id"/> + </constraint> + </table> +</schema> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json new file mode 100644 index 0000000000000..01fb68d4b58f7 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json @@ -0,0 +1,15 @@ +{ + "admin_usage_viewer_log": { + "column": { + "id": true, + "viewer_id": true, + "last_view_version": true, + "is_admin_usage_enabled": true + }, + "constraint": { + "PRIMARY": true, + "ADMIN_USAGE_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID": true, + "ADMIN_USAGE_VIEWER_LOG_VIEWER_ID": true + } + } +} \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 8ec8ac3bfaf54..86de70f75f75d 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -13,6 +13,8 @@ <argument name="tracking_url" xsi:type="string">//assets.adobedtm.com/launch-EN30eb7ffa064444f1b8b0368ef38fd3a9.min.js</argument> </arguments> </block> + + <block name="confirm_popup_block" template="Magento_AdminAnalytics::confirm_popup.phtml"/> </referenceContainer> </body> </page> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml new file mode 100644 index 0000000000000..59b972fd5b536 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> + +<div class="confirmation-modal-content"> + <p>Help us improve Magento Admin by allowing us to collect usage data.</p> + <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> + <p>You can learn more and opt out at any time by following the instructions in <a href="https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html" target="_blank">merchant documentation</a></p> +</div> + +<script> + require([ + 'jquery', + 'Magento_Ui/js/modal/modal' + ], function ($) { + 'use strict'; + + $('.confirmation-modal-content').modal({ + imports: { + logAction: '${ $.provider }:data.logAction' + }, + title: 'Allow admin usage data collection', + autoOpen: true, + type: 'popup', + clickableOverlay: false, + responsive: true, + keyEventHandlers: { + escapeKey: function(){} + }, + opened: function($Event){ + $('.modal-header button.action-close', $Event.srcElement).hide(); + }, + buttons: [ + { + text: $.mage.__(`Don't Allow`), + class: 'action', + click: function(){ + console.log(`I clicked Don't Allow`); + + + var data = { + 'form_key': window.FORM_KEY + }; + console.log(data.logAction) + $.ajax({ + type: 'POST', + url: '/magento2ce/admin_michell/adminAnalytics/config/disableAdminUsage', + data: data, + showLoader: true + }).done(function (xhr) { + if (xhr.error) { + self.onError(xhr); + } + }).fail(this.onError); + this.closeModal(); + }, + }, + { + text: $.mage.__('Ok'), + class: 'action', + click: function(){ + console.log("I clicked Ok"); + var data = { + 'form_key': window.FORM_KEY + }; + $.ajax({ + type: 'POST', + url: '/magento2ce/admin_michell/adminAnalytics/config/enableAdminUsage', + data: data, + showLoader: true + }).done(function (xhr) { + if (xhr.error) { + self.onError(xhr); + } + }).fail(this.onError); + + this.closeModal(); + }, + } + ], + }); + }); +</script> From 999f2b3cfd31dab579fcbcb51b614a956f8d08dd Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Wed, 24 Jul 2019 16:55:35 -0500 Subject: [PATCH 0250/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/Downloadable.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php index 0528f2759c4ee..fbee9c093b04f 100644 --- a/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php +++ b/app/code/Magento/DownloadableImportExport/Model/Import/Product/Type/Downloadable.php @@ -372,6 +372,11 @@ protected function isRowValidSample(array $rowData) $result = $result ?? $this->isTitle($sampleData); foreach ($sampleData as $link) { + if ($this->hasDomainNotInWhitelist($link, 'link_type', 'link_url')) { + $this->_entityModel->addRowError(static::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; + } + if ($this->hasDomainNotInWhitelist($link, 'sample_type', 'sample_url')) { $this->_entityModel->addRowError(static::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); $result = true; @@ -412,6 +417,11 @@ protected function isRowValidLink(array $rowData) $this->_entityModel->addRowError(static::ERROR_LINK_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); $result = true; } + + if ($this->hasDomainNotInWhitelist($link, 'sample_type', 'sample_url')) { + $this->_entityModel->addRowError(static::ERROR_SAMPLE_URL_NOT_IN_DOMAIN_WHITELIST, $this->rowNum); + $result = true; + } } return $result; From 60cc64dfcd54338bb604027b634f56bcde9bcb2a Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 25 Jul 2019 11:13:49 +0300 Subject: [PATCH 0251/2437] Fixing and providing a possibility to go to Admin Edit CMS Page directly by using page_id --- .../ActionGroup/AdminOpenCmsPageActionGroup.xml | 16 ++++++++++++++++ .../Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml new file mode 100644 index 0000000000000..7e907b5b395a4 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCmsPageActionGroup"> + <arguments> + <argument name="page_id" type="string"/> + </arguments> + <amOnPage url="{{AdminCmsPageEditPage.url(page_id)}}" stepKey="openEditCmsPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml new file mode 100644 index 0000000000000..978b6d6a6d261 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{id}}" parameterized="true" module="Magento_Cms"> + <section name="CmsNewPagePageActionsSection"/> + <section name="CmsNewPagePageBasicFieldsSection"/> + <section name="CmsNewPagePageContentSection"/> + <section name="CmsNewPagePageSeoSection"/> + </page> +</pages> From af53138724451b0af5c8b52729089bd30a53678a Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Thu, 25 Jul 2019 16:38:28 +0300 Subject: [PATCH 0252/2437] Remove the space before "/>?" --- .../AssertAdminUserIsInGridActionGroup.xml | 8 ++++---- .../AssertUserRoleRestrictedAccessActionGroup.xml | 2 +- .../ActionGroup/AdminOpenUserEditPageActionGroup.xml | 8 ++++---- .../ActionGroup/AdminUpdateUserRoleActionGroup.xml | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml index 01e6b67b59a6c..f32c9dc0c75c4 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml @@ -12,11 +12,11 @@ <arguments> <argument name="user" type="entity"/> </arguments> - <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter" /> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> <waitForPageLoad stepKey="waitForFiltersReset" time="15"/> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml index 4d9166751ee15..0747eab31588e 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml @@ -9,6 +9,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertUserRoleRestrictedAccessActionGroup"> - <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage" /> + <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml index 55247e9f6178f..f7348d914f37f 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenUserEditPageActionGroup.xml @@ -11,11 +11,11 @@ <arguments> <argument name="user" type="entity"/> </arguments> - <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid" /> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName" /> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch" /> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser" /> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> <waitForPageLoad stepKey="waitForUserEditPageLoad" time="15"/> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml index 313bf0b215d68..9ec4f2ef6a354 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml @@ -11,16 +11,16 @@ <arguments> <argument name="role" type="entity"/> </arguments> - <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword" /> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword"/> <scrollToTopOfPage stepKey="scrollToTop"/> <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> <click selector="{{AdminNewUserFormSection.userRoleTab}}" stepKey="openUserRoleTab"/> <waitForPageLoad stepKey="waitForUserRoleTabOpened" /> - <click selector="{{AdminNewUserFormSection.resetFilter}}" stepKey="resetGridFilter" /> - <waitForPageLoad stepKey="waitForFiltersReset" /> - <fillField selector="{{AdminNewUserFormSection.roleFilterField}}" userInput="{{role.name}}" stepKey="fillRoleFilterField" /> - <click selector="{{AdminNewUserFormSection.search}}" stepKey="clickSearchButton" /> - <waitForPageLoad stepKey="waitForFiltersApplied" /> + <click selector="{{AdminNewUserFormSection.resetFilter}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminNewUserFormSection.roleFilterField}}" userInput="{{role.name}}" stepKey="fillRoleFilterField"/> + <click selector="{{AdminNewUserFormSection.search}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForFiltersApplied"/> <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(role.name)}}" stepKey="assignRole"/> </actionGroup> </actionGroups> \ No newline at end of file From dad6118554bce879499686848257875f95d349f6 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 25 Jul 2019 14:33:42 -0500 Subject: [PATCH 0253/2437] MC-17700: Downloadable Product links --- .../Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml index 19477fb804848..0e7396541e757 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml @@ -115,7 +115,7 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> From a242f2dc1b8c6e84a8ee569e6a3bcf0f74881c05 Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Thu, 25 Jul 2019 15:43:44 -0500 Subject: [PATCH 0254/2437] MC-17147: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails --- .../Catalog/Model/Product/Compare/ListCompare.php | 2 +- .../Model/Product/Compare/ListCompareTest.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php index 12dbaf0c29f4e..0cf36214e4c31 100644 --- a/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php @@ -90,7 +90,7 @@ public function addProduct($product) $this->_addVisitorToItem($item); $item->loadByProduct($product); - if (!$item->getId()) { + if (!$item->getId() && $item->getProductId()) { $item->addProductData($product); $item->save(); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php index 3fa02aebe9170..24bba7c77c986 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php @@ -44,6 +44,9 @@ protected function tearDown() $this->_session->setCustomerId(null); } + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ public function testAddProductWithSession() { $this->_session->setCustomerId(1); @@ -51,8 +54,15 @@ public function testAddProductWithSession() $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Model\Product::class) ->load(1); - $this->_model->addProduct($product); + /** @var $product2 \Magento\Catalog\Model\Product */ + $product2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Model\Product::class) + ->load(6); + $this->_model->addProducts([$product->getId(), $product2->getId(), 'none', 99]); $this->assertTrue($this->_model->hasItems(1, $this->_visitor->getId())); + $this->assertTrue($this->_model->hasItems(6, $this->_visitor->getId())); + $this->assertFalse($this->_model->hasItems('none', $this->_visitor->getId())); + $this->assertFalse($this->_model->hasItems(99, $this->_visitor->getId())); } public function testAddProductWithoutSession() From 098f26290834b05abedfdc246c7a42c6fab3aaff Mon Sep 17 00:00:00 2001 From: David Haecker <dhaecker@magento.com> Date: Thu, 25 Jul 2019 15:47:02 -0500 Subject: [PATCH 0255/2437] MC-17700: Downloadable Product links - Fixing mftf cli cmds --- .../Test/Mftf/Test/SearchEntityResultsTest.xml | 2 +- ...DownloadableProductFromShoppingCartTest.xml | 2 +- .../OnePageCheckoutWithAllProductTypesTest.xml | 2 +- ...ddDownloadableProductToShoppingCartTest.xml | 2 +- ...loadableProductFromMiniShoppingCartTest.xml | 2 +- .../StorefrontClearAllCompareProductsTest.xml | 2 +- ...nAddDefaultVideoDownloadableProductTest.xml | 4 ++-- ...ableProductAndAssignItToCustomStoreTest.xml | 4 ++-- ...ownloadableProductWithCustomOptionsTest.xml | 2 +- ...nloadableProductWithDefaultSetLinksTest.xml | 2 +- ...teDownloadableProductWithGroupPriceTest.xml | 2 +- ...eDownloadableProductWithManageStockTest.xml | 2 +- ...loadableProductWithOutOfStockStatusTest.xml | 2 +- ...DownloadableProductWithSpecialPriceTest.xml | 2 +- ...ateDownloadableProductWithTierPriceText.xml | 6 ------ ...oductWithoutFillingQuantityAndStockTest.xml | 2 +- ...ownloadableProductWithoutTaxClassIdTest.xml | 2 +- .../AdminDeleteDownloadableProductTest.xml | 2 +- ...moveDefaultVideoDownloadableProductTest.xml | 4 ++-- ...nceCatalogSearchDownloadableProductTest.xml | 18 +++++++++--------- ...nloadableProductFromGuestToCustomerTest.xml | 2 +- ...oductsListWidgetDownloadableProductTest.xml | 4 ++-- 22 files changed, 33 insertions(+), 39 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index f98e715963d2e..9a2103ee75a7a 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -378,9 +378,9 @@ </createData> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml index 1eee2d510e7bc..e16ef70c23e3d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml @@ -31,9 +31,9 @@ </createData> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete downloadable product --> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteDownloadableProduct"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Add downloadable product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 56d1b97974bbc..e85a47ab7a91d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -91,6 +91,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -110,7 +111,6 @@ <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Add Simple Product to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index 9d595510811aa..ec9852a6a939d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -32,9 +32,9 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!-- Open Downloadable Product page --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index 852e46541c4fa..0fa503e1783b5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -31,9 +31,9 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!-- Open Downloadable Product page --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index 3069ea89099e2..374a4be581bcf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -109,6 +109,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Logout --> <actionGroup ref="logout" stepKey="logoutOfAdmin1"/> @@ -122,7 +123,6 @@ <deleteData createDataKey="createBundleProduct1" stepKey="deleteBundleProduct1"/> <deleteData createDataKey="createGroupedProduct1" stepKey="deleteGroupedProduct1"/> <deleteData createDataKey="createDownloadableProduct1" stepKey="deleteDownloadableProduct1"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer1"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml index 4bbd815e8db01..d95ddaf12470d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -19,10 +19,10 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="enableAdminAccountSharing"/> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="setStoreDefaultConfig"/> </after> <!-- Create a downloadable product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index fc638633fbf3f..4f07334640cf3 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -20,14 +20,15 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -41,7 +42,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create store view --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml index f45e5f1a47e43..54a2ff606f384 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create Downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index 2fcc87b71bbb4..8194e600673cb 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml index afac869d69c3f..06cf31b763f1c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml index 081230760e1d4..fb6a48254fa8d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml index a815d838c29d9..5e3fe6836f7e9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create Downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml index f0cc503f74c4d..fb59d51831bae 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml index f6455766a0907..ca4e560506ad0 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithTierPriceText.xml @@ -19,12 +19,6 @@ <group value="Downloadable"/> <group value="mtf_migrated"/> </annotations> - <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> - </before> - <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> - </after> <remove keyForRemoval="addCustomerGroupPrice"/> <!-- Add tier price to product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml index 6e29fe13d85e6..af9487e3e6a23 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml index 757ac7958f5cd..dd7e3331a0ed2 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml @@ -28,6 +28,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -38,7 +39,6 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> </after> <!-- Create downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml index 83e0c587c078e..07124ea4846be 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml @@ -32,9 +32,9 @@ </createData> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteDownloadableProductFilteredBySkuAndName"> <argument name="product" value="$$createDownloadableProduct$$"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index bc929cd3a68c7..0d98862d9a5e7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -19,10 +19,10 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="enableAdminAccountSharing"/> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="setStoreDefaultConfig"/> </after> <!-- Create a downloadable product --> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml index 0e7396541e757..2a320fa674e74 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml @@ -19,7 +19,7 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -29,7 +29,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchDownloadableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> @@ -43,7 +43,7 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -53,7 +53,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchDownloadableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> @@ -67,7 +67,7 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -77,7 +77,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchDownloadableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> @@ -91,7 +91,7 @@ <group value="Downloadable"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="product"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -101,7 +101,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchDownloadableByPriceTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> @@ -125,7 +125,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="delete"/> </after> </test> </tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml index 25dfe1adcb7c8..b9773415059ec 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -29,10 +29,10 @@ </createData> </before> <after> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> </after> <!--Step 1: Go to Storefront as Guest--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml index 9fed2d68c98ca..94fca6f507637 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -21,10 +21,10 @@ </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> + <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com" before="loginAsAdmin"/> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> + <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com" before="logout"/> </after> <!-- A Cms page containing the New Products Widget gets created here via extends --> From fa604dc7b68c2cbbf94b8379c9a5140c8606a990 Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Thu, 25 Jul 2019 16:24:04 -0500 Subject: [PATCH 0256/2437] MC-17147: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails --- .../Model/Product/Compare/ListCompare.php | 35 +++++++++++++++++-- .../Model/Product/Compare/ListCompareTest.php | 31 +++++++++++----- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php index 0cf36214e4c31..bfd36360ee559 100644 --- a/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Model/Product/Compare/ListCompare.php @@ -5,7 +5,10 @@ */ namespace Magento\Catalog\Model\Product\Compare; +use Magento\Catalog\Model\ProductRepository; use Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; /** * Product Compare List Model @@ -51,6 +54,11 @@ class ListCompare extends \Magento\Framework\DataObject */ protected $_compareItemFactory; + /** + * @var ProductRepository + */ + private $productRepository; + /** * Constructor * @@ -60,6 +68,7 @@ class ListCompare extends \Magento\Framework\DataObject * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Customer\Model\Visitor $customerVisitor * @param array $data + * @param ProductRepository|null $productRepository */ public function __construct( \Magento\Catalog\Model\Product\Compare\ItemFactory $compareItemFactory, @@ -67,13 +76,15 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Product\Compare\Item $catalogProductCompareItem, \Magento\Customer\Model\Session $customerSession, \Magento\Customer\Model\Visitor $customerVisitor, - array $data = [] + array $data = [], + ProductRepository $productRepository = null ) { $this->_compareItemFactory = $compareItemFactory; $this->_itemCollectionFactory = $itemCollectionFactory; $this->_catalogProductCompareItem = $catalogProductCompareItem; $this->_customerSession = $customerSession; $this->_customerVisitor = $customerVisitor; + $this->productRepository = $productRepository ?: ObjectManager::getInstance()->create(ProductRepository::class); parent::__construct($data); } @@ -82,6 +93,7 @@ public function __construct( * * @param int|\Magento\Catalog\Model\Product $product * @return $this + * @throws \Exception */ public function addProduct($product) { @@ -90,7 +102,7 @@ public function addProduct($product) $this->_addVisitorToItem($item); $item->loadByProduct($product); - if (!$item->getId() && $item->getProductId()) { + if (!$item->getId() && $this->productExists($product)) { $item->addProductData($product); $item->save(); } @@ -98,6 +110,25 @@ public function addProduct($product) return $this; } + /** + * Check product exists. + * + * @param int|\Magento\Catalog\Model\Product $product + * @return bool + */ + private function productExists($product) + { + if ($product instanceof \Magento\Catalog\Model\Product && $product->getId()) { + return true; + } + try { + $product = $this->productRepository->getById((int)$product); + return !empty($product->getId()); + } catch (NoSuchEntityException $e) { + return false; + } + } + /** * Add products to compare list * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php index 24bba7c77c986..98b264a8991bc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Compare/ListCompareTest.php @@ -6,10 +6,6 @@ namespace Magento\Catalog\Model\Product\Compare; -/** - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoDataFixture Magento/Customer/_files/customer.php - */ class ListCompareTest extends \PHPUnit\Framework\TestCase { /** @@ -45,7 +41,8 @@ protected function tearDown() } /** - * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Customer/_files/customer.php */ public function testAddProductWithSession() { @@ -58,13 +55,29 @@ public function testAddProductWithSession() $product2 = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Model\Product::class) ->load(6); - $this->_model->addProducts([$product->getId(), $product2->getId(), 'none', 99]); + $products = [$product->getId(), $product2->getId()]; + $this->_model->addProducts($products); + $this->assertTrue($this->_model->hasItems(1, $this->_visitor->getId())); - $this->assertTrue($this->_model->hasItems(6, $this->_visitor->getId())); - $this->assertFalse($this->_model->hasItems('none', $this->_visitor->getId())); - $this->assertFalse($this->_model->hasItems(99, $this->_visitor->getId())); } + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testAddProductWithSessionNeg() + { + $this->_session->setCustomerId(1); + $products = ['none', 99]; + $this->_model->addProducts($products); + + $this->assertFalse($this->_model->hasItems(1, $this->_visitor->getId())); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Customer/_files/customer.php + */ public function testAddProductWithoutSession() { /** @var $product \Magento\Catalog\Model\Product */ From 2976918d75f2b2e78904e05d81073f17831f17ca Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Thu, 25 Jul 2019 17:02:37 -0500 Subject: [PATCH 0257/2437] MC-15298: Allow admin to opt out of admin analytics tracking - UI added --- .../Adminhtml/Config/DisableAdminUsage.php | 1 + .../Adminhtml/Config/EnableAdminUsage.php | 1 - .../Model/Condition/CanViewNotification.php | 28 +- .../Http/HttpContentProvider.php | 108 ++++++++ .../Model/ContentProvider/Http/UrlBuilder.php | 64 +++++ .../Model/ResourceModel/Viewer/Logger.php | 4 +- .../DataProvider/Modifier/Notifications.php | 246 ++++++++++++++++++ .../DataProvider/NotificationDataProvider.php | 214 +++++++++++++++ .../Ui/Renderer/NotificationRenderer.php | 208 +++++++++++++++ .../view/adminhtml/layout/default.xml | 7 +- .../adminhtml/templates/confirm_popup.phtml | 5 - .../ui_component/admin_usage_notification.xml | 122 +++++++++ .../view/adminhtml/web/js/modal/component.js | 90 +++++++ 13 files changed, 1075 insertions(+), 23 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php create mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php create mode 100644 app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php create mode 100644 app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php create mode 100644 app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index eced53d960e0e..b81c60e4b3adb 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -22,6 +22,7 @@ class DisableAdminUsage extends Action private $configFactory; + /** * @var ProductMetadataInterface */ diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index ea93212dbc2d0..d1b879ae927ab 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -20,7 +20,6 @@ class EnableAdminUsage extends Action { - private $configFactory; /** * @var ProductMetadataInterface diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 31311dc015492..b33677ea05f26 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -22,14 +22,14 @@ class CanViewNotification implements VisibilityConditionInterface * * @var string */ - private static $conditionName = 'can_view_notification'; + private static $conditionName = 'can_view_admin_usage_notification'; /** * Prefix for cache * * @var string */ - private static $cachePrefix = 'release-notification-popup-'; + private static $cachePrefix = 'admin-usage-notification-popup-'; /** * @var Logger @@ -78,18 +78,18 @@ public function __construct( */ public function isVisible(array $arguments) { - $userId = $this->session->getUser()->getId(); - $cacheKey = self::$cachePrefix . $userId; - $value = $this->cacheStorage->load($cacheKey); - if ($value === false) { - $value = version_compare( - $this->viewerLogger->get($userId)->getLastViewVersion(), - $this->productMetadata->getVersion(), - '<' - ); - $this->cacheStorage->save(false, $cacheKey); - } - return (bool)$value; +// $userId = $this->session->getUser()->getId(); +// $cacheKey = self::$cachePrefix . $userId; +// $value = $this->cacheStorage->load($cacheKey); +// if ($value === false) { +// $value = version_compare( +// $this->viewerLogger->get($userId)->getLastViewVersion(), +// $this->productMetadata->getVersion(), +// '<' +// ); +// $this->cacheStorage->save(false, $cacheKey); +// } + return true; } /** diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php new file mode 100644 index 0000000000000..930cf89966936 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Model\ContentProvider\Http; + +use Magento\AdminAnalytics\Model\ContentProviderInterface; +use Magento\Setup\Module\I18n\Locale; +use Psr\Log\LoggerInterface; +use Magento\Framework\HTTP\ClientInterface; + +/** + * Requests the release notification content data via an HTTP call to a REST API + */ +class HttpContentProvider implements ContentProviderInterface +{ + /** + * @var ClientInterface + */ + private $httpClient; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var UrlBuilder + */ + private $urlBuilder; + + /** + * HttpContentProvider constructor. + * @param ClientInterface $httpClient + * @param UrlBuilder $urlBuilder + * @param LoggerInterface $logger + */ + public function __construct( + ClientInterface $httpClient, + UrlBuilder $urlBuilder, + LoggerInterface $logger + ) { + $this->httpClient = $httpClient; + $this->urlBuilder = $urlBuilder; + $this->logger = $logger; + } + + /** + * @inheritdoc + */ + public function getContent($version, $edition, $locale) + { + $result = false; + + try { + $result = $this->retrieveContent($version, $edition, $locale); + if (!$result) { + $result = $this->retrieveContent($version, $edition, Locale::DEFAULT_SYSTEM_LOCALE); + if (!$result) { + $result = $this->retrieveContent($version, '', 'default'); + } + } + } catch (\Exception $e) { + $this->logger->warning( + sprintf( + 'Failed to retrieve the release notification content. The response is: %s', + empty($result) ? 'Response body is empty.' : $result + ) + ); + } + + return $result; + } + + /** + * Retrieve content from given url + * + * @param string $version + * @param string $edition + * @param string $locale + * @return bool|string + */ + private function retrieveContent($version, $edition, $locale) + { + $url = $this->urlBuilder->getUrl($version, $edition, $locale); + return empty($url) ? false : $this->getResponse($url); + } + + /** + * Returns the response body from the HTTP client + * + * @param $url + * @return string + */ + private function getResponse($url) + { + $this->httpClient->get($url); + $responseBody = $this->httpClient->getBody(); + + if ($this->httpClient->getStatus() === 200 && !empty($responseBody)) { + return $responseBody; + } + + return false; + } +} diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php new file mode 100644 index 0000000000000..3898fb82f4ac5 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Model\ContentProvider\Http; + +use Magento\Framework\App\Config\ScopeConfigInterface; + +/** + * Builder to build Url to retrieve the notification content. + */ +class UrlBuilder +{ + /** + * Path to the configuration value which contains an URL that provides the release notification data. + * + * @var string + */ + private static $notificationContentUrlConfigPath = 'system/release_notification/content_url'; + + /** + * Path to the configuration value indicates if use https in notification content request. + * + * @var string + */ + private static $useHttpsFlagConfigPath = 'system/release_notification/use_https'; + + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @param ScopeConfigInterface $config + */ + public function __construct(ScopeConfigInterface $config) + { + $this->config = $config; + } + + /** + * Builds the URL to request the release notification content data based on passed parameters. + * + * @param string $version + * @param string $edition + * @param string $locale + * @return string + */ + public function getUrl($version, $edition, $locale) + { + $scheme = $this->config->isSetFlag(self::$useHttpsFlagConfigPath) ? 'https://' : 'http://'; + $baseUrl = $this->config->getValue(self::$notificationContentUrlConfigPath); + if (empty($baseUrl)) { + return ''; + } else { + $url = $scheme . $baseUrl; + $url .= empty($version) ? '' : '/' . $version; + $url .= empty($edition) ? '' : '/' . $edition; + $url .= empty($locale) ? '' : '/' . $locale; + return $url . '.json'; + } + } +} diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index ac5d7b1f94db4..5b67d4dfffec1 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -7,8 +7,8 @@ namespace Magento\AdminAnalytics\Model\ResourceModel\Viewer; -use Magento\ReleaseNotification\Model\Viewer\Log; -use Magento\ReleaseNotification\Model\Viewer\LogFactory; +use Magento\AdminAnalytics\Model\Viewer\Log; +use Magento\AdminAnalytics\Model\Viewer\LogFactory; use Magento\Framework\App\ResourceConnection; /** diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php new file mode 100644 index 0000000000000..f93cb1e00f019 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php @@ -0,0 +1,246 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Ui\DataProvider\Modifier; + +use Magento\AdminAnalytics\Model\ContentProviderInterface; +use Magento\AdminAnalytics\Ui\Renderer\NotificationRenderer; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\App\CacheInterface; +use Magento\Ui\Component; +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Backend\Model\Auth\Session; +use Psr\Log\LoggerInterface; + +/** + * Modifies the metadata returning to the Release Notification data provider + */ +class Notifications implements ModifierInterface +{ + /** + * @var ContentProviderInterface + */ + private $contentProvider; + + /** + * @var NotificationRenderer + */ + private $renderer; + + /** + * Prefix for cache + * + * @var string + */ + private static $cachePrefix = 'release-notification-content-'; + + /** + * @var CacheInterface + */ + private $cacheStorage; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var ProductMetadataInterface + */ + private $productMetadata; + + /** + * @var Session + */ + private $session; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param ContentProviderInterface $contentProvider + * @param NotificationRenderer $render + * @param CacheInterface $cacheStorage + * @param SerializerInterface $serializer + * @param ProductMetadataInterface $productMetadata + * @param Session $session + * @param LoggerInterface $logger + */ + public function __construct( + ContentProviderInterface $contentProvider, + NotificationRenderer $render, + CacheInterface $cacheStorage, + SerializerInterface $serializer, + ProductMetadataInterface $productMetadata, + Session $session, + LoggerInterface $logger + ) { + $this->contentProvider = $contentProvider; + $this->renderer = $render; + $this->cacheStorage = $cacheStorage; + $this->serializer = $serializer; + $this->productMetadata = $productMetadata; + $this->session = $session; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function modifyData(array $data) + { + return $data; + } + + /** + * {@inheritdoc} + */ + public function modifyMeta(array $meta) + { + $modalContent = $this->getNotificationContent(); + + if ($modalContent) { + $pages = $modalContent['pages']; + $pageCount = count($pages); + $counter = 1; + + foreach ($pages as $page) { + $meta = $this->buildNotificationMeta($meta, $page, $counter++ == $pageCount); + } + } else { + $meta = $this->hideNotification($meta); + } + + return $meta; + } + + /** + * Builds the notification modal by modifying $meta for the ui component + * + * @param array $meta + * @param array $page + * @param bool $isLastPage + * @return array + */ + private function buildNotificationMeta(array $meta, array $page, $isLastPage) + { + $meta['notification_modal_' . $page['name']]['arguments']['data']['config'] = [ + 'isTemplate' => false, + 'componentType' => Component\Modal::NAME + ]; + + $meta['notification_modal_' . $page['name']]['children']['notification_fieldset']['children'] + ['notification_text']['arguments']['data']['config'] = [ + 'text' => $this->renderer->getNotificationContent($page) + ]; + + if ($isLastPage) { + $meta['notification_modal_' . $page['name']]['arguments']['data']['config']['options'] = [ + 'title' => $this->renderer->getNotificationTitle($page), + 'buttons' => [ + [ + 'text' => 'Done', + 'actions' => [ + [ + 'targetName' => '${ $.name }', + 'actionName' => 'closeReleaseNotes' + ] + ], + 'class' => 'release-notification-button-next' + ] + ], + ]; + + $meta['notification_modal_' . $page['name']]['children']['notification_fieldset']['children'] + ['notification_buttons']['children']['notification_button_next']['arguments']['data']['config'] = [ + 'buttonClasses' => 'hide-release-notification' + ]; + } else { + $meta['notification_modal_' . $page['name']]['arguments']['data']['config']['options'] = [ + 'title' => $this->renderer->getNotificationTitle($page) + ]; + } + + return $meta; + } + + /** + * Sets the modal to not display if no content is available. + * + * @param array $meta + * @return array + */ + private function hideNotification(array $meta) + { + $meta['notification_modal_1']['arguments']['data']['config']['options'] = [ + 'autoOpen' => false + ]; + + return $meta; + } + + /** + * Returns the notification modal content data + * + * @returns array|false + */ + private function getNotificationContent() + { + $version = strtolower($this->getTargetVersion()); + $edition = strtolower($this->productMetadata->getEdition()); + $locale = strtolower($this->session->getUser()->getInterfaceLocale()); + + $cacheKey = self::$cachePrefix . $version . "-" . $edition . "-" . $locale; + $modalContent = $this->cacheStorage->load($cacheKey); + if ($modalContent === false) { + $modalContent = $this->contentProvider->getContent($version, $edition, $locale); + $this->cacheStorage->save($modalContent, $cacheKey); + } + + return !$modalContent ? $modalContent : $this->unserializeContent($modalContent); + } + + /** + * Unserializes the notification modal content to be used for rendering + * + * @param string $modalContent + * @return array|false + */ + private function unserializeContent($modalContent) + { + $result = false; + + try { + $result = $this->serializer->unserialize($modalContent); + } catch (\InvalidArgumentException $e) { + $this->logger->warning( + sprintf( + 'Failed to unserialize the release notification content. The error is: %s', + $e->getMessage() + ) + ); + } + + return $result; + } + + /** + * Returns the current Magento version used to retrieve the release notification content. + * Version information after the dash (-) character is removed (ex. -dev or -rc). + * + * @return string + */ + private function getTargetVersion() + { + $metadataVersion = $this->productMetadata->getVersion(); + $version = strstr($metadataVersion, '-', true); + + return !$version ? $metadataVersion : $version; + } +} diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php new file mode 100644 index 0000000000000..104d902d3dc94 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Ui\DataProvider; + +use Magento\Framework\Api\Search\SearchCriteriaInterface; +use Magento\Framework\Api\Search\SearchResultInterface; +use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; +use Magento\Ui\DataProvider\Modifier\PoolInterface; + +/** + * Data Provider for the Release Notifications UI component. + */ +class NotificationDataProvider implements DataProviderInterface +{ + /** + * @var PoolInterface + */ + private $pool; + + /** + * Search result object. + * + * @var SearchResultInterface + */ + private $searchResult; + + /** + * Search criteria object. + * + * @var SearchCriteriaInterface + */ + private $searchCriteria; + + /** + * Own name of this provider. + * + * @var string + */ + private $name; + + /** + * Provider configuration data. + * + * @var array + */ + private $data; + + /** + * Provider configuration meta. + * + * @var array + */ + private $meta; + + /** + * @param string $name + * @param SearchResultInterface $searchResult + * @param SearchCriteriaInterface $searchCriteria + * @param PoolInterface $pool + * @param array $meta + * @param array $data + */ + public function __construct( + $name, + SearchResultInterface $searchResult, + SearchCriteriaInterface $searchCriteria, + PoolInterface $pool, + array $meta = [], + array $data = [] + ) { + $this->name = $name; + $this->searchResult = $searchResult; + $this->searchCriteria = $searchCriteria; + $this->pool = $pool; + $this->meta = $meta; + $this->data = $data; + } + + /** + * @inheritdoc + */ + public function getData() + { + /** @var ModifierInterface $modifier */ + foreach ($this->pool->getModifiersInstances() as $modifier) { + $this->data = $modifier->modifyData($this->data); + } + + return $this->data; + } + + /** + * @inheritdoc + */ + public function getMeta() + { + /** @var ModifierInterface $modifier */ + foreach ($this->pool->getModifiersInstances() as $modifier) { + $this->meta = $modifier->modifyMeta($this->meta); + } + return $this->meta; + } + + /** + * @inheritdoc + */ + public function getName() + { + return $this->name; + } + + /** + * @inheritdoc + */ + public function getConfigData() + { + return $this->data['config'] ?? []; + } + + /** + * @inheritdoc + */ + public function setConfigData($config) + { + $this->data['config'] = $config; + + return true; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldMetaInfo($fieldSetName, $fieldName) + { + return []; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldSetMetaInfo($fieldSetName) + { + return []; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getFieldsMetaInfo($fieldSetName) + { + return []; + } + + /** + * @inheritdoc + */ + public function getPrimaryFieldName() + { + return 'release_notification'; + } + + /** + * @inheritdoc + */ + public function getRequestFieldName() + { + return 'release_notification'; + } + + /** + * @inheritdoc + */ + public function addFilter(\Magento\Framework\Api\Filter $filter) + { + } + + /** + * @inheritdoc + */ + public function addOrder($field, $direction) + { + } + + /** + * @inheritdoc + */ + public function setLimit($offset, $size) + { + } + + /** + * @inheritdoc + */ + public function getSearchCriteria() + { + return $this->searchCriteria; + } + + /** + * @inheritdoc + */ + public function getSearchResult() + { + return $this->searchResult; + } +} diff --git a/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php b/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php new file mode 100644 index 0000000000000..b66e213670f76 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php @@ -0,0 +1,208 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\Ui\Renderer; + +use Magento\Framework\Escaper; + +/** + * Builds the HTML for the release notification modals + */ +class NotificationRenderer +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @param Escaper $escaper + */ + public function __construct( + Escaper $escaper + ) { + $this->escaper = $escaper; + } + + /** + * Returns the HTML for notification's title to the ui component + * + * @param array $page + * @return string + */ + public function getNotificationTitle(array $page) + { + $title = $this->escaper->escapeHtml($page['mainContent']['title']); + $imageUrl = $this->escaper->escapeUrl($page['mainContent']['imageUrl']); + $content = ""; + + if (!empty($imageUrl)) { + $content .= "<div class='release-notification-title-with-image' style='background-image: url(\"" . $imageUrl + . "\")'>"; + $content .= $title; + $content .= "</div>"; + } else { + $content = $title; + } + + return $content; + } + + /** + * Returns the HTML for the content in the notification ui component + * + * @param array $page + * @return string + */ + public function getNotificationContent(array $page) + { + $content = $this->buildMainContent($page['mainContent']); + $content .= $this->buildSubHeadings($page['subHeading']); + $content .= $this->buildFooter($page['footer']); + + return $content; + } + + /** + * Builds the HTML for the main content in the notification ui component + * + * @param array $mainContent + * @return string + */ + private function buildMainContent(array $mainContent) + { + $content = $this->buildContentTextAreas($mainContent['content']); + $content .= $this->buildLists($mainContent['lists']); + + return $this->formatContentWithLinks($content); + } + + /** + * Builds the HTML for the main text areas in the notification ui component + * + * @param array $contentAreas + * @return string + */ + private function buildContentTextAreas(array $contentAreas) + { + $content = ""; + $lastContentArea = end($contentAreas); + + foreach ($contentAreas as $contentArea) { + $content .= "<p>"; + $content .= $this->escaper->escapeHtml($contentArea['text']); + $content .= "</p>"; + if ($contentArea != $lastContentArea) { + $content .= "<br />"; + } + } + + return $content; + } + + /** + * Builds the HTML for the bullet list content in the notification ui component + * + * @param array $lists + * @return string + */ + private function buildLists(array $lists) + { + $content = "<ul>"; + + foreach ($lists as $listItem) { + $content .= "<li><span>"; + $content .= $this->escaper->escapeHtml($listItem['text']); + $content .= "</span></li>"; + } + + $content .= "</ul>"; + + return $content; + } + + /** + * Builds the HTML for the highlighted sub heads for the overview page in the notification ui component + * + * @param array $subHeadings + * @return string + */ + private function buildSubHeadings(array $subHeadings) + { + $content = ""; + + foreach ($subHeadings as $subHeading) { + if (!empty($subHeading['imageUrl'])) { + $content .= "<div class='highlight-item' style='background-image: url(\"" + . $this->escaper->escapeUrl($subHeading['imageUrl']) . "\")'>"; + } else { + $content .= "<div class='highlight-item-no-image'>"; + } + + $content .= "<h3>"; + $content .= $this->escaper->escapeHtml($subHeading['title']); + $content .= "</h3>"; + $content .= "<p>"; + $content .= $this->formatContentWithLinks($subHeading['content']); + $content .= "</p>"; + $content .= "</div>"; + } + + return $content; + } + + /** + * Builds the HTML for the footer content in the notification ui component + * + * @param array $footer + * @return string + */ + private function buildFooter(array $footer) + { + $content = "<p>"; + $content .= $this->escaper->escapeHtml($footer['content']); + $content .= "</p>"; + + return $this->formatContentWithLinks($content); + } + + /** + * Searches a given string for a URL, formats it to an HTML anchor tag, and returns the original string in the + * correct HTML format. + * + * @param string $content + * @return string + */ + private function formatContentWithLinks($content) + { + $urlRegex = '#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#'; + $urlTextRegex = '/\[(.*?)\]/'; + + preg_match_all($urlRegex, $content, $urlMatches); + preg_match_all($urlTextRegex, $content, $urlTextMatches); + + foreach ($urlMatches[0] as $key => $urlMatch) { + if (!empty($urlTextMatches[0])) { + $linkMatch = $urlMatch . " " . $urlTextMatches[0][$key]; + $content = str_replace( + $linkMatch, + "<a target='_blank' href='{$this->escaper->escapeUrl($urlMatch)}'> + {$this->escaper->escapeHtml($urlTextMatches[1][$key])}</a>", + $content + ); + } else { + $content = str_replace( + $urlMatch, + "<a target='_blank' href='{$this->escaper->escapeUrl($urlMatch)}'> + {$this->escaper->escapeUrl($urlMatch)}</a>", + $content + ); + } + } + + return $content; + } +} diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 86de70f75f75d..5339fbc894a41 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -14,8 +14,13 @@ </arguments> </block> - <block name="confirm_popup_block" template="Magento_AdminAnalytics::confirm_popup.phtml"/> + </referenceContainer> + <referenceContainer name="content"> + <uiComponent name="admin_usage_notification"> + <visibilityCondition name="can_view_admin_usage_notification" className="Magento\AdminAnalytics\Model\Condition\CanViewNotification"/> + </uiComponent> + </referenceContainer> </body> </page> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml index 59b972fd5b536..ddd9b7acf03dd 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml @@ -38,13 +38,9 @@ text: $.mage.__(`Don't Allow`), class: 'action', click: function(){ - console.log(`I clicked Don't Allow`); - - var data = { 'form_key': window.FORM_KEY }; - console.log(data.logAction) $.ajax({ type: 'POST', url: '/magento2ce/admin_michell/adminAnalytics/config/disableAdminUsage', @@ -62,7 +58,6 @@ text: $.mage.__('Ok'), class: 'action', click: function(){ - console.log("I clicked Ok"); var data = { 'form_key': window.FORM_KEY }; diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml new file mode 100644 index 0000000000000..6f9cca8696019 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="provider" xsi:type="string">admin_usage_notification.admin_usage_notification_data_source</item> + </item> + <item name="label" xsi:type="string" translate="true">Release Notification</item> + <item name="template" xsi:type="string">templates/form/collapsible</item> + </argument> + <settings> + <namespace>admin_usage_notification</namespace> + <dataScope>data</dataScope> + <deps> + <dep>admin_usage_notification.admin_usage_notification_data_source</dep> + </deps> + </settings> + <dataSource name="admin_usage_notification_data_source"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> + </item> + </argument> + <dataProvider class="Magento\AdminAnalytics\Ui\DataProvider\NotificationDataProvider" name="admin_usage_notification_data_source"> + <settings> + <requestFieldName>id</requestFieldName> + <primaryFieldName>entity_id</primaryFieldName> + </settings> + </dataProvider> + </dataSource> + <modal name="notification_modal_1" component="Magento_AdminAnalytics/js/modal/component"> + <settings> + <state>true</state> + <options> + <option name="modalClass" xsi:type="string">admin-usage-notification</option> + <option name="title" xsi:type="string" translate="true">Allow admin usage data collection</option> + <option name="autoOpen" xsi:type="boolean">true</option> + <option name="type" xsi:type="string">popup</option> + <option name="clickableOverlay" xsi:type="boolean">false</option> + <option name="responsive" xsi:type="boolean">true</option> + <option name="innerScroll" xsi:type="boolean">false</option> + + </options> + </settings> + <fieldset name="notification_fieldset"> + <settings> + <label/> + </settings> + <container name="notification_text" template="ui/form/components/complex"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="label" xsi:type="string"/> + <item name="additionalClasses" xsi:type="string">release-notification-text</item> + <item name="text" xsi:type="string" translate="true"><![CDATA[ + <p>Help us improve Magento Admin by allowing us to collect usage data.</p> + <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> + <p>You can learn more and opt out at any time by following the instructions in <a href="https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html" target="_blank">merchant documentation</a></p> +]]></item> + </item> + </argument> + </container> + <container name="notification_buttons"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="onCancel" xsi:type="string">actionCancel</item> + <item name="options" xsi:type="array"> + <item name="buttons" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="text" xsi:type="string">Don't Allow</item> + <item name="class" xsi:type="string">action-secondary</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="string">actionCancel</item> + </item> + </item> + <item name="2" xsi:type="array"> + <item name="text" xsi:type="string">Allow</item> + <item name="class" xsi:type="string">action-primary</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="string">actionDone</item> + </item> + </item> + </item> + </item> + </item> + </argument> + + <button name="dont_allow_modal_button"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="title" xsi:type="string">Don't Allow</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="targetName" xsi:type="string">${ $.parentName}.notification_modal_1</item> + <item name="actionName" xsi:type="string">dontAllow</item> + </item> + </item> + </item> + </argument> + </button> + <button name="allow_modal_button"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="title" xsi:type="string">Allow</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="targetName" xsi:type="string">${ $.parentName}.notification_modal_1</item> + <item name="actionName" xsi:type="string">actionDone</item> + </item> + </item> + </item> + </argument> + </button> + </container> + </fieldset> + </modal> + +</form> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js new file mode 100644 index 0000000000000..db203f6fe8007 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -0,0 +1,90 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define( + [ + 'jquery', + 'Magento_Ui/js/modal/modal-component' + ], + function ($, Modal) { + 'use strict'; + + console.log('Hello two'); + + return Modal.extend( + { + defaults: { + imports: { + logAction: '${ $.provider }:data.logAction' + } + }, + keyEventHandlers: { + escapeKey: function (){} + }, + opened: function ($Event) { + $('.modal-header button.action-close', $Event.srcElement).hide(); + }, + + dontAllow: function(){ + console.log("Clicked") + }, + actionDone: function() { + console.log("Clicked") + }, + buttons: [ + { + text: $.mage.__(`Don't Allow`), + class: 'action', + click: function () { + var data = { + 'form_key': window.FORM_KEY + }; + $.ajax( + { + type: 'POST', + url: '/magento2ce/admin_michell/adminAnalytics/config/disableAdminUsage', + data: data, + showLoader: true + } + ).done( + function (xhr) { + if (xhr.error) { + self.onError(xhr); + } + } + ).fail(this.onError); + this.closeModal(); + }, + }, + { + text: $.mage.__('Ok'), + class: 'action', + click: function () { + var data = { + 'form_key': window.FORM_KEY + }; + $.ajax( + { + type: 'POST', + url: '/magento2ce/admin_michell/adminAnalytics/config/enableAdminUsage', + data: data, + showLoader: true + } + ).done( + function (xhr) { + if (xhr.error) { + self.onError(xhr); + } + } + ).fail(this.onError); + + this.closeModal(); + }, + } + ], + } + ); + } +); From d63c892696be93bd9ab024518a66900c6186fe15 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 26 Jul 2019 09:47:01 -0500 Subject: [PATCH 0258/2437] MC-15978: Catalog Product Attribute Set Name --- .../DataProvider/Product/Form/Modifier/AttributeSet.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php index 0733d21bf47d7..3420965597c5e 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php @@ -78,7 +78,13 @@ public function getOptions() \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::SORT_ORDER_ASC ); - return $collection->getData(); + $collectionData = $collection->getData(); + + array_walk($collectionData, function (&$attribute) { + $attribute['__disableTmpl'] = true; + }); + + return $collectionData; } /** From 90fe08e2d0710f5e48b5c9b2b8f6c63d7e917396 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 26 Jul 2019 11:06:19 -0500 Subject: [PATCH 0259/2437] MC-17700: Downloadable Product links --- .../Model/Import/Product/Type/DownloadableTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php index a3230d61e262c..15dd157a3154b 100644 --- a/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php +++ b/dev/tests/integration/testsuite/Magento/DownloadableImportExport/Model/Import/Product/Type/DownloadableTest.php @@ -68,7 +68,7 @@ protected function setUp() $this->productMetadata = $metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); $this->domainManager = $this->objectManager->get(DomainManagerInterface::class); - $this->domainManager->addDomains(['www.google.com', 'www.yahoo.com']); + $this->domainManager->addDomains(['www.bing.com', 'www.google.com', 'www.yahoo.com']); } /** @@ -76,7 +76,7 @@ protected function setUp() */ protected function tearDown() { - $this->domainManager->removeDomains(['www.google.com', 'www.yahoo.com']); + $this->domainManager->removeDomains(['www.bing.com', 'www.google.com', 'www.yahoo.com']); } /** From c4ee9853403f287be94b26ffea1a2bb75fb8a671 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 26 Jul 2019 11:09:25 -0500 Subject: [PATCH 0260/2437] MC-17700: Downloadable Product links --- .../testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php index 59ad82334a958..3b7273e088105 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php @@ -736,7 +736,7 @@ public function testValidateUploadFileExceptionDataProvider() ], 'image_empty' => [ 'fileName' => 'empty.png', - 'expectedErrorMsg' => 'Disallowed file type.', + 'expectedErrorMsg' => 'Wrong file size.', 'useFixture' => true ], 'notanimage' => [ From 1c8cb1e0d3f4fa00749e98a767a3467f5d0a9498 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Fri, 26 Jul 2019 12:55:09 -0500 Subject: [PATCH 0261/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Added dialog box, opt out feature --- .../Model/Condition/CanViewNotification.php | 26 +++-- .../ui_component/admin_usage_notification.xml | 85 +++++--------- .../view/adminhtml/web/js/modal/component.js | 104 ++++++++---------- 3 files changed, 86 insertions(+), 129 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index b33677ea05f26..f6dd6ecb9ff3c 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -78,18 +78,20 @@ public function __construct( */ public function isVisible(array $arguments) { -// $userId = $this->session->getUser()->getId(); -// $cacheKey = self::$cachePrefix . $userId; -// $value = $this->cacheStorage->load($cacheKey); -// if ($value === false) { -// $value = version_compare( -// $this->viewerLogger->get($userId)->getLastViewVersion(), -// $this->productMetadata->getVersion(), -// '<' -// ); -// $this->cacheStorage->save(false, $cacheKey); -// } - return true; + $userId = $this->session->getUser()->getId(); + $cacheKey = self::$cachePrefix . $userId; + $value = $this->cacheStorage->load($cacheKey); + if ($value === false) { + $value = version_compare( + $this->viewerLogger->get($userId)->getLastViewVersion(), + $this->productMetadata->getVersion(), + '<' + ); + $this->cacheStorage->save(false, $cacheKey); + } + + return (bool)$value; + } /** diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 6f9cca8696019..f9d11d2a6a7fe 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -10,7 +10,7 @@ <item name="js_config" xsi:type="array"> <item name="provider" xsi:type="string">admin_usage_notification.admin_usage_notification_data_source</item> </item> - <item name="label" xsi:type="string" translate="true">Release Notification</item> + <item name="label" xsi:type="string" translate="true">Admin Usage Notification</item> <item name="template" xsi:type="string">templates/form/collapsible</item> </argument> <settings> @@ -21,6 +21,17 @@ </deps> </settings> <dataSource name="admin_usage_notification_data_source"> + <argument name="dataProvider" xsi:type="configurableObject"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="data" xsi:type="array"> + <item name="enableLogAction" xsi:type="url" path="adminAnalytics/config/enableAdminUsage"/> + <item name="disableLogAction" xsi:type="url" path="adminAnalytics/config/disableAdminUsage"/> + + </item> + </item> + </argument> + </argument> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> @@ -39,12 +50,27 @@ <options> <option name="modalClass" xsi:type="string">admin-usage-notification</option> <option name="title" xsi:type="string" translate="true">Allow admin usage data collection</option> - <option name="autoOpen" xsi:type="boolean">true</option> + <option name="autoOpen" xsi:type="array">true</option> <option name="type" xsi:type="string">popup</option> <option name="clickableOverlay" xsi:type="boolean">false</option> <option name="responsive" xsi:type="boolean">true</option> <option name="innerScroll" xsi:type="boolean">false</option> - + <option name="buttons" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="text" xsi:type="string" translate="true">Don't Allow</item> + <item name="class" xsi:type="string">action-secondary</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="string">disableAdminUsage</item> + </item> + </item> + <item name="1" xsi:type="array"> + <item name="text" xsi:type="string" translate="true">Allow</item> + <item name="class" xsi:type="string">action-primary</item> + <item name="actions" xsi:type="array"> + <item name="0" xsi:type="string">enableAdminUsage</item> + </item> + </item> + </option> </options> </settings> <fieldset name="notification_fieldset"> @@ -64,59 +90,6 @@ </item> </argument> </container> - <container name="notification_buttons"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="onCancel" xsi:type="string">actionCancel</item> - <item name="options" xsi:type="array"> - <item name="buttons" xsi:type="array"> - <item name="0" xsi:type="array"> - <item name="text" xsi:type="string">Don't Allow</item> - <item name="class" xsi:type="string">action-secondary</item> - <item name="actions" xsi:type="array"> - <item name="0" xsi:type="string">actionCancel</item> - </item> - </item> - <item name="2" xsi:type="array"> - <item name="text" xsi:type="string">Allow</item> - <item name="class" xsi:type="string">action-primary</item> - <item name="actions" xsi:type="array"> - <item name="0" xsi:type="string">actionDone</item> - </item> - </item> - </item> - </item> - </item> - </argument> - - <button name="dont_allow_modal_button"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="title" xsi:type="string">Don't Allow</item> - <item name="actions" xsi:type="array"> - <item name="0" xsi:type="array"> - <item name="targetName" xsi:type="string">${ $.parentName}.notification_modal_1</item> - <item name="actionName" xsi:type="string">dontAllow</item> - </item> - </item> - </item> - </argument> - </button> - <button name="allow_modal_button"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="title" xsi:type="string">Allow</item> - <item name="actions" xsi:type="array"> - <item name="0" xsi:type="array"> - <item name="targetName" xsi:type="string">${ $.parentName}.notification_modal_1</item> - <item name="actionName" xsi:type="string">actionDone</item> - </item> - </item> - </item> - </argument> - </button> - </container> </fieldset> </modal> - </form> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index db203f6fe8007..6c449e45f7aad 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -11,80 +11,62 @@ define( function ($, Modal) { 'use strict'; - console.log('Hello two'); - return Modal.extend( { defaults: { imports: { - logAction: '${ $.provider }:data.logAction' + enableLogAction: '${ $.provider }:data.enableLogAction', + disableLogAction: '${ $.provider }:data.disableLogAction' } }, keyEventHandlers: { - escapeKey: function (){} - }, - opened: function ($Event) { - $('.modal-header button.action-close', $Event.srcElement).hide(); - }, - - dontAllow: function(){ - console.log("Clicked") + escapeKey: function () { + } }, - actionDone: function() { - console.log("Clicked") + opened: function () { + $('.modal-header button.action-close').hide(); }, - buttons: [ - { - text: $.mage.__(`Don't Allow`), - class: 'action', - click: function () { - var data = { - 'form_key': window.FORM_KEY - }; - $.ajax( - { - type: 'POST', - url: '/magento2ce/admin_michell/adminAnalytics/config/disableAdminUsage', - data: data, - showLoader: true - } - ).done( - function (xhr) { - if (xhr.error) { - self.onError(xhr); - } + enableAdminUsage: function () { + var data = { + 'form_key': window.FORM_KEY + }; + $.ajax( + { + type: 'POST', + url: this.enableLogAction, + data: data, + showLoader: true + } + ).done( + function (xhr) { + if (xhr.error) { + self.onError(xhr); } - ).fail(this.onError); - this.closeModal(); - }, + } + ).fail(this.onError); + this.closeModal(); }, - { - text: $.mage.__('Ok'), - class: 'action', - click: function () { - var data = { - 'form_key': window.FORM_KEY - }; - $.ajax( - { - type: 'POST', - url: '/magento2ce/admin_michell/adminAnalytics/config/enableAdminUsage', - data: data, - showLoader: true + disableAdminUsage: function () { + var data = { + 'form_key': window.FORM_KEY + }; + $.ajax( + { + type: 'POST', + url: this.disableLogAction, + data: data, + showLoader: true + } + ).done( + function (xhr) { + if (xhr.error) { + self.onError(xhr); } - ).done( - function (xhr) { - if (xhr.error) { - self.onError(xhr); - } - } - ).fail(this.onError); - - this.closeModal(); - }, + } + ).fail(this.onError); + this.closeModal(); } - ], } - ); + ) } ); From d4cb1c8f71dadb784d495359c8bc524b7037ed96 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Fri, 26 Jul 2019 15:57:44 -0500 Subject: [PATCH 0262/2437] MC-15298: Allow admin to opt out of admin analytics tracking - cleaned up code --- .../Magento/AdminAnalytics/Block/Setting.php | 52 ---- .../Adminhtml/Config/DisableAdminUsage.php | 25 +- .../Adminhtml/Config/EnableAdminUsage.php | 23 +- .../Adminhtml/Config/MarkUserNotified.php | 95 ------- .../Model/Condition/CanViewNotification.php | 32 +-- .../Model/ResourceModel/Viewer/Logger.php | 31 +-- .../AdminAnalytics/Model/Viewer/Log.php | 24 +- .../DataProvider/Modifier/Notifications.php | 246 ------------------ .../DataProvider/NotificationDataProvider.php | 6 +- .../Ui/Renderer/NotificationRenderer.php | 208 --------------- .../Magento/AdminAnalytics/etc/db_schema.xml | 17 +- .../etc/db_schema_whitelist.json | 7 +- 12 files changed, 70 insertions(+), 696 deletions(-) delete mode 100644 app/code/Magento/AdminAnalytics/Block/Setting.php delete mode 100644 app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php delete mode 100644 app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php delete mode 100644 app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php diff --git a/app/code/Magento/AdminAnalytics/Block/Setting.php b/app/code/Magento/AdminAnalytics/Block/Setting.php deleted file mode 100644 index 516c854c29337..0000000000000 --- a/app/code/Magento/AdminAnalytics/Block/Setting.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\AdminAnalytics\Block; - -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; -use Magento\Config\Model\Config\Factory; - -class Setting extends Template -{ - private $configFactory; - /** - * @param Context $context - * @param Factory $configFactory - * @param array $data - */ - public function __construct( - Context $context, - Factory $configFactory, - array $data = [] - ) { - $this->configFactory = $configFactory; - parent::__construct($context, $data); - } - - /** - * Sets the admin usage's configuration setting to yes - */ - public function enableAdminUsage() - { - $configModel = $this->configFactory->create(); - $configModel->setDataByPath('admin/usage/enabled', 1); - $configModel->save(); - } - - /** - * Sets the admin usage's configuration setting to no - */ - public function disableAdminUsage() - { - $configModel = $this->configFactory->create(); - $configModel->setDataByPath('admin/usage/enabled', 0); - $configModel->save(); - } - - public function showModal() { - return false; - } -} diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index b81c60e4b3adb..eb31e54e8041b 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -7,20 +7,18 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\Controller\ResultInterface; use Psr\Log\LoggerInterface; use Magento\Config\Model\Config\Factory; /** - * Controller to record that the current admin user has seen the release notification content + * Controller to record Admin analytics usage log */ class DisableAdminUsage extends Action { - - private $configFactory; /** @@ -39,11 +37,12 @@ class DisableAdminUsage extends Action private $logger; /** - * MarkUserNotified constructor. + * DisableAdminUsage constructor. * * @param Action\Context $context * @param ProductMetadataInterface $productMetadata * @param NotificationLogger $notificationLogger + * @param Factory $configFactory * @param LoggerInterface $logger */ public function __construct( @@ -59,6 +58,10 @@ public function __construct( $this->notificationLogger = $notificationLogger; $this->logger = $logger; } + + /** + * Changes the value of config/admin/usage/enabled + */ public function disableAdminUsage() { $configModel = $this->configFactory->create(); @@ -66,13 +69,16 @@ public function disableAdminUsage() $configModel->save(); } + /** + * Log information about the last admin usage selection + * + * @return ResultInterface + */ public function markUserNotified() { $responseContent = [ 'success' => $this->notificationLogger->log( - $this->_auth->getUser()->getId(), - $this->productMetadata->getVersion(), - 0 + $this->productMetadata->getVersion() ), 'error_message' => '' ]; @@ -80,10 +86,11 @@ public function markUserNotified() $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); return $resultJson->setData($responseContent); } + /** * Log information about the last shown advertisement * - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ public function execute() { diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index d1b879ae927ab..9ce119f0c424f 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -7,15 +7,15 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\Controller\ResultInterface; use Psr\Log\LoggerInterface; use Magento\Config\Model\Config\Factory; /** - * Controller to record that the current admin user has seen the release notification content + * Controller to record that the current admin user has responded to Admin Analytics notice */ class EnableAdminUsage extends Action { @@ -42,6 +42,7 @@ class EnableAdminUsage extends Action * @param Action\Context $context * @param ProductMetadataInterface $productMetadata * @param NotificationLogger $notificationLogger + * @param Factory $configFactory * @param LoggerInterface $logger */ public function __construct( @@ -57,6 +58,10 @@ public function __construct( $this->notificationLogger = $notificationLogger; $this->logger = $logger; } + + /** + * Changes the value of config/admin/usage/enabled + */ public function enableAdminUsage() { $configModel = $this->configFactory->create(); @@ -64,13 +69,16 @@ public function enableAdminUsage() $configModel->save(); } + /** + * Log information about the last user response + * + * @return ResultInterface + */ public function markUserNotified() { $responseContent = [ 'success' => $this->notificationLogger->log( - $this->_auth->getUser()->getId(), - $this->productMetadata->getVersion(), - 1 + $this->productMetadata->getVersion() ), 'error_message' => '' ]; @@ -78,6 +86,7 @@ public function markUserNotified() $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); return $resultJson->setData($responseContent); } + /** * Log information about the last shown advertisement * @@ -88,8 +97,10 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - + /** + * IsAllow allows function to be visible + * * @return bool */ protected function _isAllowed() diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php deleted file mode 100644 index 4c252329d07a7..0000000000000 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/MarkUserNotified.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; - -use Magento\Backend\App\Action; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Controller\ResultFactory; -use Magento\ReleaseNotification\Model\ResourceModel\Viewer\Logger as NotificationLogger; -use Magento\Framework\App\ProductMetadataInterface; -use Psr\Log\LoggerInterface; - -/** - * Controller to record that the current admin user has seen the release notification content - */ -class MarkUserNotified extends Action -{ - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - - /** - * @var NotificationLogger - */ - private $notificationLogger; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * MarkUserNotified constructor. - * - * @param Action\Context $context - * @param ProductMetadataInterface $productMetadata - * @param NotificationLogger $notificationLogger - * @param LoggerInterface $logger - */ - public function __construct( - Action\Context $context, - ProductMetadataInterface $productMetadata, - NotificationLogger $notificationLogger, - LoggerInterface $logger - ) { - parent::__construct($context); - $this->productMetadata = $productMetadata; - $this->notificationLogger = $notificationLogger; - $this->logger = $logger; - } - - /** - * Log information about the last shown advertisement - * - * @return \Magento\Framework\Controller\ResultInterface - */ - public function execute() - { - try { - $responseContent = [ - 'success' => $this->notificationLogger->log( - $this->_auth->getUser()->getId(), - $this->productMetadata->getVersion() - ), - 'error_message' => '' - ]; - } catch (LocalizedException $e) { - $this->logger->error($e->getMessage()); - $responseContent = [ - 'success' => false, - 'error_message' => $e->getMessage() - ]; - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - $responseContent = [ - 'success' => false, - 'error_message' => __('It is impossible to log user action') - ]; - } - $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON); - return $resultJson->setData($responseContent); - } - - /** - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } -} diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index f6dd6ecb9ff3c..a8a6b58bffbfc 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -10,9 +10,11 @@ use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\View\Layout\Condition\VisibilityConditionInterface; use Magento\Framework\App\CacheInterface; +use function Magento\PAT\Reports\Utils\readResponseTimeReport; /** * Dynamic validator for UI release notification, manage UI component visibility. + * * Return true if the logged in user has not seen the notification. */ class CanViewNotification implements VisibilityConditionInterface @@ -36,11 +38,6 @@ class CanViewNotification implements VisibilityConditionInterface */ private $viewerLogger; - /** - * @var Session - */ - private $session; - /** * @var ProductMetadataInterface */ @@ -55,18 +52,15 @@ class CanViewNotification implements VisibilityConditionInterface * CanViewNotification constructor. * * @param Logger $viewerLogger - * @param Session $session * @param ProductMetadataInterface $productMetadata * @param CacheInterface $cacheStorage */ public function __construct( Logger $viewerLogger, - Session $session, ProductMetadataInterface $productMetadata, CacheInterface $cacheStorage ) { $this->viewerLogger = $viewerLogger; - $this->session = $session; $this->productMetadata = $productMetadata; $this->cacheStorage = $cacheStorage; } @@ -78,20 +72,18 @@ public function __construct( */ public function isVisible(array $arguments) { - $userId = $this->session->getUser()->getId(); - $cacheKey = self::$cachePrefix . $userId; + $currentProductVersion = $this->productMetadata->getVersion(); + $cacheKey = self::$cachePrefix.$currentProductVersion; $value = $this->cacheStorage->load($cacheKey); - if ($value === false) { - $value = version_compare( - $this->viewerLogger->get($userId)->getLastViewVersion(), - $this->productMetadata->getVersion(), - '<' - ); - $this->cacheStorage->save(false, $cacheKey); + if ($value != $currentProductVersion) { + $versionViewed = $this->viewerLogger->get($currentProductVersion)->getLastViewVersion(); + $versionExists = isset($versionViewed); + if ($versionExists) { + $this->cacheStorage->save($versionViewed, $cacheKey); + } + return !$versionExists; } - - return (bool)$value; - + return false; } /** diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index 5b67d4dfffec1..6c6a76365fbbe 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -12,7 +12,7 @@ use Magento\Framework\App\ResourceConnection; /** - * Release notification viewer log data logger. + * Admin Analytics log data logger. * * Saves and retrieves release notification viewer log data. */ @@ -21,7 +21,7 @@ class Logger /** * Log table name */ - const LOG_TABLE_NAME = 'admin_usage_viewer_log'; + const LOG_TABLE_NAME = 'admin_analytics_usage_version_log'; /** * @var Resource @@ -49,53 +49,48 @@ public function __construct( /** * Save (insert new or update existing) log. * - * @param int $viewerId * @param string $lastViewVersion - * @param int $isAdminUsageEnabled * @return bool */ - public function log(int $viewerId, string $lastViewVersion, int $isAdminUsageEnabled) : bool + public function log(string $lastViewVersion) : bool { /** @var \Magento\Framework\DB\Adapter\AdapterInterface $connection */ $connection = $this->resource->getConnection(ResourceConnection::DEFAULT_CONNECTION); $connection->insertOnDuplicate( $this->resource->getTableName(self::LOG_TABLE_NAME), [ - 'viewer_id' => $viewerId, - 'last_view_version' => $lastViewVersion, - 'is_admin_usage_enabled' => $isAdminUsageEnabled + 'last_viewed_in_version' => $lastViewVersion, ], [ - 'last_view_version', - 'is_admin_usage_enabled' + 'last_viewed_in_version', ] ); return true; } /** - * Get log by viewer Id. + * Get log by the last view version. * - * @param int $viewerId + * @param string $lastViewVersion * @return Log */ - public function get(int $viewerId) : Log + public function get(string $lastViewVersion) : Log { - return $this->logFactory->create(['data' => $this->loadLogData($viewerId)]); + return $this->logFactory->create(['data' => $this->loadLogData($lastViewVersion)]); } /** - * Load release notification viewer log data by viewer id + * Load release notification viewer log data by last view version * - * @param int $viewerId + * @param string $lastViewVersion * @return array */ - private function loadLogData(int $viewerId) : array + private function loadLogData(string $lastViewVersion) : array { $connection = $this->resource->getConnection(); $select = $connection->select() ->from($this->resource->getTableName(self::LOG_TABLE_NAME)) - ->where('viewer_id = ?', $viewerId); + ->where('last_viewed_in_version = ?', $lastViewVersion); $data = $connection->fetchRow($select); if (!$data) { diff --git a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php index 28621b36d9b79..3ba1d15d716d7 100644 --- a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php +++ b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php @@ -8,7 +8,7 @@ use Magento\Framework\DataObject; /** - * Release notification viewer log resource + * Admin Analytics log resource */ class Log extends DataObject { @@ -22,16 +22,6 @@ public function getId() return $this->getData('id'); } - /** - * Get viewer id - * - * @return int - */ - public function getViewerId() - { - return $this->getData('viewer_id'); - } - /** * Get last viewed product version * @@ -39,16 +29,6 @@ public function getViewerId() */ public function getLastViewVersion() { - return $this->getData('last_view_version'); - } - - /** - * Get admin usage enabled - * - * @return int - */ - public function getIsAdminUsageEnabled() - { - return $this->getData('is_admin_usage_enabled'); + return $this->getData('last_viewed_in_version'); } } diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php deleted file mode 100644 index f93cb1e00f019..0000000000000 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/Modifier/Notifications.php +++ /dev/null @@ -1,246 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\AdminAnalytics\Ui\DataProvider\Modifier; - -use Magento\AdminAnalytics\Model\ContentProviderInterface; -use Magento\AdminAnalytics\Ui\Renderer\NotificationRenderer; -use Magento\Ui\DataProvider\Modifier\ModifierInterface; -use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\App\CacheInterface; -use Magento\Ui\Component; -use Magento\Framework\App\ProductMetadataInterface; -use Magento\Backend\Model\Auth\Session; -use Psr\Log\LoggerInterface; - -/** - * Modifies the metadata returning to the Release Notification data provider - */ -class Notifications implements ModifierInterface -{ - /** - * @var ContentProviderInterface - */ - private $contentProvider; - - /** - * @var NotificationRenderer - */ - private $renderer; - - /** - * Prefix for cache - * - * @var string - */ - private static $cachePrefix = 'release-notification-content-'; - - /** - * @var CacheInterface - */ - private $cacheStorage; - - /** - * @var SerializerInterface - */ - private $serializer; - - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - - /** - * @var Session - */ - private $session; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @param ContentProviderInterface $contentProvider - * @param NotificationRenderer $render - * @param CacheInterface $cacheStorage - * @param SerializerInterface $serializer - * @param ProductMetadataInterface $productMetadata - * @param Session $session - * @param LoggerInterface $logger - */ - public function __construct( - ContentProviderInterface $contentProvider, - NotificationRenderer $render, - CacheInterface $cacheStorage, - SerializerInterface $serializer, - ProductMetadataInterface $productMetadata, - Session $session, - LoggerInterface $logger - ) { - $this->contentProvider = $contentProvider; - $this->renderer = $render; - $this->cacheStorage = $cacheStorage; - $this->serializer = $serializer; - $this->productMetadata = $productMetadata; - $this->session = $session; - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function modifyData(array $data) - { - return $data; - } - - /** - * {@inheritdoc} - */ - public function modifyMeta(array $meta) - { - $modalContent = $this->getNotificationContent(); - - if ($modalContent) { - $pages = $modalContent['pages']; - $pageCount = count($pages); - $counter = 1; - - foreach ($pages as $page) { - $meta = $this->buildNotificationMeta($meta, $page, $counter++ == $pageCount); - } - } else { - $meta = $this->hideNotification($meta); - } - - return $meta; - } - - /** - * Builds the notification modal by modifying $meta for the ui component - * - * @param array $meta - * @param array $page - * @param bool $isLastPage - * @return array - */ - private function buildNotificationMeta(array $meta, array $page, $isLastPage) - { - $meta['notification_modal_' . $page['name']]['arguments']['data']['config'] = [ - 'isTemplate' => false, - 'componentType' => Component\Modal::NAME - ]; - - $meta['notification_modal_' . $page['name']]['children']['notification_fieldset']['children'] - ['notification_text']['arguments']['data']['config'] = [ - 'text' => $this->renderer->getNotificationContent($page) - ]; - - if ($isLastPage) { - $meta['notification_modal_' . $page['name']]['arguments']['data']['config']['options'] = [ - 'title' => $this->renderer->getNotificationTitle($page), - 'buttons' => [ - [ - 'text' => 'Done', - 'actions' => [ - [ - 'targetName' => '${ $.name }', - 'actionName' => 'closeReleaseNotes' - ] - ], - 'class' => 'release-notification-button-next' - ] - ], - ]; - - $meta['notification_modal_' . $page['name']]['children']['notification_fieldset']['children'] - ['notification_buttons']['children']['notification_button_next']['arguments']['data']['config'] = [ - 'buttonClasses' => 'hide-release-notification' - ]; - } else { - $meta['notification_modal_' . $page['name']]['arguments']['data']['config']['options'] = [ - 'title' => $this->renderer->getNotificationTitle($page) - ]; - } - - return $meta; - } - - /** - * Sets the modal to not display if no content is available. - * - * @param array $meta - * @return array - */ - private function hideNotification(array $meta) - { - $meta['notification_modal_1']['arguments']['data']['config']['options'] = [ - 'autoOpen' => false - ]; - - return $meta; - } - - /** - * Returns the notification modal content data - * - * @returns array|false - */ - private function getNotificationContent() - { - $version = strtolower($this->getTargetVersion()); - $edition = strtolower($this->productMetadata->getEdition()); - $locale = strtolower($this->session->getUser()->getInterfaceLocale()); - - $cacheKey = self::$cachePrefix . $version . "-" . $edition . "-" . $locale; - $modalContent = $this->cacheStorage->load($cacheKey); - if ($modalContent === false) { - $modalContent = $this->contentProvider->getContent($version, $edition, $locale); - $this->cacheStorage->save($modalContent, $cacheKey); - } - - return !$modalContent ? $modalContent : $this->unserializeContent($modalContent); - } - - /** - * Unserializes the notification modal content to be used for rendering - * - * @param string $modalContent - * @return array|false - */ - private function unserializeContent($modalContent) - { - $result = false; - - try { - $result = $this->serializer->unserialize($modalContent); - } catch (\InvalidArgumentException $e) { - $this->logger->warning( - sprintf( - 'Failed to unserialize the release notification content. The error is: %s', - $e->getMessage() - ) - ); - } - - return $result; - } - - /** - * Returns the current Magento version used to retrieve the release notification content. - * Version information after the dash (-) character is removed (ex. -dev or -rc). - * - * @return string - */ - private function getTargetVersion() - { - $metadataVersion = $this->productMetadata->getVersion(); - $version = strstr($metadataVersion, '-', true); - - return !$version ? $metadataVersion : $version; - } -} diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php index 104d902d3dc94..a3acf279c7a9a 100644 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php @@ -13,7 +13,7 @@ use Magento\Ui\DataProvider\Modifier\PoolInterface; /** - * Data Provider for the Release Notifications UI component. + * Data Provider for the Admin usage UI component. */ class NotificationDataProvider implements DataProviderInterface { @@ -164,7 +164,7 @@ public function getFieldsMetaInfo($fieldSetName) */ public function getPrimaryFieldName() { - return 'release_notification'; + return 'admin_analytics'; } /** @@ -172,7 +172,7 @@ public function getPrimaryFieldName() */ public function getRequestFieldName() { - return 'release_notification'; + return 'admin_analytics'; } /** diff --git a/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php b/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php deleted file mode 100644 index b66e213670f76..0000000000000 --- a/app/code/Magento/AdminAnalytics/Ui/Renderer/NotificationRenderer.php +++ /dev/null @@ -1,208 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\AdminAnalytics\Ui\Renderer; - -use Magento\Framework\Escaper; - -/** - * Builds the HTML for the release notification modals - */ -class NotificationRenderer -{ - /** - * @var Escaper - */ - private $escaper; - - /** - * @param Escaper $escaper - */ - public function __construct( - Escaper $escaper - ) { - $this->escaper = $escaper; - } - - /** - * Returns the HTML for notification's title to the ui component - * - * @param array $page - * @return string - */ - public function getNotificationTitle(array $page) - { - $title = $this->escaper->escapeHtml($page['mainContent']['title']); - $imageUrl = $this->escaper->escapeUrl($page['mainContent']['imageUrl']); - $content = ""; - - if (!empty($imageUrl)) { - $content .= "<div class='release-notification-title-with-image' style='background-image: url(\"" . $imageUrl - . "\")'>"; - $content .= $title; - $content .= "</div>"; - } else { - $content = $title; - } - - return $content; - } - - /** - * Returns the HTML for the content in the notification ui component - * - * @param array $page - * @return string - */ - public function getNotificationContent(array $page) - { - $content = $this->buildMainContent($page['mainContent']); - $content .= $this->buildSubHeadings($page['subHeading']); - $content .= $this->buildFooter($page['footer']); - - return $content; - } - - /** - * Builds the HTML for the main content in the notification ui component - * - * @param array $mainContent - * @return string - */ - private function buildMainContent(array $mainContent) - { - $content = $this->buildContentTextAreas($mainContent['content']); - $content .= $this->buildLists($mainContent['lists']); - - return $this->formatContentWithLinks($content); - } - - /** - * Builds the HTML for the main text areas in the notification ui component - * - * @param array $contentAreas - * @return string - */ - private function buildContentTextAreas(array $contentAreas) - { - $content = ""; - $lastContentArea = end($contentAreas); - - foreach ($contentAreas as $contentArea) { - $content .= "<p>"; - $content .= $this->escaper->escapeHtml($contentArea['text']); - $content .= "</p>"; - if ($contentArea != $lastContentArea) { - $content .= "<br />"; - } - } - - return $content; - } - - /** - * Builds the HTML for the bullet list content in the notification ui component - * - * @param array $lists - * @return string - */ - private function buildLists(array $lists) - { - $content = "<ul>"; - - foreach ($lists as $listItem) { - $content .= "<li><span>"; - $content .= $this->escaper->escapeHtml($listItem['text']); - $content .= "</span></li>"; - } - - $content .= "</ul>"; - - return $content; - } - - /** - * Builds the HTML for the highlighted sub heads for the overview page in the notification ui component - * - * @param array $subHeadings - * @return string - */ - private function buildSubHeadings(array $subHeadings) - { - $content = ""; - - foreach ($subHeadings as $subHeading) { - if (!empty($subHeading['imageUrl'])) { - $content .= "<div class='highlight-item' style='background-image: url(\"" - . $this->escaper->escapeUrl($subHeading['imageUrl']) . "\")'>"; - } else { - $content .= "<div class='highlight-item-no-image'>"; - } - - $content .= "<h3>"; - $content .= $this->escaper->escapeHtml($subHeading['title']); - $content .= "</h3>"; - $content .= "<p>"; - $content .= $this->formatContentWithLinks($subHeading['content']); - $content .= "</p>"; - $content .= "</div>"; - } - - return $content; - } - - /** - * Builds the HTML for the footer content in the notification ui component - * - * @param array $footer - * @return string - */ - private function buildFooter(array $footer) - { - $content = "<p>"; - $content .= $this->escaper->escapeHtml($footer['content']); - $content .= "</p>"; - - return $this->formatContentWithLinks($content); - } - - /** - * Searches a given string for a URL, formats it to an HTML anchor tag, and returns the original string in the - * correct HTML format. - * - * @param string $content - * @return string - */ - private function formatContentWithLinks($content) - { - $urlRegex = '#\bhttps?://[^,\s()<>]+(?:\([\w\d]+\)|([^,[:punct:]\s]|/))#'; - $urlTextRegex = '/\[(.*?)\]/'; - - preg_match_all($urlRegex, $content, $urlMatches); - preg_match_all($urlTextRegex, $content, $urlTextMatches); - - foreach ($urlMatches[0] as $key => $urlMatch) { - if (!empty($urlTextMatches[0])) { - $linkMatch = $urlMatch . " " . $urlTextMatches[0][$key]; - $content = str_replace( - $linkMatch, - "<a target='_blank' href='{$this->escaper->escapeUrl($urlMatch)}'> - {$this->escaper->escapeHtml($urlTextMatches[1][$key])}</a>", - $content - ); - } else { - $content = str_replace( - $urlMatch, - "<a target='_blank' href='{$this->escaper->escapeUrl($urlMatch)}'> - {$this->escaper->escapeUrl($urlMatch)}</a>", - $content - ); - } - } - - return $content; - } -} diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema.xml b/app/code/Magento/AdminAnalytics/etc/db_schema.xml index d640c6af45286..b9607ce77d150 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema.xml +++ b/app/code/Magento/AdminAnalytics/etc/db_schema.xml @@ -7,24 +7,17 @@ --> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> - <table name="admin_usage_viewer_log" resource="default" engine="innodb" + <table name="admin_analytics_usage_version_log" resource="default" engine="innodb" comment="Admin Notification Viewer Log Table"> <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" comment="Log ID"/> - <column xsi:type="int" name="viewer_id" padding="10" unsigned="true" nullable="false" identity="false" - comment="Viewer admin user ID"/> - <column xsi:type="varchar" name="last_view_version" nullable="false" length="16" - comment="Viewer last view on product version"/> - <column xsi:type="smallint" name="is_admin_usage_enabled" padding="5" unsigned="true" nullable="false" identity="false" - default="0" comment="Flag if admin usage is enabled"/> + <column xsi:type="varchar" name="last_viewed_in_version" nullable="false" length="50" + comment="Viewer last viewed on product version"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="id"/> </constraint> - <constraint xsi:type="foreign" referenceId="ADMIN_NOTIFICATION_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID" - table="admin_notification_viewer_log" column="viewer_id" referenceTable="admin_user" - referenceColumn="user_id" onDelete="CASCADE"/> - <constraint xsi:type="unique" referenceId="ADMIN_NOTIFICATION_VIEWER_LOG_VIEWER_ID"> - <column name="viewer_id"/> + <constraint xsi:type="unique" referenceId="ADMIN_ANALYTICS_USAGE_VERSION_LOG_LAST_VIEWED_IN_VERSION"> + <column name="last_viewed_in_version"/> </constraint> </table> </schema> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json index 01fb68d4b58f7..26cd4e9c6bcf4 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json @@ -2,14 +2,11 @@ "admin_usage_viewer_log": { "column": { "id": true, - "viewer_id": true, - "last_view_version": true, - "is_admin_usage_enabled": true + "last_viewed_in_version": true }, "constraint": { "PRIMARY": true, - "ADMIN_USAGE_VIEWER_LOG_VIEWER_ID_ADMIN_USER_USER_ID": true, - "ADMIN_USAGE_VIEWER_LOG_VIEWER_ID": true + "ADMIN_ANALYTICS_USAGE_VERSION_LOG_LAST_VIEWED_IN_VERSION": true } } } \ No newline at end of file From e436ae6e4956fb1c6546ccc91af05d61011ceb76 Mon Sep 17 00:00:00 2001 From: vital_pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Sat, 27 Jul 2019 05:10:52 +0300 Subject: [PATCH 0263/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Added integration test --- .../Controller/Adminhtml/CategoryTest.php | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 1001d58ee8a67..bfe02f2aa2e10 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Catalog\Model\ResourceModel\Product; use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Message\MessageInterface; use Magento\Store\Model\Store; -use Magento\Catalog\Model\ResourceModel\Product; +use Magento\TestFramework\Helper\Bootstrap; /** + * Test class for \Magento\Catalog\Controller\Adminhtml\Category. + * * @magentoAppArea adminhtml */ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendController @@ -36,6 +42,8 @@ protected function setUp() } /** + * Test save action. + * * @magentoDataFixture Magento/Store/_files/core_fixturestore.php * @magentoDbIsolation enabled * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 @@ -48,7 +56,7 @@ protected function setUp() public function testSaveAction($inputData, $defaultAttributes, $attributesSaved = [], $isSuccess = true) { /** @var $store \Magento\Store\Model\Store */ - $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); + $store = Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); $store->load('fixturestore', 'code'); $storeId = $store->getId(); @@ -61,14 +69,12 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved if ($isSuccess) { $this->assertSessionMessages( $this->equalTo(['You saved the category.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } /** @var $category \Magento\Catalog\Model\Category */ - $category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Category::class - ); + $category = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Category::class); $category->setStoreId($storeId); $category->load(2); @@ -95,6 +101,8 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved } /** + * Test save action from product creation page. + * * @param array $postData * @dataProvider categoryCreatedFromProductCreationPageDataProvider * @magentoDbIsolation enabled @@ -377,11 +385,13 @@ public function testSaveActionCategoryWithDangerRequest() $this->dispatch('backend/catalog/category/save'); $this->assertSessionMessages( $this->equalTo(['The "Name" attribute value is empty. Set the attribute and try again.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR + MessageInterface::TYPE_ERROR ); } /** + * Test move action. + * * @magentoDataFixture Magento/Catalog/_files/category_tree.php * @dataProvider moveActionDataProvider * @@ -433,6 +443,8 @@ public function moveActionDataProvider() } /** + * Test save category with product position. + * * @magentoDataFixture Magento/Catalog/_files/products_in_different_stores.php * @magentoDbIsolation disabled * @dataProvider saveActionWithDifferentWebsitesDataProvider @@ -541,7 +553,7 @@ public function saveActionWithDifferentWebsitesDataProvider() } /** - * Get items count from catalog_category_product + * Get items count from catalog_category_product. * * @return int */ @@ -555,4 +567,36 @@ private function getCategoryProductsCount(): int $this->productResource->getConnection()->fetchAll($oldCategoryProducts) ); } + + /** + * Verify that the category cannot be saved if the category url matches the admin url. + * + * @magentoConfigFixture admin/url/use_custom_path 1 + * @magentoConfigFixture admin/url/custom_path backend + */ + public function testSaveWithCustomBackendNameAction() + { + $frontNameResolver = Bootstrap::getObjectManager()->create(FrontNameResolver::class); + $urlKey = $frontNameResolver->getFrontName(); + $inputData = [ + 'id' => '2', + 'url_key' => $urlKey, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1 + ] + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($inputData); + $this->dispatch('backend/catalog/category/save'); + $this->assertSessionMessages( + $this->equalTo( + [ + 'URL key "backend" conflicts with reserved endpoint names: ' + . 'admin, soap, rest, graphql, backend. Try another url key.' + ] + ), + MessageInterface::TYPE_ERROR + ); + } } From 45742cd6cebd7ac158fe8f60dbbd0d2aef0e4af2 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Sat, 27 Jul 2019 09:37:25 -0400 Subject: [PATCH 0264/2437] Remove looped item save CartItemRepositoryInterface::save trigger quote collection and save. The update cart items resolver accepts multiple quote items and save the quote. Saving each quote item increases the quote save by the number of items passed in the update request. --- app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php index b18c6ad662335..81a7779eb98b5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php @@ -117,7 +117,6 @@ private function updateItemQuantity(int $itemId, Quote $cart, float $quantity) } $cartItem->setQty($quantity); $this->validateCartItem($cartItem); - $this->cartItemRepository->save($cartItem); } /** From e0a00fbeec82bc173844608b9f95b3b6d646ffa7 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 29 Jul 2019 09:33:44 +0300 Subject: [PATCH 0265/2437] MC-18699: Fixing integration test --- .../testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php index 59ad82334a958..3b7273e088105 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php @@ -736,7 +736,7 @@ public function testValidateUploadFileExceptionDataProvider() ], 'image_empty' => [ 'fileName' => 'empty.png', - 'expectedErrorMsg' => 'Disallowed file type.', + 'expectedErrorMsg' => 'Wrong file size.', 'useFixture' => true ], 'notanimage' => [ From 234945755b8701b2ee9b651837701e5d14d6886c Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 29 Jul 2019 08:29:13 -0500 Subject: [PATCH 0266/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added block --- app/code/Magento/AdminAnalytics/Block/Metadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/Block/Metadata.php b/app/code/Magento/AdminAnalytics/Block/Metadata.php index e89d09ab100eb..2a669829a8211 100644 --- a/app/code/Magento/AdminAnalytics/Block/Metadata.php +++ b/app/code/Magento/AdminAnalytics/Block/Metadata.php @@ -73,7 +73,7 @@ public function getCurrentUser() :string return hash('sha512', 'ADMIN_USER' . $this->authSession->getUser()->getEmail()); } /** - * Get Magento mode + * Get Magento mode that the user is using * * @return string */ From 3a7554b281103ac506e406bb8c1b9a1b53db2419 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 29 Jul 2019 08:34:02 -0500 Subject: [PATCH 0267/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added routes,system,module --- app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml | 2 +- app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml | 2 +- app/code/Magento/AdminAnalytics/etc/module.xml | 1 + .../Magento/AdminAnalytics/view/adminhtml/layout/default.xml | 1 + .../AdminAnalytics/view/adminhtml/templates/tracking.phtml | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml index e1024b12bc42d..5b5f2b52210b0 100644 --- a/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/routes.xml @@ -11,4 +11,4 @@ <module name="Magento_AdminAnalytics" /> </route> </router> -</config> \ No newline at end of file +</config> diff --git a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml index 97372e1cca087..d6867e74c4760 100644 --- a/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml +++ b/app/code/Magento/AdminAnalytics/etc/adminhtml/system.xml @@ -18,4 +18,4 @@ </group> </section> </system> -</config> \ No newline at end of file +</config> diff --git a/app/code/Magento/AdminAnalytics/etc/module.xml b/app/code/Magento/AdminAnalytics/etc/module.xml index f0990b114e25f..e27c90db11e29 100644 --- a/app/code/Magento/AdminAnalytics/etc/module.xml +++ b/app/code/Magento/AdminAnalytics/etc/module.xml @@ -8,3 +8,4 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_AdminAnalytics"/> </config> + diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 5339fbc894a41..62a9dda8b090c 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -24,3 +24,4 @@ </body> </page> + diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml index e4acd77b13353..03c4c848aa44a 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml @@ -12,4 +12,4 @@ "user": "<?= $block->escapeJs($block->getCurrentUser()) ?>", "mode": "<?= $block->escapeJs($block->getMode()) ?>" }; -</script> \ No newline at end of file +</script> From 7ade44e833f918a8f3429b25e0531dd95387f65f Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Mon, 29 Jul 2019 13:09:35 -0500 Subject: [PATCH 0268/2437] MC-18721: Customer Inline editor option error - Resolved incorrect template disabling - Updated template render logic during recursive render --- app/code/Magento/Ui/view/base/web/js/grid/editing/record.js | 5 ----- lib/web/mage/utils/template.js | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js b/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js index 14bed73a694c6..9b8998368c5ff 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/editing/record.js @@ -134,11 +134,6 @@ define([ field = utils.extend({}, fields.base, field); - field.__disableTmpl = { - label: true, - options: true - }; - return utils.template(field, { record: this, column: column diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index 4032c9387904d..08d6ea42306ae 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -136,7 +136,9 @@ define([ cycles = 0; while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) { - tmpl = template(tmpl, data); + if (!isTmplIgnored(tmpl, data)) { + tmpl = template(tmpl, data); + } if (tmpl === last) { break; From c2e3a90d7a4b3cb9ca7bdeb7dc4f36d1f537ad6e Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 29 Jul 2019 14:34:30 -0500 Subject: [PATCH 0269/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added test --- .../Section/AdminUsageNotificationSection.xml | 16 ++ .../Test/Mftf/Test/TrackingScriptTest.xml | 221 +++++++++--------- .../ui_component/admin_usage_notification.xml | 2 +- .../view/adminhtml/web/js/modal/component.js | 13 +- .../Config/Test/Mftf/Page/AdminConfigPage.xml | 3 + .../Test/Mftf/Page/ReportsSearchTermsPage.xml | 12 + 6 files changed, 150 insertions(+), 117 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml create mode 100644 app/code/Magento/Reports/Test/Mftf/Page/ReportsSearchTermsPage.xml diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml new file mode 100644 index 0000000000000..0a28e5f198669 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminUsageNotificationSection"> + <element name="adminUsageDialogBox" type="text" selector="//*[@class='modal-inner-wrap'][last()]"/> + <element name="adminUsageAllowButton" type="text" selector="//*[@id='html-body']/div[4]/aside[2]/div[2]/footer/button[2]"/> +<!-- <element name="adminUsageDontAllowButton" type="text" selector="//*button[contains('Don't Allow')]"/>--> + </section> +</sections> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml index da0d5041a6e34..f2ed558ea8832 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml @@ -21,117 +21,114 @@ </annotations> <!-- Logging in Magento admin and checking for tracking script in dashboard --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <closeAdminNotification stepKey="closeAdminNotification"/> - - <!-- Navigating to advance settings --> - <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettings"/> - <waitForPageLoad stepKey="waitForAdvanceSettings"/> - - <!-- Changing usage setting to Yes --> - <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToYes"> - <argument name="adminUsageValue" value="1"/> - </actionGroup> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrl"/> - - <!-- Checking for tracking script in salesOrderPage --> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPage"/> - <waitForPageLoad stepKey="waitForSalesOrderPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSalesOrder"/> - - <!-- Checking for tracking script in catalogProductsPage --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPage"/> - <waitForPageLoad stepKey="waitForCatalogProductsPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCatalogProducts"/> - - <!-- Checking for tracking script in customersAllCustomersPage --> - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPage"/> - <waitForPageLoad stepKey="waitForCustomersAllCustomersPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCustomersAllCustomers"/> - - <!-- Checking for tracking script in marketingCatalogPriceRulePage --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePage"/> - <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlMarketingCatalogPriceRule"/> - - <!-- Checking for tracking script in contentBlocksPage --> - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPage"/> - <waitForPageLoad stepKey="waitForContentBlocksPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlContentBlocks"/> - - <!-- Checking for tracking script in reportsSearchTermsPage --> - <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPage"/> - <waitForPageLoad stepKey="waitForSearchTermsPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlReportsSearchTerms"/> - - <!-- Checking for tracking script in storesAllStoresPage --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPage"/> - <waitForPageLoad stepKey="waitForStoresAllStoresPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlStoresAllStores"/> - - <!-- Checking for tracking script in systemImportPage --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPage"/> - <waitForPageLoad stepKey="waitForSystemImportPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSystemImport"/> - - <!-- Checking for tracking script in findPartnersAndExtensionsPage --> - <amOnPage url="{{FindPartnersAndExtensionsPage.url}}" stepKey="goToFindPartnersAndExtensionsPage"/> - <waitForPageLoad stepKey="waitForFindPartnersAndExtensionsPage"/> - <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlFindPartnersAndExtensions"/> - - <!-- Navigating to advance settings --> - <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettingsAgain"/> - <waitForPageLoad stepKey="waitForAdvanceSettingsAgain"/> - - <!-- Changing usage setting back to No --> - <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToNo"> - <argument name="adminUsageValue" value="0"/> - </actionGroup> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlTest"/> - - <!-- Checking for removed tracking script in salesOrderPage --> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPageAgain"/> - <waitForPageLoad stepKey="waitForSalesOrderPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSalesOrderTest"/> - - <!-- Checking for removed tracking script in catalogProductsPage --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPageAgain"/> - <waitForPageLoad stepKey="waitForCatalogProductsPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCatalogProductsTest"/> - - <!-- Checking for removed tracking script in customersAllCustomersPage --> - <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPageAgain"/> - <waitForPageLoad stepKey="waitForCustomersAllCustomersPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCustomersAllCustomersTest"/> - - <!-- Checking for removed tracking script in marketingCatalogPriceRulePage --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePageAgain"/> - <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlMarketingCatalogTest"/> - - <!-- Checking for removed tracking script in contentBlocksPage --> - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPageAgain"/> - <waitForPageLoad stepKey="waitForContentBlocksPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlContentBlocksTest"/> - - <!-- Checking for removed tracking script in reportsSearchTermsPage --> - <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPageAgain"/> - <waitForPageLoad stepKey="waitForSearchTermsPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlReportsSearchTest"/> - - <!-- Checking for removed tracking script in storesAllStoresPage --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPageAgain"/> - <waitForPageLoad stepKey="waitForStoresAllStoresPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlStoresAllStoresTest"/> - - <!-- Checking for removed tracking script in systemImportPage --> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPageAgain"/> - <waitForPageLoad stepKey="waitForSystemImportPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSystemsImportTest"/> - - <!-- Checking for removed tracking script in findPartnersAndExtensionsPage --> - <amOnPage url="{{FindPartnersAndExtensionsPage.url}}" stepKey="goToFindPartnersAndExtensionsPageAgain"/> - <waitForPageLoad stepKey="waitForFindPartnersAndExtensionsPageAgain"/> - <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlFindPartnersTest"/> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + + <wait time="10" stepKey="waitTenSecondsAgainAgain"/> + + <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageDialogBox}}" stepKey="seeDialogBox"/> + <wait time="10" stepKey="waitTenSecondsAgain"/> + <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" stepKey="seeAllowButton"/> +<!-- <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageDialogBox}}" stepKey="seeDialogBox"/>--> + <wait time="10" stepKey="waitTenSeconds"/> + +<!-- <!– Navigating to advance settings –>--> +<!-- <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettings"/>--> +<!-- <waitForPageLoad stepKey="waitForAdvanceSettings"/>--> + +<!-- <!– Changing usage setting to Yes –>--> +<!-- <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToYes">--> +<!-- <argument name="adminUsageValue" value="1"/>--> +<!-- </actionGroup>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrl"/>--> + +<!-- <!– Checking for tracking script in salesOrderPage –>--> +<!-- <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPage"/>--> +<!-- <waitForPageLoad stepKey="waitForSalesOrderPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSalesOrder"/>--> + +<!-- <!– Checking for tracking script in catalogProductsPage –>--> +<!-- <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPage"/>--> +<!-- <waitForPageLoad stepKey="waitForCatalogProductsPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCatalogProducts"/>--> + +<!-- <!– Checking for tracking script in customersAllCustomersPage –>--> +<!-- <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPage"/>--> +<!-- <waitForPageLoad stepKey="waitForCustomersAllCustomersPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCustomersAllCustomers"/>--> + +<!-- <!– Checking for tracking script in marketingCatalogPriceRulePage –>--> +<!-- <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePage"/>--> +<!-- <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlMarketingCatalogPriceRule"/>--> + +<!-- <!– Checking for tracking script in contentBlocksPage –>--> +<!-- <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPage"/>--> +<!-- <waitForPageLoad stepKey="waitForContentBlocksPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlContentBlocks"/>--> + +<!-- <!– Checking for tracking script in reportsSearchTermsPage –>--> +<!-- <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPage"/>--> +<!-- <waitForPageLoad stepKey="waitForSearchTermsPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlReportsSearchTerms"/>--> + +<!-- <!– Checking for tracking script in storesAllStoresPage –>--> +<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPage"/>--> +<!-- <waitForPageLoad stepKey="waitForStoresAllStoresPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlStoresAllStores"/>--> + +<!-- <!– Checking for tracking script in systemImportPage –>--> +<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPage"/>--> +<!-- <waitForPageLoad stepKey="waitForSystemImportPage"/>--> +<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSystemImport"/>--> + +<!-- <!– Navigating to advance settings –>--> +<!-- <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettingsAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForAdvanceSettingsAgain"/>--> + +<!-- <!– Changing usage setting back to No –>--> +<!-- <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToNo">--> +<!-- <argument name="adminUsageValue" value="0"/>--> +<!-- </actionGroup>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlTest"/>--> + +<!-- <!– Checking for removed tracking script in salesOrderPage –>--> +<!-- <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForSalesOrderPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSalesOrderTest"/>--> + +<!-- <!– Checking for removed tracking script in catalogProductsPage –>--> +<!-- <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForCatalogProductsPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCatalogProductsTest"/>--> + +<!-- <!– Checking for removed tracking script in customersAllCustomersPage –>--> +<!-- <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForCustomersAllCustomersPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCustomersAllCustomersTest"/>--> + +<!-- <!– Checking for removed tracking script in marketingCatalogPriceRulePage –>--> +<!-- <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlMarketingCatalogTest"/>--> + +<!-- <!– Checking for removed tracking script in contentBlocksPage –>--> +<!-- <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForContentBlocksPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlContentBlocksTest"/>--> + +<!-- <!– Checking for removed tracking script in reportsSearchTermsPage –>--> +<!-- <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForSearchTermsPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlReportsSearchTest"/>--> + +<!-- <!– Checking for removed tracking script in storesAllStoresPage –>--> +<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForStoresAllStoresPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlStoresAllStoresTest"/>--> + +<!-- <!– Checking for removed tracking script in systemImportPage –>--> +<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPageAgain"/>--> +<!-- <waitForPageLoad stepKey="waitForSystemImportPageAgain"/>--> +<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSystemsImportTest"/>--> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index f9d11d2a6a7fe..7c6d1d43006de 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -50,7 +50,7 @@ <options> <option name="modalClass" xsi:type="string">admin-usage-notification</option> <option name="title" xsi:type="string" translate="true">Allow admin usage data collection</option> - <option name="autoOpen" xsi:type="array">true</option> + <option name="autoOpen" xsi:type="boolean">true</option> <option name="type" xsi:type="string">popup</option> <option name="clickableOverlay" xsi:type="boolean">false</option> <option name="responsive" xsi:type="boolean">true</option> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 6c449e45f7aad..2cf9df87196f6 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -17,13 +17,18 @@ define( imports: { enableLogAction: '${ $.provider }:data.enableLogAction', disableLogAction: '${ $.provider }:data.disableLogAction' + }, + options: { + keyEventHandlers: { + escapeKey: function () { return; } + } } }, - keyEventHandlers: { - escapeKey: function () { - } + initModal: function () { + this.options.opened = this.onOpened.bind(this); + this._super(); }, - opened: function () { + onOpened: function () { $('.modal-header button.action-close').hide(); }, enableAdminUsage: function () { diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml index 7a62dfff8323b..688623b612c27 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigPage.xml @@ -21,4 +21,7 @@ <page name="AdminConfigGeneralPage" url="admin/system_config/edit/section/general/" area="admin" module="Magento_Config"> <section name="GeneralSection"/> </page> + <page name="AdminConfigAdvancedAdmin" url="admin/system_config/edit/section/admin/" area="admin" module="Magento_Config"> + <section name="AdvanceAdminSection"/> + </page> </pages> diff --git a/app/code/Magento/Reports/Test/Mftf/Page/ReportsSearchTermsPage.xml b/app/code/Magento/Reports/Test/Mftf/Page/ReportsSearchTermsPage.xml new file mode 100644 index 0000000000000..e9e60c6d6dccb --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/Page/ReportsSearchTermsPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="ReportsSearchTermPage" url="search/term/report/" area="admin" module="Magento_Reports"> + <section name="ReportsSearchTermSection"/> + </page> +</pages> From 6ce7e16641a225a22ac0e1e78a0e34b232f08b6d Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 29 Jul 2019 14:49:23 -0500 Subject: [PATCH 0270/2437] MC-15978: Catalog Product Attribute Set Name --- .../Ui/DataProvider/Product/Form/Modifier/AttributeSet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php index 3420965597c5e..474b139810267 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php @@ -78,7 +78,7 @@ public function getOptions() \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection::SORT_ORDER_ASC ); - $collectionData = $collection->getData(); + $collectionData = $collection->getData() ?? []; array_walk($collectionData, function (&$attribute) { $attribute['__disableTmpl'] = true; From 60bee1108ee49ae6317f0ef4f5a150523949e8a8 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 29 Jul 2019 14:53:38 -0500 Subject: [PATCH 0271/2437] MC-15978: Catalog Product Attribute Set Name --- .../testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php index 59ad82334a958..3b7273e088105 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Image/Adapter/InterfaceTest.php @@ -736,7 +736,7 @@ public function testValidateUploadFileExceptionDataProvider() ], 'image_empty' => [ 'fileName' => 'empty.png', - 'expectedErrorMsg' => 'Disallowed file type.', + 'expectedErrorMsg' => 'Wrong file size.', 'useFixture' => true ], 'notanimage' => [ From 615da2c895f97e07e311f4e2c277641ac9d2d3ca Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Mon, 29 Jul 2019 15:59:20 -0500 Subject: [PATCH 0272/2437] MC-18125: Email template improvement --- lib/internal/Magento/Framework/Filter/Template.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Filter/Template.php b/lib/internal/Magento/Framework/Filter/Template.php index c330e4d130094..0cd2935a24b1d 100644 --- a/lib/internal/Magento/Framework/Filter/Template.php +++ b/lib/internal/Magento/Framework/Filter/Template.php @@ -82,16 +82,16 @@ class Template implements \Zend_Filter_Interface 'getdatausingmethod', '__destruct', '__call', - '__callStatic', + '__callstatic', '__set', '__unset', '__sleep', '__wakeup', '__invoke', '__set_state', - '__debugInfo', - '___callParent', - '___callPlugins' + '__debuginfo', + '___callparent', + '___callplugins' ]; /** From febabea0d40b52d53553ac98b68b8a300e8195f8 Mon Sep 17 00:00:00 2001 From: Hwashiang Yu <hwyu@adobe.com> Date: Mon, 29 Jul 2019 18:39:55 -0500 Subject: [PATCH 0273/2437] MC-18721: Customer Inline editor option error - Resolved funtional test failures --- lib/web/mage/utils/template.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/utils/template.js b/lib/web/mage/utils/template.js index 08d6ea42306ae..e954ca50bac81 100644 --- a/lib/web/mage/utils/template.js +++ b/lib/web/mage/utils/template.js @@ -40,7 +40,7 @@ define([ * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}. * * @param {String} tmpl - * @param {Object} target + * @param {Object | undefined} target * @returns {Boolean|Object} */ function isTmplIgnored(tmpl, target) { @@ -136,7 +136,7 @@ define([ cycles = 0; while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) { - if (!isTmplIgnored(tmpl, data)) { + if (!isTmplIgnored(tmpl)) { tmpl = template(tmpl, data); } From 7c9b5f7fe45ba39d64f782b609efc370ea2684e3 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Tue, 30 Jul 2019 12:43:39 +0300 Subject: [PATCH 0274/2437] refactoring --- .../StorefrontClickHeaderLinkActionGroup.xml | 17 ++++++++++ .../StorefrontFillRegistryFormActionGroup.xml | 31 ------------------- .../StorefrontSeeHeaderLinksActionGroup.xml | 3 -- ...teAccountPasswordComplexityActionGroup.xml | 17 ++++++++++ .../Customer/Test/Mftf/Data/CustomerData.xml | 20 ++++++++++++ .../StorefrontCustomerCreateFormSection.xml | 1 + .../NewCustomerPasswordComplexityTest.xml | 24 +++++++++++--- .../NewCustomerPasswordComplexityTest.xml | 2 ++ 8 files changed, 77 insertions(+), 38 deletions(-) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml new file mode 100644 index 0000000000000..46c06e909b4d9 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontClickHeaderLinkActionGroup"> + <arguments> + <argument name="LinkName" type="string" defaultValue="Create an Account"/> + </arguments> + <click stepKey="ClickTheLink" selector="{{StorefrontHeaderSection.HeaderLinkByText(LinkName)}}"/> + <waitForPageLoad stepKey="Wait"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml deleted file mode 100644 index 0a1440937a554..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontFillRegistryFormActionGroup.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontFillRegistryFormActionGroup"> - - <!--Fill Registry Form with password length is below 8 characters --> - <fillField stepKey="fillFirstName1stVariation" userInput="John" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> - <fillField stepKey="fillLastName1stVariation" userInput="Doe" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> - <fillField stepKey="fillEmail1stVariation" userInput="johndoe@domain.com" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> - <fillField stepKey="fillPassword1stVariation" userInput="123123" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> - <fillField stepKey="fillConfirmPassword1stVariation" userInput="123123" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> - <click stepKey="clickCreateAccountButton1stVariation" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <see userInput="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored." stepKey="seeTheErrorMessageIsDisplayed1"/> - - <!--Fill Registry Form with not secure enough password --> - <fillField stepKey="fillFirstName2ndVariation" userInput="John" selector="{{StorefrontCustomerCreateFormSection.firstnameField}}"/> - <fillField stepKey="fillLastName2ndVariation" userInput="Doe" selector="{{StorefrontCustomerCreateFormSection.lastnameField}}"/> - <fillField stepKey="fillEmail2ndVariation" userInput="johndoe@domain.com" selector="{{StorefrontCustomerCreateFormSection.emailField}}"/> - <fillField stepKey="fillPassword2ndVariation" userInput="123123qa" selector="{{StorefrontCustomerCreateFormSection.passwordField}}"/> - <fillField stepKey="fillConfirmPassword2ndVariation" userInput="123123qa" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> - <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> - <see userInput="Minimum of different classes of characters in password is 3. Classes of characters: Lower Case, Upper Case, Digits, Special Characters." stepKey="seeTheErrorMessageIsDisplayed2"/> - - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml index b70560ad2cd86..3155cca583d59 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml @@ -11,9 +11,6 @@ <arguments> <argument name="LinkName" type="string" defaultValue="Create an Account"/> </arguments> - <see stepKey="SeeElement" selector="{{StorefrontHeaderSection.headerlinks}}" userInput="{{LinkName}}"/> - <click stepKey="ClickLink" selector="{{StorefrontHeaderSection.HeaderLinkByText(LinkName)}}"/> - <waitForPageLoad stepKey="Wait"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml new file mode 100644 index 0000000000000..9d7dbc604f59d --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup"> + <arguments> + <argument name="message" type="string"/> + </arguments> + <see userInput="{{message}}" selector="{{StorefrontCustomerCreateFormSection.PasswordErrorMessages}}" stepKey="verifyMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 5904067aea639..77bf6277d9c21 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -271,4 +271,24 @@ <requiredEntity type="address">US_Address_TX</requiredEntity> <requiredEntity type="address">US_Address_NY_Not_Default_Address</requiredEntity> </entity> + <entity name="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">123123</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> + <entity name="Simple_Customer_With_Not_Secure_Password" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">123123qa</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml index 8881a2a012ce8..5a731b2c3f0ed 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -17,6 +17,7 @@ <element name="passwordField" type="input" selector="#password"/> <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> + <element name="PasswordErrorMessages" type="text" selector="#password-error"/> </section> <section name="StoreFrontCustomerAdvancedAttributesSection"> <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml index 29135332d6125..ccd97f83cd0a6 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -21,12 +21,28 @@ <!-- TEST BODY --> <!-- Go to storefront home page --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- See the Registration Link --> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="SeeTheLink"/> <!-- Click the Registration Link --> - <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="ClickTheLink"> - <argument name="LinkName" value="Create an Account" /> + <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="ClickTheLink"> + <argument name="LinkName" value="Create an Account"/> + </actionGroup> + <!-- Fill Registration Form with Password length is bellow 8 Characters --> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="FillRegistrationFormPasswordLengthBellowEightCharacters"> + <argument name="customer" value="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters"/> + </actionGroup> + <!-- See the Error --> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="SeeTheErrorPasswordLength"> + <argument name="message" value="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored."/> + </actionGroup> + <!-- Fill Registration Form with not secure enough password --> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="FillRegistrationFormPasswordNotSecure"> + <argument name="customer" value="Simple_Customer_With_Not_Secure_Password"/> + </actionGroup> + <!-- See the Error --> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="SeeTheErrorPasswordSecure"> + <argument name="message" value="Minimum of different classes of characters in password is 3. Classes of characters: Lower Case, Upper Case, Digits, Special Characters."/> </actionGroup> - <!-- Fill Registry Form with Incorrect Password (Two variations) --> - <actionGroup ref="StorefrontFillRegistryFormActionGroup" stepKey="FillRegistryForm"/> <!--Test Body END--> </test> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml index a169a3f175cc6..7b6f3e981714c 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml @@ -16,6 +16,7 @@ <data name="customer/data/password" xsi:type="string">123123</data> <data name="customer/data/password_confirmation" xsi:type="string">123123</data> <constraint name="Magento\Security\Test\Constraint\AssertPasswordLengthErrorMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="PasswordComplexityTest" summary="Customer password is not secure enough"> <data name="tag" xsi:type="string">severity:S1</data> @@ -26,6 +27,7 @@ <data name="customer/data/password" xsi:type="string">123123qa</data> <data name="customer/data/password_confirmation" xsi:type="string">123123qa</data> <constraint name="Magento\Security\Test\Constraint\AssertPasswordIsNotSecureEnoughMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 525ddef7eb4d0fa849c6111f47bf4eace338b8cf Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 30 Jul 2019 09:07:53 -0500 Subject: [PATCH 0275/2437] MC-15978: Catalog Product Attribute Set Name --- .../Product/Form/Modifier/AttributeSet.php | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php index 474b139810267..53c9595b59e76 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AttributeSet.php @@ -80,15 +80,18 @@ public function getOptions() $collectionData = $collection->getData() ?? []; - array_walk($collectionData, function (&$attribute) { - $attribute['__disableTmpl'] = true; - }); + array_walk( + $collectionData, + function (&$attribute) { + $attribute['__disableTmpl'] = true; + } + ); return $collectionData; } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -122,17 +125,20 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function modifyData(array $data) { - return array_replace_recursive($data, [ - $this->locator->getProduct()->getId() => [ - self::DATA_SOURCE_DEFAULT => [ - 'attribute_set_id' => $this->locator->getProduct()->getAttributeSetId() - ], + return array_replace_recursive( + $data, + [ + $this->locator->getProduct()->getId() => [ + self::DATA_SOURCE_DEFAULT => [ + 'attribute_set_id' => $this->locator->getProduct()->getAttributeSetId() + ], + ] ] - ]); + ); } } From cf29d0de077caae296bfc6151aecd4a9fd082d5a Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 30 Jul 2019 11:38:38 -0500 Subject: [PATCH 0276/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added conditionalClick to action groups --- .../CloseAllDialogBoxesActionGroup.xml | 20 ++++ .../Section/AdminUsageNotificationSection.xml | 9 +- .../Test/Mftf/Test/TrackingScriptTest.xml | 112 +----------------- .../LoginAdminWithCredentialsActionGroup.xml | 1 + .../ActionGroup/LoginAsAdminActionGroup.xml | 1 + 5 files changed, 31 insertions(+), 112 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml new file mode 100644 index 0000000000000..4fd7fd17c57ee --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CloseAllDialogBoxes"> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" visible="true" stepKey="clickCloseButtonIfVisible1"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" visible="true" stepKey="clickCloseButtonIfVisible2"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" visible="true" stepKey="clickCloseButtonIfVisible3"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" visible="true" stepKey="clickCloseButtonIfVisible4"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" visible="true" stepKey="clickCloseButtonIfVisible5"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" visible="true" stepKey="clickCloseButtonIfVisible6"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" visible="true" stepKey="clickAllowButtonIfVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml index 0a28e5f198669..388214df275a7 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml @@ -10,7 +10,12 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUsageNotificationSection"> <element name="adminUsageDialogBox" type="text" selector="//*[@class='modal-inner-wrap'][last()]"/> - <element name="adminUsageAllowButton" type="text" selector="//*[@id='html-body']/div[4]/aside[2]/div[2]/footer/button[2]"/> -<!-- <element name="adminUsageDontAllowButton" type="text" selector="//*button[contains('Don't Allow')]"/>--> + <element name="adminUsageAllowButton" type="text" selector=".modal-popup .action-secondary"/> + <element name="releaseNotificationCloseButton1" type="text" selector="//aside[1]/div[2]/header/button"/> + <element name="releaseNotificationCloseButton2" type="text" selector="//aside[2]/div[2]/header/button"/> + <element name="releaseNotificationCloseButton3" type="text" selector="//aside[3]/div[2]/header/button"/> + <element name="releaseNotificationCloseButton4" type="text" selector="//aside[4]/div[2]/header/button"/> + <element name="releaseNotificationCloseButton5" type="text" selector="//aside[5]/div[2]/header/button"/> + <element name="releaseNotificationCloseButton6" type="text" selector="//aside[6]/div[2]/header/button"/> </section> </sections> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml index f2ed558ea8832..58bcacc190cff 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Test/TrackingScriptTest.xml @@ -20,115 +20,7 @@ <group value="login"/> </annotations> - <!-- Logging in Magento admin and checking for tracking script in dashboard --> - <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - - <wait time="10" stepKey="waitTenSecondsAgainAgain"/> - - <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageDialogBox}}" stepKey="seeDialogBox"/> - <wait time="10" stepKey="waitTenSecondsAgain"/> - <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" stepKey="seeAllowButton"/> -<!-- <seeElementInDOM selector="{{AdminUsageNotificationSection.adminUsageDialogBox}}" stepKey="seeDialogBox"/>--> - <wait time="10" stepKey="waitTenSeconds"/> - -<!-- <!– Navigating to advance settings –>--> -<!-- <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettings"/>--> -<!-- <waitForPageLoad stepKey="waitForAdvanceSettings"/>--> - -<!-- <!– Changing usage setting to Yes –>--> -<!-- <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToYes">--> -<!-- <argument name="adminUsageValue" value="1"/>--> -<!-- </actionGroup>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrl"/>--> - -<!-- <!– Checking for tracking script in salesOrderPage –>--> -<!-- <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPage"/>--> -<!-- <waitForPageLoad stepKey="waitForSalesOrderPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSalesOrder"/>--> - -<!-- <!– Checking for tracking script in catalogProductsPage –>--> -<!-- <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPage"/>--> -<!-- <waitForPageLoad stepKey="waitForCatalogProductsPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCatalogProducts"/>--> - -<!-- <!– Checking for tracking script in customersAllCustomersPage –>--> -<!-- <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPage"/>--> -<!-- <waitForPageLoad stepKey="waitForCustomersAllCustomersPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlCustomersAllCustomers"/>--> - -<!-- <!– Checking for tracking script in marketingCatalogPriceRulePage –>--> -<!-- <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePage"/>--> -<!-- <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlMarketingCatalogPriceRule"/>--> - -<!-- <!– Checking for tracking script in contentBlocksPage –>--> -<!-- <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPage"/>--> -<!-- <waitForPageLoad stepKey="waitForContentBlocksPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlContentBlocks"/>--> - -<!-- <!– Checking for tracking script in reportsSearchTermsPage –>--> -<!-- <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPage"/>--> -<!-- <waitForPageLoad stepKey="waitForSearchTermsPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlReportsSearchTerms"/>--> - -<!-- <!– Checking for tracking script in storesAllStoresPage –>--> -<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPage"/>--> -<!-- <waitForPageLoad stepKey="waitForStoresAllStoresPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlStoresAllStores"/>--> - -<!-- <!– Checking for tracking script in systemImportPage –>--> -<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPage"/>--> -<!-- <waitForPageLoad stepKey="waitForSystemImportPage"/>--> -<!-- <seeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="seeTrackingUrlSystemImport"/>--> - -<!-- <!– Navigating to advance settings –>--> -<!-- <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="goToAdminSettingsAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForAdvanceSettingsAgain"/>--> - -<!-- <!– Changing usage setting back to No –>--> -<!-- <actionGroup ref="SelectAdminUsageSetting" stepKey="changeAdminUsageSettingToNo">--> -<!-- <argument name="adminUsageValue" value="0"/>--> -<!-- </actionGroup>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlTest"/>--> - -<!-- <!– Checking for removed tracking script in salesOrderPage –>--> -<!-- <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToSalesOrderPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForSalesOrderPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSalesOrderTest"/>--> - -<!-- <!– Checking for removed tracking script in catalogProductsPage –>--> -<!-- <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToCatalogProductsPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForCatalogProductsPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCatalogProductsTest"/>--> - -<!-- <!– Checking for removed tracking script in customersAllCustomersPage –>--> -<!-- <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersAllCustomersPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForCustomersAllCustomersPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlCustomersAllCustomersTest"/>--> - -<!-- <!– Checking for removed tracking script in marketingCatalogPriceRulePage –>--> -<!-- <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToMarketingCatalogPriceRulePageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForMarketingCatalogPriceRulePageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlMarketingCatalogTest"/>--> - -<!-- <!– Checking for removed tracking script in contentBlocksPage –>--> -<!-- <amOnPage url="{{CmsBlocksPage.url}}" stepKey="goToContentBlocksPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForContentBlocksPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlContentBlocksTest"/>--> - -<!-- <!– Checking for removed tracking script in reportsSearchTermsPage –>--> -<!-- <amOnPage url="{{ReportsSearchTermPage.url}}" stepKey="goToReportsSearchTermsPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForSearchTermsPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlReportsSearchTest"/>--> - -<!-- <!– Checking for removed tracking script in storesAllStoresPage –>--> -<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToStoresAllStoresPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForStoresAllStoresPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlStoresAllStoresTest"/>--> - -<!-- <!– Checking for removed tracking script in systemImportPage –>--> -<!-- <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToSystemImportPageAgain"/>--> -<!-- <waitForPageLoad stepKey="waitForSystemImportPageAgain"/>--> -<!-- <dontSeeElementInDOM selector="{{AdminHeaderSection.adminTrackingScript}}" stepKey="dontSeeTrackingUrlSystemsImportTest"/>--> + <!-- Logging in Magento admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml index 6aaa612b249b6..b5510404c9630 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -16,6 +16,7 @@ <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> <closeAdminNotification stepKey="closeAdminNotification"/> </actionGroup> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml index b2fbadcbe38e2..9c1963119d381 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml @@ -16,6 +16,7 @@ <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser.username}}" stepKey="fillUsername"/> <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminUser.password}}" stepKey="fillPassword"/> <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <closeAdminNotification stepKey="closeAdminNotification"/> </actionGroup> </actionGroups> From e9394a35e9bf9ed0871563d85592a803e6ca516c Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 30 Jul 2019 12:57:46 -0500 Subject: [PATCH 0277/2437] MC-15298: Allow admin to opt out of admin analytics tracking - fixed conditionalClick --- .../Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml index b5510404c9630..e423c68dd938b 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -16,8 +16,8 @@ <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}" stepKey="navigateToAdmin"/> <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <closeAdminNotification stepKey="closeAdminNotification"/> </actionGroup> </actionGroups> From 04a9f107c0aa55d100001efdaa0d22cba79c3755 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 26 Jul 2019 11:11:25 -0500 Subject: [PATCH 0278/2437] MC-18009: Disabled Products Do Not Appear in Search Results of Link Attribute - update to show all products regardless of status --- app/code/Magento/Catalog/Model/ProductLink/Search.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductLink/Search.php b/app/code/Magento/Catalog/Model/ProductLink/Search.php index 8750345aa222b..2ad838fc6c01d 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Search.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Search.php @@ -58,7 +58,6 @@ public function prepareCollection( ): \Magento\Catalog\Model\ResourceModel\Product\Collection { $productCollection = $this->productCollectionFactory->create(); $productCollection->addAttributeToSelect(ProductInterface::NAME); - $productCollection->setVisibility($this->catalogVisibility->getVisibleInCatalogIds()); $productCollection->setPage($pageNum, $limit); $this->filter->addFilter($productCollection, 'fulltext', ['fulltext' => $searchKey]); $productCollection->setPage($pageNum, $limit); From fa4844dfd36b14fd9a3f5f6f00209522bbf79c31 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 26 Jul 2019 14:17:43 -0500 Subject: [PATCH 0279/2437] MC-18009: Disabled Products Do Not Appear in Search Results of Link Attribute - update tests --- .../Magento/Catalog/Model/ProductLink/Search.php | 4 +++- .../Controller/Adminhtml/Product/SearchTest.php | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductLink/Search.php b/app/code/Magento/Catalog/Model/ProductLink/Search.php index 2ad838fc6c01d..ad7f3370ab3fe 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Search.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Search.php @@ -10,7 +10,9 @@ use Magento\Catalog\Api\Data\ProductInterface; -/** Returns collection of product visible in catalog by search key */ +/** + * Returns collection of product visible in catalog by search key + */ class Search { /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index 8a33543e93439..cdc2b4ca55e68 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -57,6 +57,20 @@ public function testExecuteNotVisibleIndividuallyProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('{"options":[],"total":0}', $responseBody); + $this->assertContains('"total":1}', $responseBody); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiple_mixed_products.php + */ + public function testExecuteEnabledAndDisabledProducts() : void + { + $this->getRequest() + ->setPostValue('searchKey', 'simple') + ->setPostValue('page', 1) + ->setPostValue('limit', 50); + $this->dispatch('backend/catalog/product/search'); + $responseBody = $this->getResponse()->getBody(); + $this->assertContains('"total":3}', $responseBody); } } From f641a988ae56f463fbac39dd56a19feaaf325606 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Mon, 29 Jul 2019 11:57:05 -0500 Subject: [PATCH 0280/2437] MC-18009: Disabled Products Do Not Appear in Search Results of Link Attribute - update tests --- .../Catalog/Controller/Adminhtml/Product/SearchTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index cdc2b4ca55e68..ee9ed5f656080 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -38,7 +38,8 @@ public function testExecuteNonExistingSearchKey() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('{"options":[],"total":0}', $responseBody); + $jsonResponse = json_decode($responseBody); + $this->assertEmpty($jsonResponse['options']); } /** @@ -57,7 +58,8 @@ public function testExecuteNotVisibleIndividuallyProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('"total":1}', $responseBody); + $jsonResponse = json_decode($responseBody); + $this->assertEquals(1, $jsonResponse['total']); } /** @@ -71,6 +73,7 @@ public function testExecuteEnabledAndDisabledProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('"total":3}', $responseBody); + $jsonResponse = json_decode($responseBody); + $this->assertEquals(7, $jsonResponse['total']); } } From cf9e61f56998189c9cc6eccd5fbf95c60dfe1ce8 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Mon, 29 Jul 2019 13:36:35 -0500 Subject: [PATCH 0281/2437] MC-18009: Disabled Products Do Not Appear in Search Results of Link Attribute - update tests --- .../Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index ee9ed5f656080..875c7f87d6009 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -60,6 +60,7 @@ public function testExecuteNotVisibleIndividuallyProducts() : void $responseBody = $this->getResponse()->getBody(); $jsonResponse = json_decode($responseBody); $this->assertEquals(1, $jsonResponse['total']); + $this->assertCount(1, $jsonResponse['options']); } /** @@ -75,5 +76,6 @@ public function testExecuteEnabledAndDisabledProducts() : void $responseBody = $this->getResponse()->getBody(); $jsonResponse = json_decode($responseBody); $this->assertEquals(7, $jsonResponse['total']); + $this->assertCount(7, $jsonResponse['options']); } } From 9c4c61dac3b04e3819b3d4c4fff41ea6836b412c Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Mon, 29 Jul 2019 15:50:23 -0500 Subject: [PATCH 0282/2437] MC-18009: Disabled Products Do Not Appear in Search Results of Link Attribute - update tests --- .../Catalog/Controller/Adminhtml/Product/SearchTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index 875c7f87d6009..0704d59a1431c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -38,7 +38,7 @@ public function testExecuteNonExistingSearchKey() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody); + $jsonResponse = json_decode($responseBody, true); $this->assertEmpty($jsonResponse['options']); } @@ -58,7 +58,7 @@ public function testExecuteNotVisibleIndividuallyProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody); + $jsonResponse = json_decode($responseBody, true); $this->assertEquals(1, $jsonResponse['total']); $this->assertCount(1, $jsonResponse['options']); } @@ -74,7 +74,7 @@ public function testExecuteEnabledAndDisabledProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody); + $jsonResponse = json_decode($responseBody, true); $this->assertEquals(7, $jsonResponse['total']); $this->assertCount(7, $jsonResponse['options']); } From 564f4411775cc0495a8dd8bffc6a110989c75b7d Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 31 Jul 2019 10:34:53 +0300 Subject: [PATCH 0283/2437] MC-17878: Fix delete attribute behavior --- .../Model/ResourceModel/Entity/Attribute.php | 21 ++++++++++- .../Api/ProductAttributeRepositoryTest.php | 20 +++++++++- .../Indexer/Product/Flat/ProcessorTest.php | 25 +++++++------ .../Filter/_files/attribute_with_option.php | 1 + .../Model/ResourceModel/Eav/AttributeTest.php | 10 ++++- .../Catalog/_files/category_attribute.php | 3 +- .../Catalog/_files/product_attribute.php | 3 +- .../_files/product_system_attribute.php | 16 ++++++++ .../product_system_attribute_rollback.php | 37 +++++++++++++++++++ .../Search/_files/date_attribute.php | 1 + .../Search/_files/filterable_attribute.php | 2 + .../Search/_files/filterable_attributes.php | 1 + .../Swatches/_files/swatch_attribute.php | 1 + 13 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute_rollback.php diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php index 0e7a46125d872..364e4d31d4d1d 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php @@ -11,7 +11,9 @@ use Magento\Eav\Model\Entity\Attribute as EntityAttribute; use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; +use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; /** * EAV attribute resource model @@ -20,7 +22,7 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ -class Attribute extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Attribute extends AbstractDb { /** * Eav Entity attributes cache @@ -189,6 +191,23 @@ protected function _beforeSave(AbstractModel $object) return parent::_beforeSave($object); } + /** + * @inheritdoc + * + * @param AbstractModel $attribute + * @return AbstractDb + * @throws CouldNotDeleteException + */ + protected function _beforeDelete(AbstractModel $attribute) + { + /** @var $attribute \Magento\Eav\Api\Data\AttributeInterface */ + if ($attribute->getId() && !$attribute->getIsUserDefined()) { + throw new CouldNotDeleteException(__("The system attribute can't be deleted.")); + } + + return parent::_beforeDelete($attribute); + } + /** * Save additional attribute data after save attribute * diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php index 386bd9fc9aeeb..42aa92652a5f1 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeRepositoryTest.php @@ -4,12 +4,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Catalog\Api; use Magento\Framework\Webapi\Exception as HTTPExceptionCodes; -use Magento\TestFramework\Helper\Bootstrap; +/** + * API tests for \Magento\Catalog\Model\Product\Attribute\Repository. + */ class ProductAttributeRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstract { const SERVICE_NAME = 'catalogProductAttributeRepositoryV1'; @@ -130,6 +131,7 @@ public function testCreateWithExceptionIfAttributeAlreadyExists() try { $this->createAttribute($attributeCode); $this->fail("Expected exception"); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (\SoapFault $e) { //Expects soap exception } catch (\Exception $e) { @@ -320,6 +322,20 @@ public function testDeleteById() $this->assertTrue($this->deleteAttribute($attributeCode)); } + /** + * Trying to delete system attribute. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_system_attribute.php + * @expectedException \Exception + * @expectedExceptionMessage The system attribute can't be deleted. + * @return void + */ + public function testDeleteSystemAttributeById(): void + { + $attributeCode = 'test_attribute_code_333'; + $this->deleteAttribute($attributeCode); + } + /** * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/ProcessorTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/ProcessorTest.php index bbaf453938705..9ae9cc6b6629f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/ProcessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Product/Flat/ProcessorTest.php @@ -5,8 +5,10 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Flat; +use Magento\Catalog\Model\Product\Attribute\Repository; + /** - * Class FullTest + * Integration tests for \Magento\Catalog\Model\Indexer\Product\Flat\Processor. */ class ProcessorTest extends \Magento\TestFramework\Indexer\TestCase { @@ -64,22 +66,23 @@ public function testSaveAttribute() } /** - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoAppArea adminhtml - * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_attribute_in_flat.php * @magentoConfigFixture current_store catalog/frontend/flat_catalog_product 1 */ public function testDeleteAttribute() { - /** @var $product \Magento\Catalog\Model\Product */ - $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product::class - ); - - /** @var \Magento\Catalog\Model\ResourceModel\Product $productResource */ - $productResource = $product->getResource(); - $productResource->getAttribute('media_gallery')->delete(); + /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $model */ + $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + /** @var Repository $productAttributeRepository */ + $productAttributeRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(Repository::class); + $productAttrubute = $productAttributeRepository->get('flat_attribute'); + $productAttributeId = $productAttrubute->getAttributeId(); + $model->load($productAttributeId)->delete(); $this->assertTrue($this->_processor->getIndexer()->isInvalid()); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option.php index 833d1b114a0b5..b4431678b2016 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option.php @@ -20,6 +20,7 @@ 'is_global' => 1, 'frontend_input' => 'select', 'is_filterable' => 1, + 'is_user_defined' => 1, 'option' => ['value' => ['option_0' => [0 => 'Option Label']]], 'backend_type' => 'int', ] diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Eav/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Eav/AttributeTest.php index 349853c8a3935..498c3167ed734 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Eav/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Eav/AttributeTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\ResourceModel\Eav; +/** + * Test for \Magento\Catalog\Model\ResourceModel\Eav\Attribute. + */ class AttributeTest extends \PHPUnit\Framework\TestCase { /** @@ -19,6 +22,11 @@ protected function setUp() ); } + /** + * Test Create -> Read -> Update -> Delete attribute operations. + * + * @return void + */ public function testCRUD() { $this->_model->setAttributeCode( @@ -31,7 +39,7 @@ public function testCRUD() )->getId() )->setFrontendLabel( 'test' - ); + )->setIsUserDefined(1); $crud = new \Magento\TestFramework\Entity($this->_model, ['frontend_label' => uniqid()]); $crud->testCrud(); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_attribute.php index 37398abdb8f7d..29b4a05c4dcbe 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_attribute.php @@ -9,5 +9,6 @@ ->create(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); $attribute->setAttributeCode('test_attribute_code_666') ->setEntityTypeId(3) - ->setIsGlobal(1); + ->setIsGlobal(1) + ->setIsUserDefined(1); $attribute->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_attribute.php index b5187d5d80768..b59675c5a28ae 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_attribute.php @@ -10,5 +10,6 @@ $attribute->setAttributeCode('test_attribute_code_333') ->setEntityTypeId(4) ->setIsGlobal(1) - ->setPrice(95); + ->setPrice(95) + ->setIsUserDefined(1); $attribute->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute.php new file mode 100644 index 0000000000000..1e7429cc831f5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +// phpcs:ignore Magento2.Security.IncludeFile +require __DIR__ . '/product_attribute.php'; +/** @var $attributeRepository \Magento\Catalog\Model\Product\Attribute\Repository */ +$attributeRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Model\Product\Attribute\Repository::class); +/** @var $attribute \Magento\Eav\Api\Data\AttributeInterface */ +$attribute = $attributeRepository->get('test_attribute_code_333'); + +$attributeRepository->save($attribute->setIsUserDefined(0)); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute_rollback.php new file mode 100644 index 0000000000000..0f997ff4b4941 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_system_attribute_rollback.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +/** @var $attributeRepository \Magento\Catalog\Model\Product\Attribute\Repository */ +$attributeRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Model\Product\Attribute\Repository::class); + +try { + /** @var $attribute \Magento\Eav\Api\Data\AttributeInterface */ + $attribute = $attributeRepository->get('test_attribute_code_333'); + $attributeRepository->save($attribute->setIsUserDefined(1)); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch +} catch (NoSuchEntityException $e) { +} +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $attribute = $attributeRepository->get('test_attribute_code_333'); + if ($attribute->getId()) { + $attribute->delete(); + } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch +} catch (\Exception $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/date_attribute.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/date_attribute.php index c7e118f4a4e2b..d11248a47a0cc 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/date_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/date_attribute.php @@ -25,6 +25,7 @@ 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), 'is_global' => 1, 'is_filterable' => 1, + 'is_user_defined' => 1, 'backend_type' => 'datetime', 'frontend_input' => 'date', 'frontend_label' => 'Test Date', diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php index 9188bb9657633..a74669e9890af 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php @@ -21,6 +21,7 @@ 'is_global' => 1, 'frontend_input' => 'select', 'is_filterable' => 1, + 'is_user_defined' => 1, 'option' => [ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], 'order' => ['option_0' => 1, 'option_1' => 2], @@ -48,6 +49,7 @@ 'is_global' => 1, 'frontend_input' => 'multiselect', 'is_filterable' => 1, + 'is_user_defined' => 1, 'option' => [ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], 'order' => ['option_0' => 1, 'option_1' => 2], diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php index f4f3337a253c0..03269b88ee2a1 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php @@ -78,6 +78,7 @@ 'entity_type_id' => $productEntityTypeId, 'is_global' => 1, 'is_filterable' => 1, + 'is_user_defined' => 1, 'backend_type' => 'datetime', 'frontend_input' => 'date', 'frontend_label' => 'Test Date', diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/swatch_attribute.php index 182f4781a9632..202fd0a8c73d5 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/swatch_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/swatch_attribute.php @@ -16,6 +16,7 @@ 'backend_type' => '', 'is_searchable' => 0, 'is_filterable' => 0, + 'is_user_defined' => 1, 'is_filterable_in_search' => 0, 'frontend_label' => 'Attribute ', 'entity_type_id' => 4 From 98f99ae4cec56ab6b567c7430a15e5f78c52af85 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 31 Jul 2019 16:09:19 +0300 Subject: [PATCH 0284/2437] MC-18750: Failed function test Magento\FunctionalTestingFramework.functional.StorefrontUKCustomerCheckoutWithCouponTest --- .../Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index ebf24e710fe39..13928dd91ec75 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -18,6 +18,7 @@ </annotations> <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"> <field key="price">20.00</field> @@ -38,6 +39,7 @@ </createData> </before> <after> + <magentoCLI command="downloadable:domains:remove" arguments="example.com static.magento.com" stepKey="removeDownloadableDomain"/> <deleteData createDataKey="createDownloadableProduct" stepKey="deleteProduct"/> <deleteData createDataKey="virtualProduct" stepKey="deleteVirtualProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> @@ -127,4 +129,4 @@ <see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="$15.00" stepKey="seeGrandTotal"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderStatus"/> </test> -</tests> \ No newline at end of file +</tests> From 3f0cac5163284b87c00b80f1cc69db1b74180724 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 08:21:33 -0500 Subject: [PATCH 0285/2437] MC-15298: Allow admin to opt out of admin analytics tracking - --- .../Adminhtml/Config/DisableAdminUsage.php | 10 +--------- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 12 +----------- .../ActionGroup/CloseAllDialogBoxesActionGroup.xml | 8 +------- .../Mftf/Section/AdminUsageNotificationSection.xml | 8 +------- .../AdminAnalytics/etc/db_schema_whitelist.json | 2 +- .../view/adminhtml/web/js/modal/component.js | 4 +++- 6 files changed, 8 insertions(+), 36 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index eb31e54e8041b..73635ddfee313 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -88,7 +88,7 @@ public function markUserNotified() } /** - * Log information about the last shown advertisement + * Changes the admin usage to no, and marks user notified * * @return ResultInterface */ @@ -97,12 +97,4 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } - - /** - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 9ce119f0c424f..e2ecb700e2b4c 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -88,7 +88,7 @@ public function markUserNotified() } /** - * Log information about the last shown advertisement + * Changes the admin usage to yes, and marks user notified * * @return \Magento\Framework\Controller\ResultInterface */ @@ -97,14 +97,4 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - - /** - * IsAllow allows function to be visible - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml index 4fd7fd17c57ee..71c039773f6bf 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml @@ -9,12 +9,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CloseAllDialogBoxes"> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" visible="true" stepKey="clickCloseButtonIfVisible1"/> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" visible="true" stepKey="clickCloseButtonIfVisible2"/> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" visible="true" stepKey="clickCloseButtonIfVisible3"/> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" visible="true" stepKey="clickCloseButtonIfVisible4"/> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" visible="true" stepKey="clickCloseButtonIfVisible5"/> - <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" visible="true" stepKey="clickCloseButtonIfVisible6"/> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" visible="true" stepKey="clickAllowButtonIfVisible"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml index 388214df275a7..8bd6263d35e38 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageNotificationSection.xml @@ -10,12 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUsageNotificationSection"> <element name="adminUsageDialogBox" type="text" selector="//*[@class='modal-inner-wrap'][last()]"/> - <element name="adminUsageAllowButton" type="text" selector=".modal-popup .action-secondary"/> - <element name="releaseNotificationCloseButton1" type="text" selector="//aside[1]/div[2]/header/button"/> - <element name="releaseNotificationCloseButton2" type="text" selector="//aside[2]/div[2]/header/button"/> - <element name="releaseNotificationCloseButton3" type="text" selector="//aside[3]/div[2]/header/button"/> - <element name="releaseNotificationCloseButton4" type="text" selector="//aside[4]/div[2]/header/button"/> - <element name="releaseNotificationCloseButton5" type="text" selector="//aside[5]/div[2]/header/button"/> - <element name="releaseNotificationCloseButton6" type="text" selector="//aside[6]/div[2]/header/button"/> + <element name="adminUsageDontAllowButton" type="text" selector=".modal-popup .action-secondary"/> </section> </sections> diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json index 26cd4e9c6bcf4..626e3ec14bc90 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json @@ -1,5 +1,5 @@ { - "admin_usage_viewer_log": { + "admin_analytics_usage_version_log": { "column": { "id": true, "last_viewed_in_version": true diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 2cf9df87196f6..17cffb33f043c 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -20,7 +20,8 @@ define( }, options: { keyEventHandlers: { - escapeKey: function () { return; } + escapeKey: function () { + return; } } } }, @@ -32,6 +33,7 @@ define( $('.modal-header button.action-close').hide(); }, enableAdminUsage: function () { + var data = { 'form_key': window.FORM_KEY }; From 254676a98582dbd1c3894a3a5e3c9d84179894a4 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 31 Jul 2019 16:22:13 +0300 Subject: [PATCH 0286/2437] MC-18749: Failed funtional test Magento\FunctionalTestingFramework.functional.StorefrontPrintOrderGuestTest --- .../Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 5c0d405102464..0bd8ab4855e97 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -19,6 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> + <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> <createData entity="ApiCategory" stepKey="createCategory"/> @@ -207,6 +208,7 @@ <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> </before> <after> + <magentoCLI command="downloadable:domains:remove" arguments="example.com static.magento.com" stepKey="removeDownloadableDomain"/> <deleteData createDataKey="downloadableProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> @@ -275,4 +277,4 @@ <see userInput="Flat Rate - Fixed" selector="{{StorefrontOrderDetailsSection.shippingMethod}}" stepKey="assertShippingMethodOnPrintOrder"/> <switchToPreviousTab stepKey="switchToPreviousTab"/> </test> -</tests> \ No newline at end of file +</tests> From 0b8deef2c17e326d06cf06e41b14bd044dcf60ae Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 31 Jul 2019 17:11:24 +0300 Subject: [PATCH 0287/2437] Working on the test --- .../AdminOpenConfigurationPageActionGroup.xml | 14 +++++++ .../AdminConfigurationAdminSectionPage.xml | 12 ++++++ .../Mftf/Test/LockAdminUserEntityTest.xml | 38 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml create mode 100644 app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml new file mode 100644 index 0000000000000..69972ea238b5e --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenConfigurationPageActionGroup"> + <amOnPage url="{{AdminConfigurationAdminSectionPage.url}}" stepKey="goToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml new file mode 100644 index 0000000000000..fe704162ddac7 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigurationAdminSectionPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> + <section name="AdminSection"/> + </page> +</pages> diff --git a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml new file mode 100644 index 0000000000000..6777573570783 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLockAdminUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Lock admin user during login"/> + <title value="Lock admin user after entering incorrect password specified number of times"/> + <description value="Lock admin user after entering incorrect password specified number of times"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <actionGroup ref="AdminOpenConfigurationPageActionGroup" stepKey="goToConfigurationPage"/> + + </test> +</tests> \ No newline at end of file From c2cc4ec892db647e348841b8eab1c7c3edabd81b Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 10:25:04 -0500 Subject: [PATCH 0288/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Added UI component --- .../Adminhtml/Config/DisableAdminUsage.php | 10 +++- .../Adminhtml/Config/EnableAdminUsage.php | 12 +++- .../AdminAnalytics/ViewModel/Notification.php | 56 +++++++++++++++++++ .../etc/db_schema_whitelist.json | 2 +- .../view/adminhtml/layout/default.xml | 15 +++-- .../view/adminhtml/requirejs-config.js | 14 +++++ .../adminhtml/templates/notification.phtml | 16 ++++++ .../view/adminhtml/web/js/modal/component.js | 29 ++++++---- .../modal/component-mixin.js | 29 ++++++++++ 9 files changed, 164 insertions(+), 19 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/ViewModel/Notification.php create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/requirejs-config.js create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/templates/notification.phtml create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 73635ddfee313..eb31e54e8041b 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -88,7 +88,7 @@ public function markUserNotified() } /** - * Changes the admin usage to no, and marks user notified + * Log information about the last shown advertisement * * @return ResultInterface */ @@ -97,4 +97,12 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index e2ecb700e2b4c..9ce119f0c424f 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -88,7 +88,7 @@ public function markUserNotified() } /** - * Changes the admin usage to yes, and marks user notified + * Log information about the last shown advertisement * * @return \Magento\Framework\Controller\ResultInterface */ @@ -97,4 +97,14 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } + + /** + * IsAllow allows function to be visible + * + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/app/code/Magento/AdminAnalytics/ViewModel/Notification.php b/app/code/Magento/AdminAnalytics/ViewModel/Notification.php new file mode 100644 index 0000000000000..5b4a51c5b6539 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/ViewModel/Notification.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdminAnalytics\ViewModel; + +/** + * Class Notification + */ +class Notification implements \Magento\Framework\View\Element\Block\ArgumentInterface +{ + /** + * @var \Magento\AdminAnalytics\Model\Condition\CanViewNotification + */ + private $canViewNotificationAnalytics; + + /** + * @var \Magento\ReleaseNotification\Model\Condition\CanViewNotification + */ + private $canViewNotificationRelease; + + /** + * Notification constructor. + * @param \Magento\AdminAnalytics\Model\Condition\CanViewNotification $canViewNotificationAnalytics + * @param \Magento\ReleaseNotification\Model\Condition\CanViewNotification $canViewNotificationRelease + */ + public function __construct( + \Magento\AdminAnalytics\Model\Condition\CanViewNotification $canViewNotificationAnalytics, + \Magento\ReleaseNotification\Model\Condition\CanViewNotification $canViewNotificationRelease + ) { + $this->canViewNotificationAnalytics = $canViewNotificationAnalytics; + $this->canViewNotificationRelease = $canViewNotificationRelease; + } + + /** + * Determine if the analytics popup is visible + * + * @return bool + */ + public function isAnalyticsVisible(): bool + { + return $this->canViewNotificationAnalytics->isVisible([]); + } + + /** + * Determine if the release popup is visible + * + * @return bool + */ + public function isReleaseVisible(): bool + { + return $this->canViewNotificationRelease->isVisible([]); + } +} diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json index 626e3ec14bc90..26cd4e9c6bcf4 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json @@ -1,5 +1,5 @@ { - "admin_analytics_usage_version_log": { + "admin_usage_viewer_log": { "column": { "id": true, "last_viewed_in_version": true diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 62a9dda8b090c..688bbc1e41888 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -13,14 +13,17 @@ <argument name="tracking_url" xsi:type="string">//assets.adobedtm.com/launch-EN30eb7ffa064444f1b8b0368ef38fd3a9.min.js</argument> </arguments> </block> - - </referenceContainer> <referenceContainer name="content"> - <uiComponent name="admin_usage_notification"> - <visibilityCondition name="can_view_admin_usage_notification" className="Magento\AdminAnalytics\Model\Condition\CanViewNotification"/> - </uiComponent> - </referenceContainer> + <uiComponent name="admin_usage_notification"> + <visibilityCondition name="can_view_admin_usage_notification" className="Magento\AdminAnalytics\Model\Condition\CanViewNotification"/> + </uiComponent> + <block name="tracking_notification" as="tracking_notification" template="Magento_AdminAnalytics::notification.phtml"> + <arguments> + <argument name="notification" xsi:type="object">Magento\AdminAnalytics\ViewModel\Notification</argument> + </arguments> + </block> + </referenceContainer> </body> </page> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/requirejs-config.js b/app/code/Magento/AdminAnalytics/view/adminhtml/requirejs-config.js new file mode 100644 index 0000000000000..1361210929789 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/requirejs-config.js @@ -0,0 +1,14 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + config: { + mixins: { + 'Magento_ReleaseNotification/js/modal/component': { + 'Magento_AdminAnalytics/js/release-notification/modal/component-mixin': true + } + } + } +}; diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/notification.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/notification.phtml new file mode 100644 index 0000000000000..4b1f971670184 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/notification.phtml @@ -0,0 +1,16 @@ + +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> + +<script> + define('analyticsPopupConfig', function () { + return { + analyticsVisible: <?= $block->getNotification()->isAnalyticsVisible() ? 1 : 0; ?>, + releaseVisible: <?= $block->getNotification()->isReleaseVisible() ? 1 : 0; ?>, + } + }); +</script> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 17cffb33f043c..8afa4861dd153 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -3,12 +3,14 @@ * See COPYING.txt for license details. */ -define( - [ +define([ + 'underscore', 'jquery', - 'Magento_Ui/js/modal/modal-component' - ], - function ($, Modal) { + 'Magento_Ui/js/modal/modal-component', + 'uiRegistry', + 'analyticsPopupConfig' +], + function (_, $, Modal, registry, analyticsPopupConfig) { 'use strict'; return Modal.extend( @@ -20,10 +22,10 @@ define( }, options: { keyEventHandlers: { - escapeKey: function () { - return; } + escapeKey: function () { return; } } - } + }, + notificationWindow: null, }, initModal: function () { this.options.opened = this.onOpened.bind(this); @@ -33,7 +35,6 @@ define( $('.modal-header button.action-close').hide(); }, enableAdminUsage: function () { - var data = { 'form_key': window.FORM_KEY }; @@ -51,6 +52,7 @@ define( } } ).fail(this.onError); + this.openReleasePopup(); this.closeModal(); }, disableAdminUsage: function () { @@ -71,8 +73,15 @@ define( } } ).fail(this.onError); + this.openReleasePopup(); this.closeModal(); - } + }, + openReleasePopup: function () { + if (analyticsPopupConfig.releaseVisible) { + var notificationModal = registry.get('release_notification.release_notification.notification_modal_1'); + notificationModal.initializeContentAfterAnalytics(); + } + }, } ) } diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js new file mode 100644 index 0000000000000..342046986186c --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js @@ -0,0 +1,29 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { + 'use strict'; + + var deferred = $.Deferred(); + var mixin = { + initializeContent: function () { + var initializeContent = this._super.bind(this); + if (!analyticsPopupConfig.analyticsVisible) { + initializeContent(); + } else { + deferred.then(function () { + initializeContent(); + }); + } + }, + initializeContentAfterAnalytics: function () { + deferred.resolve(); + } + }; + + return function (target) { + return target.extend(mixin); + }; +}); From ad9293979e0c2afd02000fd540f1f4a20a7c3312 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 11:46:20 -0500 Subject: [PATCH 0289/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added function that allows admin usage notification to be shown first --- .../Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml index 71c039773f6bf..4fd7fd17c57ee 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/CloseAllDialogBoxesActionGroup.xml @@ -9,6 +9,12 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CloseAllDialogBoxes"> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton1}}" visible="true" stepKey="clickCloseButtonIfVisible1"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton2}}" visible="true" stepKey="clickCloseButtonIfVisible2"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton3}}" visible="true" stepKey="clickCloseButtonIfVisible3"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton4}}" visible="true" stepKey="clickCloseButtonIfVisible4"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton5}}" visible="true" stepKey="clickCloseButtonIfVisible5"/> + <conditionalClick selector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" dependentSelector="{{AdminUsageNotificationSection.releaseNotificationCloseButton6}}" visible="true" stepKey="clickCloseButtonIfVisible6"/> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageAllowButton}}" visible="true" stepKey="clickAllowButtonIfVisible"/> </actionGroup> </actionGroups> From f1840ce0c8b42310b11c0a9e16eaf9d4129df0d1 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 15:34:43 -0500 Subject: [PATCH 0290/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Fixed static errors --- .../Adminhtml/Config/DisableAdminUsage.php | 16 ++++------------ .../Adminhtml/Config/EnableAdminUsage.php | 18 ++++-------------- .../Model/Condition/CanViewNotification.php | 4 ++-- .../etc/db_schema_whitelist.json | 2 +- .../ui_component/admin_usage_notification.xml | 2 +- .../view/adminhtml/web/js/modal/component.js | 7 +++++-- .../modal/component-mixin.js | 1 + 7 files changed, 18 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index eb31e54e8041b..82d8ac6480b0d 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -39,11 +39,11 @@ class DisableAdminUsage extends Action /** * DisableAdminUsage constructor. * - * @param Action\Context $context + * @param Action\Context $context * @param ProductMetadataInterface $productMetadata - * @param NotificationLogger $notificationLogger - * @param Factory $configFactory - * @param LoggerInterface $logger + * @param NotificationLogger $notificationLogger + * @param Factory $configFactory + * @param LoggerInterface $logger */ public function __construct( Action\Context $context, @@ -97,12 +97,4 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } - - /** - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 9ce119f0c424f..899d05d317e6f 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -39,11 +39,11 @@ class EnableAdminUsage extends Action /** * MarkUserNotified constructor. * - * @param Action\Context $context + * @param Action\Context $context * @param ProductMetadataInterface $productMetadata - * @param NotificationLogger $notificationLogger - * @param Factory $configFactory - * @param LoggerInterface $logger + * @param NotificationLogger $notificationLogger + * @param Factory $configFactory + * @param LoggerInterface $logger */ public function __construct( Action\Context $context, @@ -97,14 +97,4 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - - /** - * IsAllow allows function to be visible - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index a8a6b58bffbfc..861fb0a385f91 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -51,9 +51,9 @@ class CanViewNotification implements VisibilityConditionInterface /** * CanViewNotification constructor. * - * @param Logger $viewerLogger + * @param Logger $viewerLogger * @param ProductMetadataInterface $productMetadata - * @param CacheInterface $cacheStorage + * @param CacheInterface $cacheStorage */ public function __construct( Logger $viewerLogger, diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json index 26cd4e9c6bcf4..626e3ec14bc90 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json +++ b/app/code/Magento/AdminAnalytics/etc/db_schema_whitelist.json @@ -1,5 +1,5 @@ { - "admin_usage_viewer_log": { + "admin_analytics_usage_version_log": { "column": { "id": true, "last_viewed_in_version": true diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 7c6d1d43006de..090259af427bf 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -85,7 +85,7 @@ <item name="text" xsi:type="string" translate="true"><![CDATA[ <p>Help us improve Magento Admin by allowing us to collect usage data.</p> <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> - <p>You can learn more and opt out at any time by following the instructions in <a href="https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html" target="_blank">merchant documentation</a></p> + <p>You can learn more and opt out at any time by following the instructions in <a href="https://docs.magento.com/m2/ce/user_guide/stores/admin.html" target="_blank">merchant documentation</a></p> ]]></item> </item> </argument> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 8afa4861dd153..55335f17139af 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -38,6 +38,7 @@ define([ var data = { 'form_key': window.FORM_KEY }; + $.ajax( { type: 'POST', @@ -59,6 +60,7 @@ define([ var data = { 'form_key': window.FORM_KEY }; + $.ajax( { type: 'POST', @@ -77,12 +79,13 @@ define([ this.closeModal(); }, openReleasePopup: function () { + var notificationModal = registry.get('release_notification.release_notification.notification_modal_1'); + if (analyticsPopupConfig.releaseVisible) { - var notificationModal = registry.get('release_notification.release_notification.notification_modal_1'); notificationModal.initializeContentAfterAnalytics(); } }, } - ) + ); } ); diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js index 342046986186c..fdfb85dd11813 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js @@ -10,6 +10,7 @@ define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { var mixin = { initializeContent: function () { var initializeContent = this._super.bind(this); + if (!analyticsPopupConfig.analyticsVisible) { initializeContent(); } else { From 12f6ed91d8281780412cab46b0b8e3028f9c6b45 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 21:19:36 -0500 Subject: [PATCH 0291/2437] MC-15298: Allow admin to opt out of admin analytics tracking - corrected static errors --- .../Model/Condition/CanViewNotification.php | 31 ++--- .../Http/HttpContentProvider.php | 108 ------------------ .../Model/ContentProvider/Http/UrlBuilder.php | 64 ----------- .../Model/ResourceModel/Viewer/Logger.php | 17 ++- .../Condition/CanViewNotificationTest.php | 87 ++++++++++++++ .../DataProvider/NotificationDataProvider.php | 3 + app/code/Magento/AdminAnalytics/composer.json | 2 +- .../view/adminhtml/web/js/modal/component.js | 8 +- 8 files changed, 120 insertions(+), 200 deletions(-) delete mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php delete mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php create mode 100644 app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 861fb0a385f91..1af8702e55108 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -6,8 +6,6 @@ namespace Magento\AdminAnalytics\Model\Condition; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; -use Magento\Backend\Model\Auth\Session; -use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\View\Layout\Condition\VisibilityConditionInterface; use Magento\Framework\App\CacheInterface; use function Magento\PAT\Reports\Utils\readResponseTimeReport; @@ -31,18 +29,13 @@ class CanViewNotification implements VisibilityConditionInterface * * @var string */ - private static $cachePrefix = 'admin-usage-notification-popup-'; + private static $cachePrefix = 'admin-usage-notification-popup'; /** * @var Logger */ private $viewerLogger; - /** - * @var ProductMetadataInterface - */ - private $productMetadata; - /** * @var CacheInterface */ @@ -51,37 +44,33 @@ class CanViewNotification implements VisibilityConditionInterface /** * CanViewNotification constructor. * - * @param Logger $viewerLogger - * @param ProductMetadataInterface $productMetadata - * @param CacheInterface $cacheStorage + * @param Logger $viewerLogger + * @param CacheInterface $cacheStorage */ public function __construct( Logger $viewerLogger, - ProductMetadataInterface $productMetadata, CacheInterface $cacheStorage ) { $this->viewerLogger = $viewerLogger; - $this->productMetadata = $productMetadata; $this->cacheStorage = $cacheStorage; } /** * Validate if notification popup can be shown and set the notification flag * + * @param array $arguments Attributes from element node. * @inheritdoc */ public function isVisible(array $arguments) { - $currentProductVersion = $this->productMetadata->getVersion(); - $cacheKey = self::$cachePrefix.$currentProductVersion; + $cacheKey = self::$cachePrefix; $value = $this->cacheStorage->load($cacheKey); - if ($value != $currentProductVersion) { - $versionViewed = $this->viewerLogger->get($currentProductVersion)->getLastViewVersion(); - $versionExists = isset($versionViewed); - if ($versionExists) { - $this->cacheStorage->save($versionViewed, $cacheKey); + if ($value !== 'log-exists') { + $logExists = $this->viewerLogger->checkLogExists(); + if ($logExists) { + $this->cacheStorage->save('log-exists', $cacheKey); } - return !$versionExists; + return !$logExists; } return false; } diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php deleted file mode 100644 index 930cf89966936..0000000000000 --- a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/HttpContentProvider.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\AdminAnalytics\Model\ContentProvider\Http; - -use Magento\AdminAnalytics\Model\ContentProviderInterface; -use Magento\Setup\Module\I18n\Locale; -use Psr\Log\LoggerInterface; -use Magento\Framework\HTTP\ClientInterface; - -/** - * Requests the release notification content data via an HTTP call to a REST API - */ -class HttpContentProvider implements ContentProviderInterface -{ - /** - * @var ClientInterface - */ - private $httpClient; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * @var UrlBuilder - */ - private $urlBuilder; - - /** - * HttpContentProvider constructor. - * @param ClientInterface $httpClient - * @param UrlBuilder $urlBuilder - * @param LoggerInterface $logger - */ - public function __construct( - ClientInterface $httpClient, - UrlBuilder $urlBuilder, - LoggerInterface $logger - ) { - $this->httpClient = $httpClient; - $this->urlBuilder = $urlBuilder; - $this->logger = $logger; - } - - /** - * @inheritdoc - */ - public function getContent($version, $edition, $locale) - { - $result = false; - - try { - $result = $this->retrieveContent($version, $edition, $locale); - if (!$result) { - $result = $this->retrieveContent($version, $edition, Locale::DEFAULT_SYSTEM_LOCALE); - if (!$result) { - $result = $this->retrieveContent($version, '', 'default'); - } - } - } catch (\Exception $e) { - $this->logger->warning( - sprintf( - 'Failed to retrieve the release notification content. The response is: %s', - empty($result) ? 'Response body is empty.' : $result - ) - ); - } - - return $result; - } - - /** - * Retrieve content from given url - * - * @param string $version - * @param string $edition - * @param string $locale - * @return bool|string - */ - private function retrieveContent($version, $edition, $locale) - { - $url = $this->urlBuilder->getUrl($version, $edition, $locale); - return empty($url) ? false : $this->getResponse($url); - } - - /** - * Returns the response body from the HTTP client - * - * @param $url - * @return string - */ - private function getResponse($url) - { - $this->httpClient->get($url); - $responseBody = $this->httpClient->getBody(); - - if ($this->httpClient->getStatus() === 200 && !empty($responseBody)) { - return $responseBody; - } - - return false; - } -} diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php b/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php deleted file mode 100644 index 3898fb82f4ac5..0000000000000 --- a/app/code/Magento/AdminAnalytics/Model/ContentProvider/Http/UrlBuilder.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\AdminAnalytics\Model\ContentProvider\Http; - -use Magento\Framework\App\Config\ScopeConfigInterface; - -/** - * Builder to build Url to retrieve the notification content. - */ -class UrlBuilder -{ - /** - * Path to the configuration value which contains an URL that provides the release notification data. - * - * @var string - */ - private static $notificationContentUrlConfigPath = 'system/release_notification/content_url'; - - /** - * Path to the configuration value indicates if use https in notification content request. - * - * @var string - */ - private static $useHttpsFlagConfigPath = 'system/release_notification/use_https'; - - /** - * @var ScopeConfigInterface - */ - private $config; - - /** - * @param ScopeConfigInterface $config - */ - public function __construct(ScopeConfigInterface $config) - { - $this->config = $config; - } - - /** - * Builds the URL to request the release notification content data based on passed parameters. - * - * @param string $version - * @param string $edition - * @param string $locale - * @return string - */ - public function getUrl($version, $edition, $locale) - { - $scheme = $this->config->isSetFlag(self::$useHttpsFlagConfigPath) ? 'https://' : 'http://'; - $baseUrl = $this->config->getValue(self::$notificationContentUrlConfigPath); - if (empty($baseUrl)) { - return ''; - } else { - $url = $scheme . $baseUrl; - $url .= empty($version) ? '' : '/' . $version; - $url .= empty($edition) ? '' : '/' . $edition; - $url .= empty($locale) ? '' : '/' . $locale; - return $url . '.json'; - } - } -} diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index 6c6a76365fbbe..505257f051472 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -79,18 +79,29 @@ public function get(string $lastViewVersion) : Log return $this->logFactory->create(['data' => $this->loadLogData($lastViewVersion)]); } + /** + * Get log by the last view version. + * + * @return boolean + */ + public function checkLogExists() : bool + { + $data = $this->logFactory->create(['data' => $this->loadLogData()]); + $lastViewedVersion = $data->getLastViewVersion(); + return isset($lastViewedVersion); + } + /** * Load release notification viewer log data by last view version * - * @param string $lastViewVersion * @return array */ - private function loadLogData(string $lastViewVersion) : array + private function loadLogData() : array { $connection = $this->resource->getConnection(); $select = $connection->select() ->from($this->resource->getTableName(self::LOG_TABLE_NAME)) - ->where('last_viewed_in_version = ?', $lastViewVersion); + ->limit(['count' => 1]); $data = $connection->fetchRow($select); if (!$data) { diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php new file mode 100644 index 0000000000000..75306ddcd85ea --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AdminAnalytics\Test\Unit\Model\Condition; + +use Magento\AdminAnalytics\Model\Condition\CanViewNotification; +use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; +use Magento\AdminAnalytics\Model\Viewer\Log; +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\App\CacheInterface; + +class CanViewNotificationTest extends \PHPUnit\Framework\TestCase +{ + /** @var CanViewNotification */ + private $canViewNotification; + + /** @var Logger|\PHPUnit_Framework_MockObject_MockObject */ + private $viewerLoggerMock; + + /** @var ProductMetadataInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $productMetadataMock; + + /** @var Log|\PHPUnit_Framework_MockObject_MockObject */ + private $logMock; + + /** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */ + private $cacheStorageMock; + + public function setUp() + { + $this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class) + ->getMockForAbstractClass(); + $this->logMock = $this->getMockBuilder(Log::class) + ->getMock(); + $this->viewerLoggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new ObjectManager($this); + $this->canViewNotification = $objectManager->getObject( + CanViewNotification::class, + [ + 'viewerLogger' => $this->viewerLoggerMock, + 'productMetadata' => $this->productMetadataMock, + 'cacheStorage' => $this->cacheStorageMock, + ] + ); + } + + /** + * @param $expected + * @param $cacheResponse + * @param $logExists + * @dataProvider isVisibleProvider + */ + public function testIsVisibleLoadDataFromLog($expected, $cacheResponse, $logExists) + { + $this->cacheStorageMock->expects($this->once()) + ->method('load') + ->with('admin-usage-notification-popup') + ->willReturn($cacheResponse); + $this->viewerLoggerMock + ->method('checkLogExists') + ->willReturn($logExists); + $this->cacheStorageMock + ->method('save') + ->with('log-exists', 'admin-usage-notification-popup'); + $this->assertEquals($expected, $this->canViewNotification->isVisible([])); + } + + /** + * @return array + */ + public function isVisibleProvider() + { + return [ + [true, false, false], + [false, 'log-exists', true], + [false, false, true], + ]; + } +} diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php index a3acf279c7a9a..8b3e25e2a6d2e 100644 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php @@ -180,6 +180,7 @@ public function getRequestFieldName() */ public function addFilter(\Magento\Framework\Api\Filter $filter) { + return null; } /** @@ -187,6 +188,7 @@ public function addFilter(\Magento\Framework\Api\Filter $filter) */ public function addOrder($field, $direction) { + return null; } /** @@ -194,6 +196,7 @@ public function addOrder($field, $direction) */ public function setLimit($offset, $size) { + return null; } /** diff --git a/app/code/Magento/AdminAnalytics/composer.json b/app/code/Magento/AdminAnalytics/composer.json index 085d258c72b57..7fa6c721d44c8 100644 --- a/app/code/Magento/AdminAnalytics/composer.json +++ b/app/code/Magento/AdminAnalytics/composer.json @@ -5,7 +5,7 @@ "sort-packages": true }, "require": { - "php": "~7.1.3||~7.2.0", + "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", "magento/module-backend": "*" }, diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 55335f17139af..091098a059a99 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -22,7 +22,9 @@ define([ }, options: { keyEventHandlers: { - escapeKey: function () { return; } + escapeKey: function () { + return; + } } }, notificationWindow: null, @@ -79,10 +81,10 @@ define([ this.closeModal(); }, openReleasePopup: function () { - var notificationModal = registry.get('release_notification.release_notification.notification_modal_1'); + var notifiModal = registry.get('release_notification.release_notification.notification_modal_1'); if (analyticsPopupConfig.releaseVisible) { - notificationModal.initializeContentAfterAnalytics(); + notifiModal.initializeContentAfterAnalytics(); } }, } From 6d966b9f8998bc46db0a8b3bcea6d636d3c586db Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 31 Jul 2019 21:31:01 -0500 Subject: [PATCH 0292/2437] MC-15298: Allow admin to opt out of admin analytics tracking - corrected composer.json --- app/code/Magento/AdminAnalytics/composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/composer.json b/app/code/Magento/AdminAnalytics/composer.json index 7fa6c721d44c8..0c930560425f4 100644 --- a/app/code/Magento/AdminAnalytics/composer.json +++ b/app/code/Magento/AdminAnalytics/composer.json @@ -7,7 +7,10 @@ "require": { "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", - "magento/module-backend": "*" + "magento/module-backend": "*", + "magento/Config": "*", + "magento/Ui": "*", + "magento/ReleaseNotification": "*" }, "type": "magento2-module", "license": [ From 923198b5d1c4ae9bc053a3451b8e03eaa47d846b Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 1 Aug 2019 10:26:02 +0300 Subject: [PATCH 0293/2437] MC-18750: Failed function test Magento\FunctionalTestingFramework.functional.StorefrontUKCustomerCheckoutWithCouponTest --- .../Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index 13928dd91ec75..482e2fb6233a6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -19,7 +19,6 @@ <before> <magentoCLI command="downloadable:domains:add" arguments="example.com static.magento.com" stepKey="addDownloadableDomain"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> <createData entity="ApiDownloadableProduct" stepKey="createDownloadableProduct"> <field key="price">20.00</field> </createData> @@ -44,6 +43,7 @@ <deleteData createDataKey="virtualProduct" stepKey="deleteVirtualProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -108,6 +108,9 @@ <seeElement selector="{{StorefrontMiniCartSection.emptyMiniCart}}" stepKey="assertEmptyCart" /> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="orderId"/> + <!-- Login to Admin Page --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!-- Open Order Index Page --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForPageLoad5"/> From ae388490572ba10eddb5ba598ebc057326b4943c Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 1 Aug 2019 19:17:29 +0300 Subject: [PATCH 0294/2437] MC-18830: [FT] [MFTF] AdminCreateCustomerWithCountryUSATest fluky because of bad design --- .../Mftf/Section/AdminCustomerAddressesGridActionsSection.xml | 1 + .../Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesGridActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesGridActionsSection.xml index e743c4af66d9f..3ecbf5ff450c8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesGridActionsSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAddressesGridActionsSection.xml @@ -17,5 +17,6 @@ <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']" timeout="30"/> <element name="headerRow" type="text" selector=".admin__data-grid-header-row.row.row-gutter"/> + <element name="clearFilter" type="button" selector=".admin__data-grid-header .admin__data-grid-filters-current button.action-clear" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml index 43f2aa7f8de95..a487571c43534 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryUSATest.xml @@ -85,6 +85,7 @@ <see selector="{{AdminCustomerAddressesDefaultBillingSection.addressDetails}}" userInput="{{US_Address_CA.telephone}}" stepKey="seePhoneNumberInDefaultAddressSection"/> <!--Assert Customer Address Grid --> + <conditionalClick selector="{{AdminCustomerAddressesGridActionsSection.clearFilter}}" dependentSelector="{{AdminCustomerAddressesGridActionsSection.clearFilter}}" visible="true" stepKey="clearAddressesGridFilter"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{US_Address_CA.street}}" stepKey="seeStreetAddress"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{US_Address_CA.city}}" stepKey="seeCity"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{US_Address_CA.country}}" stepKey="seeCountry"/> From ec58fb1e25978d6866e0254eb12b31859a5787e7 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 1 Aug 2019 11:43:53 -0500 Subject: [PATCH 0295/2437] MC-17700: Downloadable Product links --- .../Patch/Data/AddDownloadableHostsConfig.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 575a6a4d7180b..8393c9f8551f2 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -9,7 +9,9 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; use Zend\Uri\Uri as UriHandler; use Magento\Framework\Url\ScopeResolverInterface; use Magento\Downloadable\Api\DomainManagerInterface as DomainManager; @@ -80,7 +82,17 @@ public function __construct( public function apply() { foreach ($this->scopeResolver->getScopes() as $scope) { - $this->addHost($scope->getBaseUrl()); + /** @var $scope Store */ + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)); } $customAdminUrl = $this->scopeConfig->getValue( From dd170ed0a01a0653ab44f43998de4c9f9597a275 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 1 Aug 2019 12:20:45 -0500 Subject: [PATCH 0296/2437] MC-17700: Downloadable Product links --- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 8393c9f8551f2..4bb0c38ce4c11 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -7,6 +7,7 @@ namespace Magento\Downloadable\Setup\Patch\Data; +use Magento\Config\Model\Config\Backend\Admin\Custom; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\UrlInterface; @@ -81,7 +82,11 @@ public function __construct( */ public function apply() { - foreach ($this->scopeResolver->getScopes() as $scope) { + $customStoreScope = $this->scopeResolver->getScope(Custom::CONFIG_SCOPE_ID); + $storeScopes = $this->scopeResolver->getScopes(); + $allStoreScopes = array_merge($storeScopes, [$customStoreScope]); + + foreach ($allStoreScopes as $scope) { /** @var $scope Store */ $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, false)); $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, true)); From 6e8b5943de72bb2a6570ec240c737e6f5aa5ff8d Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 1 Aug 2019 14:33:38 -0500 Subject: [PATCH 0297/2437] MC-17700: Downloadable Product links --- .../Patch/Data/AddDownloadableHostsConfig.php | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 4bb0c38ce4c11..958a9f58153a0 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -9,6 +9,7 @@ use Magento\Config\Model\Config\Backend\Admin\Custom; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\UrlInterface; use Magento\Store\Model\ScopeInterface; @@ -87,17 +88,7 @@ public function apply() $allStoreScopes = array_merge($storeScopes, [$customStoreScope]); foreach ($allStoreScopes as $scope) { - /** @var $scope Store */ - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, true)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, true)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, true)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)); + $this->addStoreAndWebsiteUrlsFromScope($scope); } $customAdminUrl = $this->scopeConfig->getValue( @@ -152,6 +143,42 @@ public function apply() $this->domainManager->addDomains($this->whitelist); } + /** + * Add stores and website urls from store scope + * + * @param Store $scope + */ + private function addStoreAndWebsiteUrlsFromScope(Store $scope) + { + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_WEB, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_LINK, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, true)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)); + + try { + $website = $scope->getWebsite(); + } catch (NoSuchEntityException $e) { + return; + } + + if ($website) { + $this->addHost($website->getConfig(Store::XML_PATH_SECURE_BASE_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_UNSECURE_BASE_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_SECURE_BASE_LINK_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_UNSECURE_BASE_LINK_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_SECURE_BASE_MEDIA_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_UNSECURE_BASE_MEDIA_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_SECURE_BASE_STATIC_URL)); + $this->addHost($website->getConfig(Store::XML_PATH_UNSECURE_BASE_STATIC_URL)); + } + } + /** * Add host to whitelist * @@ -159,6 +186,10 @@ public function apply() */ private function addHost($url) { + if (!is_string($url)) { + return; + } + $host = $this->uriHandler->parse($url)->getHost(); if ($host && !in_array($host, $this->whitelist)) { $this->whitelist[] = $host; From c3097eab3398cdf56a59abc9adee38e95c536a20 Mon Sep 17 00:00:00 2001 From: vital_pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Thu, 1 Aug 2019 19:39:21 +0300 Subject: [PATCH 0298/2437] MC-18822: Increase test coverage for Content functional area - Integration test for MC-11441 --- .../Newsletter/Controller/SubscriberTest.php | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php index 9dbf5c4d2a2a9..35f9cb4a5a11c 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php @@ -3,8 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Newsletter\Controller; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Data\Form\FormKey; +use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberLoader; +use Magento\Newsletter\Model\Subscriber; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\AbstractController; @@ -13,11 +20,6 @@ */ class SubscriberTest extends AbstractController { - protected function setUp() - { - parent::setUp(); - } - public function testNewAction() { $this->getRequest()->setMethod('POST'); @@ -34,9 +36,7 @@ public function testNewAction() public function testNewActionUnusedEmail() { $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue([ - 'email' => 'not_used@example.com', - ]); + $this->getRequest()->setPostValue(['email' => 'not_used@example.com']); $this->dispatch('newsletter/subscriber/new'); @@ -50,15 +50,11 @@ public function testNewActionUnusedEmail() public function testNewActionUsedEmail() { $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue([ - 'email' => 'customer@example.com', - ]); + $this->getRequest()->setPostValue(['email' => 'customer@example.com']); $this->dispatch('newsletter/subscriber/new'); - $this->assertSessionMessages($this->equalTo([ - 'Thank you for your subscription.', - ])); + $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.'])); $this->assertRedirect($this->anything()); } @@ -68,9 +64,7 @@ public function testNewActionUsedEmail() public function testNewActionOwnerEmail() { $this->getRequest()->setMethod('POST'); - $this->getRequest()->setPostValue([ - 'email' => 'customer@example.com', - ]); + $this->getRequest()->setPostValue(['email' => 'customer@example.com']); $this->login(1); $this->dispatch('newsletter/subscriber/new'); @@ -79,6 +73,65 @@ public function testNewActionOwnerEmail() $this->assertRedirect($this->anything()); } + /** + * Check that Customer still subscribed for newsletters emails after registration. + * + * @magentoConfigFixture ccustomer/create/account_confirm 1 + */ + public function testCreatePosWithSubscribeEmailAction() + { + $subscriber = Bootstrap::getObjectManager()->create(Subscriber::class); + $customerEmail = 'subscribeemail@example.com'; + // Subscribe by email + $subscriber->subscribe($customerEmail); + $subscriber->loadByEmail($customerEmail); + $subscriber->confirm($subscriber->getSubscriberConfirmCode()); + + // Create customer + $this->fillRequestWithAccountDataAndFormKey($customerEmail); + $this->dispatch('customer/account/createPost'); + $this->dispatch('customer/account/confirm'); + + $customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $customerRepository->get($customerEmail); + $subscriberResource = Bootstrap::getObjectManager() + ->create(SubscriberLoader::class); + + // check customer subscribed to newsletter + $this->assertTrue($subscriberResource->loadByCustomerData($customer)['subscriber_status'] === "1"); + } + + /** + * Customer Data. + * + * @param string $email + * @return void + */ + private function fillRequestWithAccountDataAndFormKey($email) + { + Bootstrap::getObjectManager()->get(RequestInterface::class) + ->setMethod('POST') + ->setParam('firstname', 'firstname1') + ->setParam('lastname', 'lastname1') + ->setParam('company', '') + ->setParam('email', $email) + ->setParam('password', '_Password1') + ->setParam('password_confirmation', '_Password1') + ->setParam('telephone', '5123334444') + ->setParam('street', ['1234 fake street', '']) + ->setParam('city', 'Austin') + ->setParam('region_id', 57) + ->setParam('region', '') + ->setParam('postcode', '78701') + ->setParam('country_id', 'US') + ->setParam('default_billing', '1') + ->setParam('default_shipping', '1') + ->setParam('is_subscribed', '0') + ->setPostValue('create_address', true) + ->setParam('form_key', Bootstrap::getObjectManager()->get(FormKey::class)->getFormKey()); + } + /** * Login the user * From d713dac0293adc342826e2e6999b4b770a662166 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 1 Aug 2019 16:29:17 -0500 Subject: [PATCH 0299/2437] MC-18115: Email template preview bugfix --- .../Block/Adminhtml/Template/Preview.php | 12 +- .../Adminhtml/Email/Template/Popup.php | 4 +- .../Adminhtml/Email/Template/Preview.php | 9 +- .../Block/Adminhtml/Template/PreviewTest.php | 21 --- .../Adminhtml/Email/Template/PreviewTest.php | 15 +- .../ViewModel/Template/Preview/FormTest.php | 154 ++++++++++++++++++ .../Email/ViewModel/Template/Preview/Form.php | 71 ++++++++ .../adminhtml_email_template_preview.xml | 6 +- .../templates/preview/iframeswitcher.phtml | 31 +++- .../adminhtml/templates/template/edit.phtml | 3 +- 10 files changed, 268 insertions(+), 58 deletions(-) create mode 100644 app/code/Magento/Email/Test/Unit/ViewModel/Template/Preview/FormTest.php create mode 100644 app/code/Magento/Email/ViewModel/Template/Preview/Form.php diff --git a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php index 5fabc584403bd..ec5596e2194a1 100644 --- a/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Email/Block/Adminhtml/Template/Preview.php @@ -3,12 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Adminhtml system template preview block - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Email\Block\Adminhtml\Template; /** @@ -55,16 +51,12 @@ public function __construct( * Prepare html output * * @return string - * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Exception */ protected function _toHtml() { $request = $this->getRequest(); - if (!$request instanceof \Magento\Framework\App\RequestSafetyInterface || !$request->isSafeMethod()) { - throw new \Magento\Framework\Exception\LocalizedException(__('Wrong request.')); - } - $storeId = $this->getAnyStoreView()->getId(); /** @var $template \Magento\Email\Model\Template */ $template = $this->_emailFactory->create(); diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Popup.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Popup.php index 31d172935da7f..4f36eedd09b83 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Popup.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Popup.php @@ -7,12 +7,12 @@ namespace Magento\Email\Controller\Adminhtml\Email\Template; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * Rendering popup email template. */ -class Popup extends \Magento\Backend\App\Action implements HttpGetActionInterface +class Popup extends \Magento\Backend\App\Action implements HttpPostActionInterface { /** * @var \Magento\Framework\View\Result\PageFactory diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php index c1a8eec07e461..a92836b2995a2 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Preview.php @@ -4,19 +4,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Email\Controller\Adminhtml\Email\Template; +use Magento\Email\Controller\Adminhtml\Email\Template; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * Rendering email template preview. */ -class Preview extends \Magento\Email\Controller\Adminhtml\Email\Template implements HttpGetActionInterface +class Preview extends Template implements HttpGetActionInterface, HttpPostActionInterface { /** * Preview transactional email action. - * - * @return void */ public function execute() { @@ -24,7 +26,6 @@ public function execute() $this->_view->loadLayout(); $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Email Preview')); $this->_view->renderLayout(); - $this->getResponse()->setHeader('Content-Security-Policy', "script-src 'self'"); } catch (\Exception $e) { $this->messageManager->addErrorMessage( __('An error occurred. The email template can not be opened for preview.') diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php index 83eda7e39a810..4d168ffbf2bdc 100644 --- a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/PreviewTest.php @@ -152,9 +152,6 @@ protected function setUp() */ public function testToHtml($requestParamMap) { - $this->request->expects($this->atLeastOnce()) - ->method('isSafeMethod') - ->willReturn(true); $this->request->expects($this->any()) ->method('getParam') ->willReturnMap($requestParamMap); @@ -171,24 +168,6 @@ public function testToHtml($requestParamMap) $this->assertEquals(self::MALICIOUS_TEXT, $this->preview->toHtml()); } - /** - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testToHtmlWithException() - { - $this->request->expects($this->atLeastOnce()) - ->method('isSafeMethod') - ->willReturn(false); - $this->template - ->expects($this->never()) - ->method('getDesignConfig'); - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); - $this->expectExceptionMessage( - (string)__('Wrong request.') - ); - $this->preview->toHtml(); - } - /** * Data provider * diff --git a/app/code/Magento/Email/Test/Unit/Controller/Adminhtml/Email/Template/PreviewTest.php b/app/code/Magento/Email/Test/Unit/Controller/Adminhtml/Email/Template/PreviewTest.php index 9a67bf59dd4bf..d4584ce86dff2 100644 --- a/app/code/Magento/Email/Test/Unit/Controller/Adminhtml/Email/Template/PreviewTest.php +++ b/app/code/Magento/Email/Test/Unit/Controller/Adminhtml/Email/Template/PreviewTest.php @@ -54,11 +54,6 @@ class PreviewTest extends \PHPUnit\Framework\TestCase */ protected $pageTitleMock; - /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $responseMock; - protected function setUp() { $objectManager = new ObjectManager($this); @@ -84,16 +79,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class) - ->setMethods(['setHeader']) - ->getMockForAbstractClass(); - $this->context = $objectManager->getObject( \Magento\Backend\App\Action\Context::class, [ 'request' => $this->requestMock, - 'view' => $this->viewMock, - 'response' => $this->responseMock + 'view' => $this->viewMock ] ); $this->object = $objectManager->getObject( @@ -118,9 +108,6 @@ public function testExecute() $this->pageTitleMock->expects($this->once()) ->method('prepend') ->willReturnSelf(); - $this->responseMock->expects($this->once()) - ->method('setHeader') - ->with('Content-Security-Policy', "script-src 'self'"); $this->assertNull($this->object->execute()); } diff --git a/app/code/Magento/Email/Test/Unit/ViewModel/Template/Preview/FormTest.php b/app/code/Magento/Email/Test/Unit/ViewModel/Template/Preview/FormTest.php new file mode 100644 index 0000000000000..88c323c923c45 --- /dev/null +++ b/app/code/Magento/Email/Test/Unit/ViewModel/Template/Preview/FormTest.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Email\Test\Unit\ViewModel\Template\Preview; + +use Magento\Email\ViewModel\Template\Preview\Form; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Class FormTest + * + * @covers \Magento\Email\ViewModel\Template\Preview\Form + */ +class FormTest extends \PHPUnit\Framework\TestCase +{ + /** @var Form */ + protected $form; + + /** @var Http|\PHPUnit_Framework_MockObject_MockObject */ + protected $requestMock; + + protected function setUp() + { + $this->requestMock = $this->createPartialMock( + Http::class, + ['getParam', 'getMethod'] + ); + + $objectManagerHelper = new ObjectManager($this); + + $this->form = $objectManagerHelper->getObject( + Form::class, + ['request'=> $this->requestMock] + ); + } + + /** + * Tests that the form is created with the expected fields based on the request type. + * + * @dataProvider getFormFieldsDataProvider + * @param string $httpMethod + * @param array $httpParams + * @param array $expectedFields + * @throws LocalizedException + */ + public function testGetFormFields(string $httpMethod, array $httpParams, array $expectedFields) + { + $this->requestMock->expects($this->once()) + ->method('getMethod') + ->willReturn($httpMethod); + + $this->requestMock->expects($this->any()) + ->method('getParam') + ->willReturnMap($httpParams); + + $actualFields = $this->form->getFormFields(); + + $this->assertEquals($expectedFields, $actualFields); + } + + /** + * Tests that an exception is thrown when a required parameter is missing for the request type. + * + * @dataProvider getFormFieldsInvalidDataProvider + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Missing expected parameter + * @param string $httpMethod + * @param array $httpParams + */ + public function testGetFormFieldsMissingParameter(string $httpMethod, array $httpParams) + { + $this->requestMock->expects($this->once()) + ->method('getMethod') + ->willReturn($httpMethod); + + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturnMap($httpParams); + + $this->form->getFormFields(); + } + + /** + * @return array + */ + public function getFormFieldsDataProvider() + { + return [ + 'get_request_valid' => [ + 'httpMethod' => 'GET', + 'httpParams' => [ + ['id', null, 1] + ], + 'expectedFields' => [ + 'id' => 1 + ] + ], + 'get_request_valid_ignore_params' => [ + 'httpMethod' => 'GET', + 'httpParams' => [ + ['id', null, 1], + ['text', null, 'Hello World'], + ['type', null, 2], + ['styles', null, ''] + ], + 'expectedFields' => [ + 'id' => 1 + ] + ], + 'post_request_valid' => [ + 'httpMethod' => 'POST', + 'httpParams' => [ + ['text', null, 'Hello World'], + ['type', null, 2], + ['styles', null, ''] + ], + 'expectedFields' => [ + 'text' => 'Hello World', + 'type' => 2, + 'styles' => '' + ] + ] + ]; + } + + /** + * @return array + */ + public function getFormFieldsInvalidDataProvider() + { + return [ + 'get_request_missing_id' => [ + 'httpMethod' => 'GET', + 'httpParams' => [ + ['text', null, 'Hello World'], + ['type', null, 2], + ['styles', null, ''] + ] + ], + 'post_request_missing_text' => [ + 'httpMethod' => 'POST', + 'httpParams' => [ + ['type', null, 2], + ['styles', null, ''] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Email/ViewModel/Template/Preview/Form.php b/app/code/Magento/Email/ViewModel/Template/Preview/Form.php new file mode 100644 index 0000000000000..9db93cb94a299 --- /dev/null +++ b/app/code/Magento/Email/ViewModel/Template/Preview/Form.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Email\ViewModel\Template\Preview; + +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * Class Form + */ +class Form implements ArgumentInterface +{ + private $expectedParamsGetRequest = [ + 'id' + ]; + + private $expectedParamsPostRequest = [ + 'text', + 'type', + 'styles' + ]; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @param RequestInterface $request + */ + public function __construct(RequestInterface $request) + { + $this->request = $request; + } + + /** + * Gets the fields to be included in the email preview form. + * + * @return array + * @throws LocalizedException + */ + public function getFormFields() + { + $params = $fields = []; + $method = $this->request->getMethod(); + + if ($method === 'GET') { + $params = $this->expectedParamsGetRequest; + } elseif ($method === 'POST') { + $params = $this->expectedParamsPostRequest; + } + + foreach ($params as $paramName) { + $fieldValue = $this->request->getParam($paramName); + if ($fieldValue === null) { + throw new LocalizedException( + __("Missing expected parameter \"$paramName\" while attempting to generate template preview.") + ); + } + $fields[$paramName] = $fieldValue; + } + + return $fields; + } +} diff --git a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml index 97f31c618f9b7..e7cbc675ce386 100644 --- a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml +++ b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml @@ -12,7 +12,11 @@ <referenceContainer name="backend.page" remove="true"/> <referenceContainer name="menu.wrapper" remove="true"/> <referenceContainer name="root"> - <block name="preview.page.content" class="Magento\Backend\Block\Page" template="Magento_Email::preview/iframeswitcher.phtml"/> + <block name="preview.page.content" class="Magento\Backend\Block\Page" template="Magento_Email::preview/iframeswitcher.phtml"> + <arguments> + <argument name="preview_form_view_model" xsi:type="object">Magento\Email\ViewModel\Template\Preview\Form</argument> + </arguments> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Email/view/adminhtml/templates/preview/iframeswitcher.phtml b/app/code/Magento/Email/view/adminhtml/templates/preview/iframeswitcher.phtml index 4d26b59b093e2..29ceb71a138e4 100644 --- a/app/code/Magento/Email/view/adminhtml/templates/preview/iframeswitcher.phtml +++ b/app/code/Magento/Email/view/adminhtml/templates/preview/iframeswitcher.phtml @@ -7,13 +7,34 @@ /** @var \Magento\Backend\Block\Page $block */ ?> <div id="preview" class="cms-revision-preview"> - <iframe - name="preview_iframe" + <iframe name="preview_iframe" id="preview_iframe" frameborder="0" title="<?= $block->escapeHtmlAttr(__('Preview')) ?>" width="100%" - sandbox="allow-forms allow-pointer-lock" - src="<?= $block->escapeUrl($block->getUrl('*/*/popup', ['_current' => true])) ?>" - /> + sandbox="allow-same-origin allow-pointer-lock" + ></iframe> + <form id="preview_form" + action="<?= $block->escapeUrl($block->getUrl('*/*/popup')) ?>" + method="post" + target="preview_iframe" + > + <input type="hidden" name="form_key" value="<?= /* @noEscape */ $block->getFormKey() ?>" /> + <?php foreach ($block->getPreviewFormViewModel()->getFormFields() as $name => $value) : ?> + <input type="hidden" name="<?= $block->escapeHtmlAttr($name) ?>" value="<?= $block->escapeHtmlAttr($value) ?>"/> + <?php endforeach; ?> + </form> </div> +<script> +require([ + 'jquery' +], function($) { + $(document).ready(function() { + $('#preview_form').submit(); + }); + + $('#preview_iframe').load(function() { + $(this).height($(this).contents().height()); + }); +}); +</script> diff --git a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml index 9653156e85e80..d99a2703e3f9c 100644 --- a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml @@ -48,7 +48,7 @@ use Magento\Framework\App\TemplateTypesInterface; <?= /* @noEscape */ $block->getFormHtml() ?> </form> -<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="get" id="email_template_preview_form" target="_blank"> +<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="post" id="email_template_preview_form" target="_blank"> <?= /* @noEscape */ $block->getBlockHtml('formkey') ?> <div class="no-display"> <input type="hidden" id="preview_type" name="type" value="<?= /* @noEscape */ $block->isTextType() ? 1 : 2 ?>" /> @@ -148,6 +148,7 @@ require([ } else { $('preview_type').value = <?= (int) $block->getTemplateType() ?>; } + if (typeof tinyMCE == 'undefined' || !tinyMCE.get('template_text')) { $('preview_text').value = $('template_text').value; } else { From 2f1860b9876094d7933bac3cf2ab7595f4bca0a4 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Thu, 1 Aug 2019 16:37:22 -0500 Subject: [PATCH 0300/2437] MC-17648: Registration email confirmation bugfix --- .../Customer/Api/AccountManagementMeTest.php | 27 ++++++++++--- .../Customer/Api/AccountManagementTest.php | 38 +++++++++++-------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php index 31894c1332ad5..3c5a5a6770f5a 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php @@ -215,6 +215,15 @@ public function testGetCustomerData() public function testGetCustomerActivateCustomer() { + // Update the customer's confirmation key to a known value + $this->customerData = $this->customerHelper->updateSampleCustomer( + $this->customerData[Customer::ID], + [ + 'id' => $this->customerData[Customer::ID], + 'confirmation' => CustomerHelper::CONFIRMATION + ] + ); + $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/activate', @@ -228,14 +237,22 @@ public function testGetCustomerActivateCustomer() 'token' => $this->token ] ]; - $requestData = ['confirmationKey' => $this->customerData[CustomerInterface::CONFIRMATION]]; + + $requestData = ['confirmationKey' => CustomerHelper::CONFIRMATION]; if (TESTS_WEB_API_ADAPTER === 'soap') { $requestData['customerId'] = 0; } - $customerResponseData = $this->_webApiCall($serviceInfo, $requestData); - $this->assertEquals($this->customerData[CustomerInterface::ID], $customerResponseData[CustomerInterface::ID]); - // Confirmation key is removed after confirmation - $this->assertFalse(isset($customerResponseData[CustomerInterface::CONFIRMATION])); + + try { + $customerResponseData = $this->_webApiCall($serviceInfo, $requestData); + $this->assertEquals( + $this->customerData[CustomerInterface::ID], + $customerResponseData[CustomerInterface::ID] + ); + } catch (\Exception $e) { + $this->fail('Customer is not activated.'); + } + } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php index b2276d79f5ecf..a93bbcbdf04b2 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementTest.php @@ -249,7 +249,15 @@ public function testCreateCustomerWithoutOptionalFields() public function testActivateCustomer() { $customerData = $this->_createCustomer(); - $this->assertNotNull($customerData[Customer::CONFIRMATION], 'Customer activation is not required'); + + // Update the customer's confirmation key to a known value + $customerData = $this->customerHelper->updateSampleCustomer( + $customerData[Customer::ID], + [ + 'id' => $customerData[Customer::ID], + 'confirmation' => CustomerHelper::CONFIRMATION + ] + ); $serviceInfo = [ 'rest' => [ @@ -265,16 +273,15 @@ public function testActivateCustomer() $requestData = [ 'email' => $customerData[Customer::EMAIL], - 'confirmationKey' => $customerData[Customer::CONFIRMATION], + 'confirmationKey' => CustomerHelper::CONFIRMATION ]; - $result = $this->_webApiCall($serviceInfo, $requestData); - - $this->assertEquals($customerData[Customer::ID], $result[Customer::ID], 'Wrong customer!'); - $this->assertTrue( - !isset($result[Customer::CONFIRMATION]) || $result[Customer::CONFIRMATION] === null, - 'Customer is not activated!' - ); + try { + $result = $this->_webApiCall($serviceInfo, $requestData); + $this->assertEquals($customerData[Customer::ID], $result[Customer::ID], 'Wrong customer!'); + } catch (\Exception $e) { + $this->fail('Customer is not activated.'); + } } public function testGetCustomerActivateCustomer() @@ -294,14 +301,15 @@ public function testGetCustomerActivateCustomer() ]; $requestData = [ 'email' => $customerData[Customer::EMAIL], - 'confirmationKey' => $customerData[Customer::CONFIRMATION], + 'confirmationKey' => CustomerHelper::CONFIRMATION ]; - $customerResponseData = $this->_webApiCall($serviceInfo, $requestData); - - $this->assertEquals($customerData[Customer::ID], $customerResponseData[Customer::ID]); - // Confirmation key is removed after confirmation - $this->assertFalse(isset($customerResponseData[Customer::CONFIRMATION])); + try { + $customerResponseData = $this->_webApiCall($serviceInfo, $requestData); + $this->assertEquals($customerData[Customer::ID], $customerResponseData[Customer::ID]); + } catch (\Exception $e) { + $this->fail('Customer is not activated.'); + } } public function testValidateResetPasswordLinkToken() From a282e764dcaaa44942dba29835aa5b6efd3745d5 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Thu, 1 Aug 2019 16:56:56 -0500 Subject: [PATCH 0301/2437] MC-17648: Registration email confirmation bugfix * Removed extra newline --- .../testsuite/Magento/Customer/Api/AccountManagementMeTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php index 3c5a5a6770f5a..f3c981f74b83f 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php @@ -252,7 +252,6 @@ public function testGetCustomerActivateCustomer() } catch (\Exception $e) { $this->fail('Customer is not activated.'); } - } /** From a1cedf1558e7b23583aa4968acdbcd4958f9355a Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Fri, 2 Aug 2019 10:09:38 +0300 Subject: [PATCH 0302/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../Sales/Model/Order/ShipmentTest.php | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 1d04a79ae3f84..9c0024480d164 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -117,9 +117,12 @@ public function testAddComment() $saved = $this->shipmentRepository->save($shipment); $comments = $saved->getComments(); - $actual = array_map(function (CommentInterface $comment) { - return $comment->getComment(); - }, $comments); + $actual = array_map( + function (CommentInterface $comment) { + return $comment->getComment(); + }, + $comments + ); self::assertEquals(2, count($actual)); self::assertEquals([$message1, $message2], $actual); } @@ -144,4 +147,33 @@ private function getOrder(string $incrementId): OrderInterface return array_pop($items); } + + /** + * Check that getTracksCollection() returns only order related tracks. + */ + public function testGetTracksCollection() + { + $order = $this->getOrder('100000001'); + $items = []; + foreach ($order->getItems() as $item) { + $items[$item->getId()] = $item->getQtyOrdered(); + } + /** @var \Magento\Sales\Model\Order\Shipment $shipment */ + $shipment = $this->objectManager->get(ShipmentFactory::class) + ->create($order, $items); + + $tracks = $shipment->getTracksCollection(); + self::assertTrue(empty($tracks->getItems())); + + /** @var ShipmentTrackInterface $track */ + $track = $this->objectManager->create(ShipmentTrackInterface::class); + $track->setNumber('Test Number') + ->setTitle('Test Title') + ->setCarrierCode('Test CODE'); + + $shipment->addTrack($track); + $this->shipmentRepository->save($shipment); + $saved = $shipment->getTracksCollection(); + self::assertTrue(in_array($track->getId(), $saved->getColumnValues('id'))); + } } From d18dfe8554e158282b59870c75f64bdef3492301 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 2 Aug 2019 08:39:13 -0500 Subject: [PATCH 0303/2437] MC-17700: Downloadable Product links --- .../Setup/Patch/Data/AddDownloadableHostsConfig.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php index 958a9f58153a0..0e88bd166b604 100644 --- a/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php +++ b/app/code/Magento/Downloadable/Setup/Patch/Data/AddDownloadableHostsConfig.php @@ -158,8 +158,11 @@ private function addStoreAndWebsiteUrlsFromScope(Store $scope) $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_DIRECT_LINK, true)); $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, false)); $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_MEDIA, true)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false)); - $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)); + + try { + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, false)); + $this->addHost($scope->getBaseUrl(UrlInterface::URL_TYPE_STATIC, true)); + } catch (\UnexpectedValueException $e) {} //@codingStandardsIgnoreLine try { $website = $scope->getWebsite(); From d1144d42126167b284edf08e859ac87bb3766df3 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Fri, 2 Aug 2019 08:49:02 -0500 Subject: [PATCH 0304/2437] MC-17648: Registration email confirmation bugfix --- .../Magento/Customer/Api/AccountManagementMeTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php index f3c981f74b83f..808906c64a5b2 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php @@ -217,9 +217,9 @@ public function testGetCustomerActivateCustomer() { // Update the customer's confirmation key to a known value $this->customerData = $this->customerHelper->updateSampleCustomer( - $this->customerData[Customer::ID], + $this->customerData[CustomerInterface::ID], [ - 'id' => $this->customerData[Customer::ID], + 'id' => $this->customerData[CustomerInterface::ID], 'confirmation' => CustomerHelper::CONFIRMATION ] ); From 8334ad0daa529f5345c536112b9194c6d2f73683 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Fri, 2 Aug 2019 09:09:38 -0500 Subject: [PATCH 0305/2437] MC-17648: Registration email confirmation bugfix --- .../Magento/Customer/Api/AccountManagementMeTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php index 808906c64a5b2..88bb3a8d59afd 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/AccountManagementMeTest.php @@ -215,15 +215,6 @@ public function testGetCustomerData() public function testGetCustomerActivateCustomer() { - // Update the customer's confirmation key to a known value - $this->customerData = $this->customerHelper->updateSampleCustomer( - $this->customerData[CustomerInterface::ID], - [ - 'id' => $this->customerData[CustomerInterface::ID], - 'confirmation' => CustomerHelper::CONFIRMATION - ] - ); - $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . '/activate', From e62450604e7742381e2358400bb197f49035e4bb Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Fri, 2 Aug 2019 22:59:59 -0700 Subject: [PATCH 0306/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Updated composer, DisableAdminUsage, EnableAdminUsage --- .../Adminhtml/Config/DisableAdminUsage.php | 8 + .../Adminhtml/Config/EnableAdminUsage.php | 8 + app/code/Magento/AdminAnalytics/composer.json | 6 +- package-lock.json | 861 ++++++++++++++++++ 4 files changed, 880 insertions(+), 3 deletions(-) create mode 100644 package-lock.json diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 82d8ac6480b0d..a5bf4640c6504 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -97,4 +97,12 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 899d05d317e6f..397176f37b9d6 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -97,4 +97,12 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } + + /** + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/app/code/Magento/AdminAnalytics/composer.json b/app/code/Magento/AdminAnalytics/composer.json index 0c930560425f4..0b977a23ad3ca 100644 --- a/app/code/Magento/AdminAnalytics/composer.json +++ b/app/code/Magento/AdminAnalytics/composer.json @@ -8,9 +8,9 @@ "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", "magento/module-backend": "*", - "magento/Config": "*", - "magento/Ui": "*", - "magento/ReleaseNotification": "*" + "magento/module-config": "*", + "magento/module-ui": "*", + "magento/module-release-notification": "*" }, "type": "magento2-module", "license": [ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000..b3e14ddab5aa6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,861 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "acorn": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", + "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==" + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz", + "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==", + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^6.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", + "integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==", + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + }, + "espree": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", + "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inquirer": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", + "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz", + "integrity": "sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==", + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "requires": { + "mkdirp": "^0.5.1" + } + } + } +} From a70b6c9bb88109300d8f89cd3b93661a8500a238 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Sat, 3 Aug 2019 15:16:12 -0700 Subject: [PATCH 0307/2437] MC-15298: Allow admin to opt out of admin analytics tracking - edited DisableAdmin, EnableAdmin, admin_usage_notification --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 2 ++ .../Controller/Adminhtml/Config/EnableAdminUsage.php | 2 ++ ...nDataProvider.php => AdminUsageNotificationDataProvider.php} | 2 +- .../view/adminhtml/ui_component/admin_usage_notification.xml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) rename app/code/Magento/AdminAnalytics/Ui/DataProvider/{NotificationDataProvider.php => AdminUsageNotificationDataProvider.php} (98%) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index a5bf4640c6504..16667737f59a3 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -99,6 +99,8 @@ public function execute() } /** + * Determines if its allowed + * * @return bool */ protected function _isAllowed() diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 397176f37b9d6..9d68121c147a4 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -99,6 +99,8 @@ public function execute() } /** + * Determines if its allowed + * * @return bool */ protected function _isAllowed() diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php similarity index 98% rename from app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php rename to app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php index 8b3e25e2a6d2e..9ab7c8b2c501e 100644 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php @@ -15,7 +15,7 @@ /** * Data Provider for the Admin usage UI component. */ -class NotificationDataProvider implements DataProviderInterface +class AdminUsageNotificationDataProvider implements DataProviderInterface { /** * @var PoolInterface diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 090259af427bf..5547946eb66c1 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -37,7 +37,7 @@ <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> </item> </argument> - <dataProvider class="Magento\AdminAnalytics\Ui\DataProvider\NotificationDataProvider" name="admin_usage_notification_data_source"> + <dataProvider class="Magento\AdminAnalytics\Ui\DataProvider\AdminUsageNotificationDataProvider" name="admin_usage_notification_data_source"> <settings> <requestFieldName>id</requestFieldName> <primaryFieldName>entity_id</primaryFieldName> From 1a4a5066c3f3f8b0ad12b17e68ea617ccac9c119 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Mon, 5 Aug 2019 11:56:24 +0300 Subject: [PATCH 0308/2437] MC-15577: Fix url output --- lib/internal/Magento/Framework/Escaper.php | 25 ++++++++++++++++++- .../Framework/Test/Unit/EscaperTest.php | 23 +++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index cb4636471cd75..5fac9633bb372 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -28,6 +28,11 @@ class Escaper */ private $logger; + /** + * @var \Magento\Framework\Translate\InlineInterface + */ + private $translateInline; + /** * @var string[] */ @@ -335,8 +340,11 @@ public function escapeJsQuote($data, $quote = '\'') */ public function escapeXssInUrl($data) { + $data = (string)$data; + $this->getTranslateInline()->processResponseBody($data); + return htmlspecialchars( - $this->escapeScriptIdentifiers((string)$data), + $this->escapeScriptIdentifiers($data), $this->htmlSpecialCharsFlag | ENT_HTML5 | ENT_HTML401, 'UTF-8', false @@ -430,4 +438,19 @@ private function filterProhibitedTags(array $allowedTags): array return $allowedTags; } + + /** + * Resolve inline translator. + * + * @return \Magento\Framework\Translate\InlineInterface + */ + private function getTranslateInline() + { + if ($this->translateInline === null) { + $this->translateInline = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Translate\InlineInterface::class); + } + + return $this->translateInline; + } } diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index bc12b0bd1227c..802477c0ee7bf 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -7,6 +7,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Escaper; +use Magento\Framework\Translate\Inline; /** * \Magento\Framework\Escaper test case @@ -16,26 +17,40 @@ class EscaperTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Framework\Escaper */ - protected $escaper = null; + protected $escaper; /** * @var \Magento\Framework\ZendEscaper */ private $zendEscaper; + /** + * @var Inline + */ + private $translateInline; + /** * @var \Psr\Log\LoggerInterface */ private $loggerMock; + /** + * @inheritdoc + */ protected function setUp() { + $objectManagerHelper = new ObjectManager($this); $this->escaper = new Escaper(); $this->zendEscaper = new \Magento\Framework\ZendEscaper(); + $this->translateInline = $objectManagerHelper->getObject(Inline::class); $this->loggerMock = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class); - $objectManagerHelper = new ObjectManager($this); $objectManagerHelper->setBackwardCompatibleProperty($this->escaper, 'escaper', $this->zendEscaper); $objectManagerHelper->setBackwardCompatibleProperty($this->escaper, 'logger', $this->loggerMock); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->escaper, + 'translateInline', + $this->translateInline + ); } /** @@ -390,6 +405,10 @@ public function escapeDataProvider() 'http://test.com/?redirect=\x64\x61\x74\x61\x3a\x74\x65\x78\x74x2cCPHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg', 'http://test.com/?redirect=:\x74\x65\x78\x74x2cCPHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg', ], + [ + 'http://test.com/?redirect={{{j}}{{j}}{{j}}{{j}}}avascript:alert(1)', + 'http://test.com/?redirect=:alert(1)', + ], ]; } } From 004db8edd825a3fd8798c89ed52885d66fcf579d Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 08:37:25 -0500 Subject: [PATCH 0309/2437] MC-15298: Allow admin to opt out of admin analytics tracking - edited DisableAdmin, EnableAdmin --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 10 ---------- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 16667737f59a3..82d8ac6480b0d 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -97,14 +97,4 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } - - /** - * Determines if its allowed - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 9d68121c147a4..899d05d317e6f 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -97,14 +97,4 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - - /** - * Determines if its allowed - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } From 3d1721e6a948727c96009544bb58685f30388b86 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 08:51:39 -0500 Subject: [PATCH 0310/2437] MC-15298: Allow admin to opt out of admin analytics tracking - edited CanViewNotificationTest's namespace --- .../Test/Unit/Condition/CanViewNotificationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php index 75306ddcd85ea..00309f9527015 100644 --- a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\AdminAnalytics\Test\Unit\Model\Condition; +namespace Magento\AdminAnalytics\Test\Unit\Condition; use Magento\AdminAnalytics\Model\Condition\CanViewNotification; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; From 4f660a1d2def3cb6030b23afff4c0acc01ad1080 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Mon, 5 Aug 2019 17:22:59 +0300 Subject: [PATCH 0311/2437] Add action group to configure 'Maximum Login Failures to Lockout Account' --- .../AdminExpandSecurityTabActionGroup.xml | 16 +++++++++++++++ ... AdminOpenAdminSectionPageActionGroup.xml} | 4 ++-- ...ginFailuresToLockoutAccountActionGroup.xml | 20 +++++++++++++++++++ .../AdminConfigurationAdminSectionPage.xml | 2 +- .../Config/Test/Mftf/Section/AdminSection.xml | 2 ++ .../Mftf/Test/LockAdminUserEntityTest.xml | 6 +++++- 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml rename app/code/Magento/Config/Test/Mftf/ActionGroup/{AdminOpenConfigurationPageActionGroup.xml => AdminOpenAdminSectionPageActionGroup.xml} (70%) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml new file mode 100644 index 0000000000000..76a1c9291f4e8 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandSecurityTabActionGroup"> + <conditionalClick selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true" stepKey="openSecurityTab"/> + </actionGroup> +</actionGroups> + + diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml similarity index 70% rename from app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml rename to app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml index 69972ea238b5e..df78c374623a1 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigurationPageActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml @@ -7,8 +7,8 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenConfigurationPageActionGroup"> - <amOnPage url="{{AdminConfigurationAdminSectionPage.url}}" stepKey="goToConfigurationPage"/> + <actionGroup name="AdminOpenAdminSectionPageActionGroup"> + <amOnPage url="{{AdminEditAdminSectionPage.url}}" stepKey="goToConfigurationPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml new file mode 100644 index 0000000000000..02c528009ea87 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup"> + <arguments> + <argument name="qty" type="string" defaultValue="5"/> + </arguments> + <uncheckOption selector="{{AdminSection.systemValueForMaximumLoginFailures}}" stepKey="uncheckUseSystemValue"/> + <fillField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="setMaximumLoginFailures"/> + <seeInField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="seeNewValueInField"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml index fe704162ddac7..02879ad1fc708 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminConfigurationAdminSectionPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> + <page name="AdminEditAdminSectionPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> <section name="AdminSection"/> </page> </pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml index 7b6c9f8ab3b79..4aea038bec716 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml @@ -13,5 +13,7 @@ <element name="SecurityTab" type="button" selector="#admin_security-head"/> <element name="AdminAccountSharing" type="button" selector="#admin_security_admin_account_sharing"/> <element name="EnableSystemValue" type="button" selector="#admin_security_admin_account_sharing_inherit"/> + <element name="systemValueForMaximumLoginFailures" type="checkbox" selector="#admin_security_lockout_failures_inherit"/> + <element name="MaximumLoginFailures" type="input" selector="#admin_security_lockout_failures"/> </section> </sections> diff --git a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml index 6777573570783..c1e5154ba98e3 100644 --- a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml @@ -32,7 +32,11 @@ </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - <actionGroup ref="AdminOpenConfigurationPageActionGroup" stepKey="goToConfigurationPage"/> + <!--Configure 'Maximum Login Failures to Lockout Account'--> + <actionGroup ref="AdminOpenAdminSectionPageActionGroup" stepKey="goToConfigurationPage"/> + <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> + <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"/> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveCahges"/> </test> </tests> \ No newline at end of file From 7d3f1c72994b8e7509aaa8a19a8cdcde6a300f43 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 09:53:30 -0500 Subject: [PATCH 0312/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Edited composer.json, DisableAdminUsage, and EnableAdminUsage --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 10 ++++++++++ .../Controller/Adminhtml/Config/EnableAdminUsage.php | 10 ++++++++++ composer.lock | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 82d8ac6480b0d..7165a705233a0 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -97,4 +97,14 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } + + /** + * Checks if DisableAdminUsage is allowed + * + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 899d05d317e6f..b6414b2a5996b 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -97,4 +97,14 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } + + /** + * Checks if EnableAdminUsage is allowed + * + * @return bool + */ + protected function _isAllowed() + { + return parent::_isAllowed(); + } } diff --git a/composer.lock b/composer.lock index 8c9f79cac256f..e376df91e863d 100644 --- a/composer.lock +++ b/composer.lock @@ -2402,7 +2402,7 @@ }, { "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "email": "backendtea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", From fd2b4ed135d9cd0420bb92790e0522f331879fe4 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 11:27:15 -0500 Subject: [PATCH 0313/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Implemented HttpPostActionInterface to EnableAdminUsage, DisableAdminUsage --- .../Adminhtml/Config/DisableAdminUsage.php | 13 ++----------- .../Adminhtml/Config/EnableAdminUsage.php | 13 ++----------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 7165a705233a0..356f27498fa6b 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -7,6 +7,7 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; @@ -17,7 +18,7 @@ /** * Controller to record Admin analytics usage log */ -class DisableAdminUsage extends Action +class DisableAdminUsage extends Action implements HttpPostActionInterface { private $configFactory; @@ -97,14 +98,4 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } - - /** - * Checks if DisableAdminUsage is allowed - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index b6414b2a5996b..28f22533a42bd 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -7,6 +7,7 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; @@ -17,7 +18,7 @@ /** * Controller to record that the current admin user has responded to Admin Analytics notice */ -class EnableAdminUsage extends Action +class EnableAdminUsage extends Action implements HttpPostActionInterface { private $configFactory; @@ -97,14 +98,4 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - - /** - * Checks if EnableAdminUsage is allowed - * - * @return bool - */ - protected function _isAllowed() - { - return parent::_isAllowed(); - } } From 65d4444aeb618555c5b3609f0d7e2f240636b089 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 14:10:39 -0500 Subject: [PATCH 0314/2437] MC-17593: add mftw test case - updated LoginUser, AdminAuthLogin --- .../Backend/Test/Handler/Ui/LoginUser.php | 1 + .../Backend/Test/Page/AdminAuthLogin.php | 26 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Ui/LoginUser.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Ui/LoginUser.php index 01d8401b22fe1..39f5866a3c2ad 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Ui/LoginUser.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Ui/LoginUser.php @@ -47,6 +47,7 @@ public function persist(FixtureInterface $fixture = null) $loginForm->fill($fixture); $loginForm->submit(); $loginPage->waitForHeaderBlock(); + $loginPage->dismissAdminUsageNotification(); } } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php index c836c4db81ef1..1501a21163b50 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php @@ -40,6 +40,11 @@ class AdminAuthLogin extends Page */ protected $messagesBlock = '.messages'; + /** + * Admin Analytics selector + */ + protected $adminUsageSelector ='.modal-inner-wrap'; + /** * Constructor. */ @@ -98,4 +103,23 @@ function () use ($browser, $selector) { } ); } -} + + public function dismissAdminUsageNotification() + { + $browser = $this->browser; + $selector = $this->adminUsageSelector; + $browser->waitUntil( + function () use ($browser, $selector) { + $item = $browser->find($selector); + if ($item->isVisible()) { + return true; + } + usleep(200000); + return true; + } + ); + if ($this->browser->find($selector)->isVisible()) { + $this->browser->find($selector)->click(); + } + } +} \ No newline at end of file From e6bceb3d653d29e1d2215b17fcce80afe6765873 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 14:36:22 -0500 Subject: [PATCH 0315/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Edited DisableAdminUsage, EnableAdminUsage, AdminUsasgeNotificationDataProvider, admin_usage_notification, component, component-mixin, componser --- .../Adminhtml/Config/DisableAdminUsage.php | 11 +++++++++ .../Adminhtml/Config/EnableAdminUsage.php | 11 +++++++++ .../AdminUsageNotificationDataProvider.php | 9 -------- .../ui_component/admin_usage_notification.xml | 2 +- .../view/adminhtml/web/js/modal/component.js | 23 +++++++++++++++++++ .../modal/component-mixin.js | 10 ++++++-- composer.json | 1 + composer.lock | 2 +- 8 files changed, 56 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 356f27498fa6b..170067bcc5dbd 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -98,4 +98,15 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } + + /** + * Checks if DisableAdminUsage is allowed + * + * @return bool + */ + protected function _isAllowed() + { + $isAllowed = parent::_isAllowed(); + return $isAllowed; + } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 28f22533a42bd..cdb00f12e1d3d 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -98,4 +98,15 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } + + /** + * Checks if EnableAdminUsage is allowed + * + * @return bool + */ + protected function _isAllowed() + { + $isAllowed = parent::_isAllowed(); + return $isAllowed; + } } diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php index 9ab7c8b2c501e..5e8bc2d9160d4 100644 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php @@ -86,11 +86,6 @@ public function __construct( */ public function getData() { - /** @var ModifierInterface $modifier */ - foreach ($this->pool->getModifiersInstances() as $modifier) { - $this->data = $modifier->modifyData($this->data); - } - return $this->data; } @@ -99,10 +94,6 @@ public function getData() */ public function getMeta() { - /** @var ModifierInterface $modifier */ - foreach ($this->pool->getModifiersInstances() as $modifier) { - $this->meta = $modifier->modifyMeta($this->meta); - } return $this->meta; } diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 5547946eb66c1..3c35f1937783b 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -85,7 +85,7 @@ <item name="text" xsi:type="string" translate="true"><![CDATA[ <p>Help us improve Magento Admin by allowing us to collect usage data.</p> <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> - <p>You can learn more and opt out at any time by following the instructions in <a href="https://docs.magento.com/m2/ce/user_guide/stores/admin.html" target="_blank">merchant documentation</a></p> + <p>You can learn more and opt out at any time by following the instructions in <a href="https://docs.magento.com/m2/ce/user_guide/stores/admin.html" target="_blank">merchant documentation</a>.</p> ]]></item> </item> </argument> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 091098a059a99..1725cf3dce330 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -22,6 +22,9 @@ define([ }, options: { keyEventHandlers: { + /** + * Prevents escape key from exiting out of modal + */ escapeKey: function () { return; } @@ -29,13 +32,25 @@ define([ }, notificationWindow: null, }, + + /** + * Initializes modal on opened function + */ initModal: function () { this.options.opened = this.onOpened.bind(this); this._super(); }, + + /** + * Once the modal is opened it hides the X + */ onOpened: function () { $('.modal-header button.action-close').hide(); }, + + /** + * Changes admin usage setting to yes + */ enableAdminUsage: function () { var data = { 'form_key': window.FORM_KEY @@ -58,6 +73,10 @@ define([ this.openReleasePopup(); this.closeModal(); }, + + /** + * Changes admin usage setting to no + */ disableAdminUsage: function () { var data = { 'form_key': window.FORM_KEY @@ -80,6 +99,10 @@ define([ this.openReleasePopup(); this.closeModal(); }, + + /** + * Allows admin usage popup to be shown first and then new release notification + */ openReleasePopup: function () { var notifiModal = registry.get('release_notification.release_notification.notification_modal_1'); diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js index fdfb85dd11813..0cab32739a3b3 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js @@ -6,8 +6,10 @@ define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { 'use strict'; - var deferred = $.Deferred(); - var mixin = { + var deferred = $.Deferred(), mixin = { + /** + * Initializes content only if its visible + */ initializeContent: function () { var initializeContent = this._super.bind(this); @@ -19,6 +21,10 @@ define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { }); } }, + + /** + * Initializes release notification content after admin analytics + */ initializeContentAfterAnalytics: function () { deferred.resolve(); } diff --git a/composer.json b/composer.json index 7235e75f2326e..a2d5624ea6fd7 100644 --- a/composer.json +++ b/composer.json @@ -99,6 +99,7 @@ }, "replace": { "magento/module-marketplace": "*", + "magento/module-admin-analytics": "*", "magento/module-admin-notification": "*", "magento/module-advanced-pricing-import-export": "*", "magento/module-amqp": "*", diff --git a/composer.lock b/composer.lock index e376df91e863d..52a40e589fd98 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0cf49b3b5a47076608e87c7ddb566073", + "content-hash": "630adf0f19937bd3303fd1e72a68efad", "packages": [ { "name": "braintree/braintree_php", From 439830da50d2e54e112cd5583cae5bbd4752dfa4 Mon Sep 17 00:00:00 2001 From: Sergey Dovbenko <sdovbenko@magecom.us> Date: Mon, 5 Aug 2019 20:49:19 +0000 Subject: [PATCH 0316/2437] Set GraphQlInputException when not formatted --- .../CatalogGraphQl/Model/Product/Option/DateType.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php index e1106a3f696e4..40d77e36659a2 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php +++ b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php @@ -10,6 +10,7 @@ use Magento\Catalog\Model\Product\Option\Type\Date as ProductDateOptionType; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; /** * @inheritdoc @@ -43,6 +44,13 @@ private function formatValues($values) if (isset($values[$this->getOption()->getId()])) { $value = $values[$this->getOption()->getId()]; $dateTime = \DateTime::createFromFormat(DateTime::DATETIME_PHP_FORMAT, $value); + + if (!$dateTime) { + throw new GraphQlInputException( + __('Invalid format provided. Please use \'Y-m-d H:i:s\' format.') + ); + } + $values[$this->getOption()->getId()] = [ 'date' => $value, 'year' => $dateTime->format('Y'), From 2503817fa0302820ac7c1a7f59d4160b221d2c01 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 15:55:55 -0500 Subject: [PATCH 0317/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Edited component, component-mixin, AdminAuthLogin static failures --- .../AdminAnalytics/view/adminhtml/web/js/modal/component.js | 2 +- .../web/js/release-notification/modal/component-mixin.js | 5 ++++- .../tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index 1725cf3dce330..bfe9ec3d97e99 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -30,7 +30,7 @@ define([ } } }, - notificationWindow: null, + notificationWindow: null }, /** diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js index 0cab32739a3b3..ffecd031cbb43 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/release-notification/modal/component-mixin.js @@ -6,7 +6,9 @@ define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { 'use strict'; - var deferred = $.Deferred(), mixin = { + var deferred = $.Deferred(), + + mixin = { /** * Initializes content only if its visible */ @@ -34,3 +36,4 @@ define(['jquery', 'analyticsPopupConfig'], function ($, analyticsPopupConfig) { return target.extend(mixin); }; }); + diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php index 1501a21163b50..7201054121dca 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php @@ -122,4 +122,4 @@ function () use ($browser, $selector) { $this->browser->find($selector)->click(); } } -} \ No newline at end of file +} From 8b60dd092c86b0ba7457a8e2dcd9d266b4662d7c Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 16:39:43 -0500 Subject: [PATCH 0318/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Edited component static failure --- .../AdminAnalytics/view/adminhtml/web/js/modal/component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index bfe9ec3d97e99..bc09890d0d0b4 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -109,7 +109,7 @@ define([ if (analyticsPopupConfig.releaseVisible) { notifiModal.initializeContentAfterAnalytics(); } - }, + } } ); } From 35a278548c483ff1e5d0161ff5e2980446820267 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Mon, 5 Aug 2019 20:13:43 -0500 Subject: [PATCH 0319/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Edited CreateTest, IndexTest, default.xml, adminhtml_dashboard_index --- .../layout/adminhtml_dashboard_index.xml | 22 +++++++++++++++++++ .../view/adminhtml/layout/default.xml | 10 --------- .../Adminhtml/Dashboard/IndexTest.php | 4 ++-- .../Controller/Adminhtml/Order/CreateTest.php | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml new file mode 100644 index 0000000000000..3069db1ecc2bb --- /dev/null +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="content"> + <uiComponent name="admin_usage_notification"> + <visibilityCondition name="can_view_admin_usage_notification" className="Magento\AdminAnalytics\Model\Condition\CanViewNotification"/> + </uiComponent> + <block name="tracking_notification" as="tracking_notification" template="Magento_AdminAnalytics::notification.phtml"> + <arguments> + <argument name="notification" xsi:type="object">Magento\AdminAnalytics\ViewModel\Notification</argument> + </arguments> + </block> + </referenceContainer> + </body> +</page> \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 688bbc1e41888..05f7df8284164 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -14,16 +14,6 @@ </arguments> </block> </referenceContainer> - <referenceContainer name="content"> - <uiComponent name="admin_usage_notification"> - <visibilityCondition name="can_view_admin_usage_notification" className="Magento\AdminAnalytics\Model\Condition\CanViewNotification"/> - </uiComponent> - <block name="tracking_notification" as="tracking_notification" template="Magento_AdminAnalytics::notification.phtml"> - <arguments> - <argument name="notification" xsi:type="object">Magento\AdminAnalytics\ViewModel\Notification</argument> - </arguments> - </block> - </referenceContainer> </body> </page> diff --git a/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php b/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php index 2f89f70a6c872..ffe3ccf49ebad 100644 --- a/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php @@ -72,7 +72,7 @@ public function testExecuteEmptyContent() $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); $actual = $this->getResponse()->getBody(); - $this->assertNotContains('"autoOpen":true', $actual); + $this->assertContains('"autoOpen":false', $actual); } public function testExecuteFalseContent() @@ -87,6 +87,6 @@ public function testExecuteFalseContent() $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); $actual = $this->getResponse()->getBody(); - $this->assertNotContains('"autoOpen":true', $actual); + $this->assertContains('"autoOpen":false', $actual); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php index a07616474a410..3d0eb2d4d75e9 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php @@ -55,7 +55,7 @@ public function testLoadBlockAction() $this->getRequest()->setParam('block', ','); $this->getRequest()->setParam('json', 1); $this->dispatch('backend/sales/order_create/loadBlock'); - $this->assertEquals('{"message":""}', $this->getResponse()->getBody()); + $this->assertContains('"message":""}', $this->getResponse()->getBody()); } /** From 45e708cacfecd84181ff437c08733d93540b9086 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Tue, 6 Aug 2019 11:02:50 +0300 Subject: [PATCH 0320/2437] MC-15577: Fix url output --- lib/internal/Magento/Framework/Test/Unit/EscaperTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php index 802477c0ee7bf..426081e71fde1 100644 --- a/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/EscaperTest.php @@ -406,8 +406,8 @@ public function escapeDataProvider() 'http://test.com/?redirect=:\x74\x65\x78\x74x2cCPHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg', ], [ - 'http://test.com/?redirect={{{j}}{{j}}{{j}}{{j}}}avascript:alert(1)', - 'http://test.com/?redirect=:alert(1)', + 'http://test.com/?{{{test}}{{test_translated}}{{tes_origin}}{{theme}}}', + 'http://test.com/?test', ], ]; } From c75e923887f687ae88c859ddce6d82dfee402860 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 6 Aug 2019 11:51:56 +0300 Subject: [PATCH 0321/2437] MC-15930: [FT] [MFTF] AddOutOfStockProductToCompareListTest or functionality is wrong --- .../Controller/Product/Compare/Add.php | 61 ++++++++++++++++++- .../Controller/Product/Compare/Remove.php | 6 +- .../AddOutOfStockProductToCompareListTest.xml | 14 ++--- .../Checker/AddToCompareAvailability.php | 9 ++- ...refrontOnePageCheckoutJsValidationTest.xml | 1 + 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php index 2b989dde9809f..8b854361fd4ef 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Add.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Add.php @@ -6,15 +6,72 @@ */ namespace Magento\Catalog\Controller\Product\Compare; -use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\Data\Form\FormKey\Validator; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Result\PageFactory; /** * Add item to compare list action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Add extends \Magento\Catalog\Controller\Product\Compare implements HttpPostActionInterface { + /** + * @var AddToCompareAvailability + */ + private $compareAvailability; + + /** + * @param \Magento\Framework\App\Action\Context $context + * @param \Magento\Catalog\Model\Product\Compare\ItemFactory $compareItemFactory + * @param \Magento\Catalog\Model\ResourceModel\Product\Compare\Item\CollectionFactory $itemCollectionFactory + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Customer\Model\Visitor $customerVisitor + * @param \Magento\Catalog\Model\Product\Compare\ListCompare $catalogProductCompareList + * @param \Magento\Catalog\Model\Session $catalogSession + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param Validator $formKeyValidator + * @param PageFactory $resultPageFactory + * @param ProductRepositoryInterface $productRepository + * @param AddToCompareAvailability|null $compareAvailability + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\Framework\App\Action\Context $context, + \Magento\Catalog\Model\Product\Compare\ItemFactory $compareItemFactory, + \Magento\Catalog\Model\ResourceModel\Product\Compare\Item\CollectionFactory $itemCollectionFactory, + \Magento\Customer\Model\Session $customerSession, + \Magento\Customer\Model\Visitor $customerVisitor, + \Magento\Catalog\Model\Product\Compare\ListCompare $catalogProductCompareList, + \Magento\Catalog\Model\Session $catalogSession, + \Magento\Store\Model\StoreManagerInterface $storeManager, + Validator $formKeyValidator, + PageFactory $resultPageFactory, + ProductRepositoryInterface $productRepository, + AddToCompareAvailability $compareAvailability = null + ) { + parent::__construct( + $context, + $compareItemFactory, + $itemCollectionFactory, + $customerSession, + $customerVisitor, + $catalogProductCompareList, + $catalogSession, + $storeManager, + $formKeyValidator, + $resultPageFactory, + $productRepository + ); + + $this->compareAvailability = $compareAvailability + ?: $this->_objectManager->get(AddToCompareAvailability::class); + } + /** * Add item to compare list. * @@ -37,7 +94,7 @@ public function execute() $product = null; } - if ($product && (int)$product->getStatus() !== Status::STATUS_DISABLED) { + if ($product && $this->compareAvailability->isAvailableForCompare($product)) { $this->_catalogProductCompareList->addProduct($product); $productName = $this->_objectManager->get( \Magento\Framework\Escaper::class diff --git a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php index acf0f1b754c12..f5d56dc9e6b0e 100644 --- a/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php +++ b/app/code/Magento/Catalog/Controller/Product/Compare/Remove.php @@ -6,6 +6,7 @@ */ namespace Magento\Catalog\Controller\Product\Compare; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; @@ -17,12 +18,13 @@ class Remove extends \Magento\Catalog\Controller\Product\Compare implements Http /** * Remove item from compare list. * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { $productId = (int)$this->getRequest()->getParam('product'); - if ($productId) { + if ($this->_formKeyValidator->validate($this->getRequest()) && $productId) { $storeId = $this->_storeManager->getStore()->getId(); try { /** @var \Magento\Catalog\Model\Product $product */ @@ -31,7 +33,7 @@ public function execute() $product = null; } - if ($product && $product->isSalable()) { + if ($product && (int)$product->getStatus() !== Status::STATUS_DISABLED) { /** @var $item \Magento\Catalog\Model\Product\Compare\Item */ $item = $this->_compareItemFactory->create(); if ($this->_customerSession->isLoggedIn()) { diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index 31204c7b4b0bf..9a0f5ad002725 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -18,13 +18,10 @@ <testCaseId value="MAGETWO-98644"/> <useCaseId value="MAGETWO-98522"/> <group value="Catalog"/> - <skip> - <issueId value="MC-15930"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <magentoCLI command="config:set cataloginventory/options/show_out_of_stock 0" stepKey="displayOutOfStockNo"/> + <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <createData entity="SimpleSubCategory" stepKey="category"/> <createData entity="SimpleProduct4" stepKey="product"> @@ -32,7 +29,7 @@ </createData> </before> <after> - <magentoCLI command="config:set cataloginventory/options/show_out_of_stock 0" stepKey="displayOutOfStockNo2"/> + <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> @@ -40,23 +37,24 @@ </after> <!--Open product page--> <comment userInput="Open product page" stepKey="openProdPage"/> - <amOnPage url="{{StorefrontProductPage.url($$product.name$$)}}" stepKey="goToSimpleProductPage"/> + <amOnPage url="{{StorefrontProductPage.url($$product.custom_attributes[url_key]$$)}}" stepKey="goToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPage"/> <!--'Add to compare' link is not available--> <comment userInput="'Add to compare' link is not available" stepKey="addToCompareLinkAvailability"/> <dontSeeElement selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="dontSeeAddToCompareLink"/> <!--Turn on 'out on stock' config--> <comment userInput="Turn on 'out of stock' config" stepKey="onOutOfStockConfig"/> - <magentoCLI command="config:set cataloginventory/options/show_out_of_stock 1" stepKey="displayOutOfStockYes"/> + <magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockEnable.path}} {{CatalogInventoryOptionsShowOutOfStockEnable.value}}" stepKey="setConfigShowOutOfStockTrue"/> <!--Clear cache and reindex--> <comment userInput="Clear cache and reindex" stepKey="cleanCache"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Open product page--> <comment userInput="Open product page" stepKey="openProductPage"/> - <amOnPage url="{{StorefrontProductPage.url($$product.name$$)}}" stepKey="goToSimpleProductPage2"/> + <amOnPage url="{{StorefrontProductPage.url($$product.custom_attributes[url_key]$$)}}" stepKey="goToSimpleProductPage2"/> <waitForPageLoad stepKey="waitForSimpleProductPage2"/> <!--Click on 'Add to Compare' link--> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="seeAddToCompareLink"/> <comment userInput="Click on 'Add to Compare' link" stepKey="clickOnAddToCompareLink"/> <click selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="clickOnAddToCompare"/> <waitForPageLoad stepKey="waitForProdAddToCmpList"/> diff --git a/app/code/Magento/Catalog/ViewModel/Product/Checker/AddToCompareAvailability.php b/app/code/Magento/Catalog/ViewModel/Product/Checker/AddToCompareAvailability.php index 27829155af292..00bac7e61b5b4 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Checker/AddToCompareAvailability.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Checker/AddToCompareAvailability.php @@ -10,6 +10,7 @@ use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; /** * Check is available add to compare. @@ -37,7 +38,11 @@ public function __construct(StockConfigurationInterface $stockConfiguration) */ public function isAvailableForCompare(ProductInterface $product): bool { - return $this->isInStock($product) || $this->stockConfiguration->isShowOutOfStock(); + if ((int)$product->getStatus() !== Status::STATUS_DISABLED) { + return $this->isInStock($product) || $this->stockConfiguration->isShowOutOfStock(); + } + + return false; } /** @@ -53,6 +58,6 @@ private function isInStock(ProductInterface $product): bool return $product->isSalable(); } - return isset($quantityAndStockStatus['is_in_stock']) && $quantityAndStockStatus['is_in_stock']; + return $quantityAndStockStatus['is_in_stock'] ?? false; } } diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml index 63751ad697ede..8ed8e590eb229 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml @@ -11,6 +11,7 @@ <test name="StorefrontOnePageCheckoutJsValidationTest"> <annotations> <features value="Checkout"/> + <stories value="Checkout"/> <title value="Js validation error messages must be absent for required fields after checkout start."/> <description value="Js validation error messages must be absent for required fields after checkout start."/> <severity value="MAJOR" /> From 63cdff53653a0787fff90659dc36b1246e886a41 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Tue, 6 Aug 2019 12:01:59 +0300 Subject: [PATCH 0322/2437] Finish the test --- ...> AdminOpenConfigAdminPageActionGroup.xml} | 4 +-- ...ginFailuresToLockoutAccountActionGroup.xml | 2 +- ...ctionPage.xml => AdminConfigAdminPage.xml} | 2 +- .../Magento/User/Test/Mftf/Data/UserData.xml | 35 ++++++++++++++++++- .../Mftf/Test/LockAdminUserEntityTest.xml | 35 +++++++++++++++++-- 5 files changed, 70 insertions(+), 8 deletions(-) rename app/code/Magento/Config/Test/Mftf/ActionGroup/{AdminOpenAdminSectionPageActionGroup.xml => AdminOpenConfigAdminPageActionGroup.xml} (72%) rename app/code/Magento/Config/Test/Mftf/Page/{AdminConfigurationAdminSectionPage.xml => AdminConfigAdminPage.xml} (73%) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml similarity index 72% rename from app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml rename to app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml index df78c374623a1..361f4fd1fa91b 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenAdminSectionPageActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml @@ -7,8 +7,8 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenAdminSectionPageActionGroup"> - <amOnPage url="{{AdminEditAdminSectionPage.url}}" stepKey="goToConfigurationPage"/> + <actionGroup name="AdminOpenConfigAdminPageActionGroup"> + <amOnPage url="{{AdminConfigAdminPage.url}}" stepKey="goToAdminSectionPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml index 02c528009ea87..1dd05b7c45ddc 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup"> <arguments> - <argument name="qty" type="string" defaultValue="5"/> + <argument name="qty" type="string" defaultValue="3"/> </arguments> <uncheckOption selector="{{AdminSection.systemValueForMaximumLoginFailures}}" stepKey="uncheckUseSystemValue"/> <fillField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="setMaximumLoginFailures"/> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml similarity index 73% rename from app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml rename to app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml index 02879ad1fc708..661bb734bcbe4 100644 --- a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigurationAdminSectionPage.xml +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml @@ -6,7 +6,7 @@ */ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminEditAdminSectionPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> + <page name="AdminConfigAdminPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> <section name="AdminSection"/> </page> </pages> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index d465851c62373..b8dd8f30346fa 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -108,7 +108,40 @@ <item>1</item> </array> </entity> - + <entity name="adminUserCorrectPassword" type="user"> + <data key="username">admin_user_with_correct_password</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">true</data> + <data key="is_active_label">Active</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> + <entity name="adminUserIncorrectPassword" type="user"> + <data key="username">admin_user_with_correct_password</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123123q</data> + <data key="password_confirmation">123123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">true</data> + <data key="is_active_label">Active</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> <!-- Since User delete action is performed via POST request we created this entity to be able to delete it. Please use "AdminDeleteUserViaCurlActionGroup". diff --git a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml index c1e5154ba98e3..68276c5e7015c 100644 --- a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml @@ -28,15 +28,44 @@ <!--Create New User--> <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> - <argument name="user" value="NewAdminUser"/> + <argument name="user" value="adminUserCorrectPassword"/> </actionGroup> <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> <!--Configure 'Maximum Login Failures to Lockout Account'--> - <actionGroup ref="AdminOpenAdminSectionPageActionGroup" stepKey="goToConfigurationPage"/> + <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToAdminSectionPage"/> <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"/> - <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveCahges"/> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> + + <!-- Log in to Admin Panel with incorrect password specified number of times--> + <actionGroup ref="logout" stepKey="logoutAsDefaultUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFirstAttempt"> + <argument name="adminUser" value="adminUserIncorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFirstAttempt"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserSecondAttempt"> + <argument name="adminUser" value="adminUserIncorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorSecondAttempt"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> + <argument name="adminUser" value="adminUserIncorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> + + <!-- Log in to Admin Panel with correct password--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFourthAttempt"> + <argument name="adminUser" value="adminUserCorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFourthAttempt"/> + + <!--Login as default admin user--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + + <!--Delete new User--> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="adminUserCorrectPassword"/> + </actionGroup> </test> </tests> \ No newline at end of file From eb60f826cf874da58b241c94f6b50d980bbfbd53 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Tue, 6 Aug 2019 13:25:15 +0300 Subject: [PATCH 0323/2437] Code refactoring --- .../AdminExpandSecurityTabActionGroup.xml | 2 -- .../AdminOpenConfigAdminPageActionGroup.xml | 4 ++-- ...imumLoginFailuresToLockoutAccountActionGroup.xml | 1 - app/code/Magento/User/Test/Mftf/Data/UserData.xml | 13 ------------- ...ityTest.xml => AdminLockAdminUserEntityTest.xml} | 4 ++-- .../User/Test/TestCase/LockAdminUserEntityTest.xml | 1 + 6 files changed, 5 insertions(+), 20 deletions(-) rename app/code/Magento/User/Test/Mftf/Test/{LockAdminUserEntityTest.xml => AdminLockAdminUserEntityTest.xml} (98%) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml index 76a1c9291f4e8..5f9f35ceb8d0d 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml @@ -12,5 +12,3 @@ <conditionalClick selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true" stepKey="openSecurityTab"/> </actionGroup> </actionGroups> - - diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml index 361f4fd1fa91b..6d4fba179ecf4 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenConfigAdminPageActionGroup"> - <amOnPage url="{{AdminConfigAdminPage.url}}" stepKey="goToAdminSectionPage"/> + <amOnPage url="{{AdminConfigAdminPage.url}}" stepKey="goToConfigAdminSectionPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml index 1dd05b7c45ddc..ada58e9f4225e 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml @@ -17,4 +17,3 @@ <seeInField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="seeNewValueInField"/> </actionGroup> </actionGroups> - diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index b8dd8f30346fa..e5c6a48497626 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -127,20 +127,7 @@ </entity> <entity name="adminUserIncorrectPassword" type="user"> <data key="username">admin_user_with_correct_password</data> - <data key="firstname">John</data> - <data key="lastname">Doe</data> - <data key="email" unique="prefix">admin@example.com</data> <data key="password">123123123q</data> - <data key="password_confirmation">123123123q</data> - <data key="interface_local">en_US</data> - <data key="interface_local_label">English (United States)</data> - <data key="is_active">true</data> - <data key="is_active_label">Active</data> - <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> - <data key="role">Administrators</data> - <array key="roles"> - <item>1</item> - </array> </entity> <!-- Since User delete action is performed via POST request we created this entity to be able to delete it. diff --git a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml similarity index 98% rename from app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml rename to app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index 68276c5e7015c..06c7d429a625a 100644 --- a/app/code/Magento/User/Test/Mftf/Test/LockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -33,7 +33,7 @@ <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> <!--Configure 'Maximum Login Failures to Lockout Account'--> - <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToAdminSectionPage"/> + <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"/> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> @@ -68,4 +68,4 @@ </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml index 052197e3db33c..f89f94ba03e73 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml @@ -24,6 +24,7 @@ <data name="incorrectPassword" xsi:type="string">honey boo boo</data> <data name="attempts" xsi:type="string">7</data> <constraint name="Magento\User\Test\Constraint\AssertUserFailedLoginMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 0fa8549b13e72e40481d1225d3ce1c32a7c15e49 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Tue, 6 Aug 2019 16:50:14 +0300 Subject: [PATCH 0324/2437] Code refactoring --- .../Test/Mftf/Test/AdminLockAdminUserEntityTest.xml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index 06c7d429a625a..4a021597ce405 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -35,7 +35,9 @@ <!--Configure 'Maximum Login Failures to Lockout Account'--> <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> - <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"/> + <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"> + <argument name="qty" value="2"/> + </actionGroup> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> <!-- Log in to Admin Panel with incorrect password specified number of times--> @@ -48,16 +50,12 @@ <argument name="adminUser" value="adminUserIncorrectPassword"/> </actionGroup> <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorSecondAttempt"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> - <argument name="adminUser" value="adminUserIncorrectPassword"/> - </actionGroup> - <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> <!-- Log in to Admin Panel with correct password--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFourthAttempt"> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> <argument name="adminUser" value="adminUserCorrectPassword"/> </actionGroup> - <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFourthAttempt"/> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> <!--Login as default admin user--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> From a079c9139a1ae30f28f85b1f693a28e787e0f0b2 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 6 Aug 2019 17:06:42 +0300 Subject: [PATCH 0325/2437] MC-18967: [IT] Magento.SendFriend.Controller.SendmailTest fail on 2.3.3-develop --- .../testsuite/Magento/SendFriend/Controller/SendmailTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index 8dbe9468923fb..1d6adf52466d2 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -24,10 +24,11 @@ class SendmailTest extends AbstractController /** * Share the product to friend as logged in customer * + * @magentoAppArea frontend * @magentoDbIsolation enabled * @magentoAppIsolation enabled - * @magentoConfigFixture default/sendfriend/email/allow_guest 0 - * @magentoConfigFixture default/sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 0 + * @magentoConfigFixture default_store sendfriend/email/enabled 1 * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Catalog/_files/products.php */ From 52abb64c9491bf5a33dc6f77e5f4c04a0c8cfe3a Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 09:55:21 -0500 Subject: [PATCH 0326/2437] MC-15298: Allow admin to opt out of admin analytics tracking - admin analytics css --- .../web/css/source/_module.less | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/design/adminhtml/Magento/backend/Magento_AdminAnalytics/web/css/source/_module.less diff --git a/app/design/adminhtml/Magento/backend/Magento_AdminAnalytics/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_AdminAnalytics/web/css/source/_module.less new file mode 100644 index 0000000000000..05c0653a9bac3 --- /dev/null +++ b/app/design/adminhtml/Magento/backend/Magento_AdminAnalytics/web/css/source/_module.less @@ -0,0 +1,43 @@ +// /** +// * Copyright © Magento, Inc. All rights reserved. +// * See COPYING.txt for license details. +// */ + +// +// Magento_AdminAnalytics Modal on dashboard +// --------------------------------------------- + +.admin-usage-notification { + -webkit-transition: visibility 0s .5s, opacity .5s ease; + transition: visibility 0s .5s, opacity .5s ease; + + &._show { + -webkit-transition: opacity .5s ease; + opacity: 1; + transition: opacity .5s ease; + visibility: visible; + } + + .modal-inner-wrap { + .modal-content, + .modal-header { + padding-left: 4rem; + padding-right: 4rem; + + .action-close { + display: none; + } + } + + -webkit-transform: translateX(0); + -webkit-transition: -webkit-transform 0s; + transition: transform 0s; + transform: translateX(0); + margin-top: 13rem; + max-width: 75rem; + } + + .admin__fieldset { + padding: 0; + } +} From 3ce548211bb881c7eee2a8121d303d98efdcb9b8 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 11:00:51 -0500 Subject: [PATCH 0327/2437] MC-15298: Allow admin to opt out of admin analytics tracking - edited AdminAuthLogin --- .../tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php index 7201054121dca..8154f4bf1f20c 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php @@ -43,7 +43,7 @@ class AdminAuthLogin extends Page /** * Admin Analytics selector */ - protected $adminUsageSelector ='.modal-inner-wrap'; + protected $adminUsageSelector ='.action-secondary'; /** * Constructor. From f681416e6975126c310dc68f8795db02b397054a Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 14:06:05 -0500 Subject: [PATCH 0328/2437] MC-15298: Allow admin to opt out of admin analytics tracking - changes based on review --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 9 ++++++--- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 10 ++++++---- .../Model/Condition/CanViewNotification.php | 4 +--- .../Model/ResourceModel/Viewer/Logger.php | 8 +++----- app/code/Magento/AdminAnalytics/Model/Viewer/Log.php | 4 ++-- .../Test/Unit/Condition/CanViewNotificationTest.php | 3 +++ app/code/Magento/AdminAnalytics/etc/module.xml | 1 - 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 170067bcc5dbd..d0e5cff85d9a4 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -20,6 +20,9 @@ */ class DisableAdminUsage extends Action implements HttpPostActionInterface { + /** + * @var Factory + */ private $configFactory; /** @@ -63,7 +66,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - public function disableAdminUsage() + private function disableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 0); @@ -75,7 +78,7 @@ public function disableAdminUsage() * * @return ResultInterface */ - public function markUserNotified() + private function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( @@ -104,7 +107,7 @@ public function execute() * * @return bool */ - protected function _isAllowed() + public function _isAllowed() { $isAllowed = parent::_isAllowed(); return $isAllowed; diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index cdb00f12e1d3d..be2a8ce1d3ca3 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -20,7 +20,9 @@ */ class EnableAdminUsage extends Action implements HttpPostActionInterface { - + /** + * @var Factory + */ private $configFactory; /** * @var ProductMetadataInterface @@ -63,7 +65,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - public function enableAdminUsage() + private function enableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 1); @@ -75,7 +77,7 @@ public function enableAdminUsage() * * @return ResultInterface */ - public function markUserNotified() + private function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( @@ -104,7 +106,7 @@ public function execute() * * @return bool */ - protected function _isAllowed() + public function _isAllowed() { $isAllowed = parent::_isAllowed(); return $isAllowed; diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 1af8702e55108..76591ffe5cc10 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -11,9 +11,7 @@ use function Magento\PAT\Reports\Utils\readResponseTimeReport; /** - * Dynamic validator for UI release notification, manage UI component visibility. - * - * Return true if the logged in user has not seen the notification. + * Dynamic validator for UI admin analytics notification, manage UI component visibility. */ class CanViewNotification implements VisibilityConditionInterface { diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index 505257f051472..38fdd2afed7a7 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -34,7 +34,6 @@ class Logger private $logFactory; /** - * Logger constructor. * @param ResourceConnection $resource * @param LogFactory $logFactory */ @@ -71,16 +70,15 @@ public function log(string $lastViewVersion) : bool /** * Get log by the last view version. * - * @param string $lastViewVersion * @return Log */ - public function get(string $lastViewVersion) : Log + public function get() : Log { - return $this->logFactory->create(['data' => $this->loadLogData($lastViewVersion)]); + return $this->logFactory->create(['data' => $this->loadLogData()]); } /** - * Get log by the last view version. + * Checks is log already exists. * * @return boolean */ diff --git a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php index 3ba1d15d716d7..e97b8f94e12ba 100644 --- a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php +++ b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php @@ -17,7 +17,7 @@ class Log extends DataObject * * @return int */ - public function getId() + public function getId() : int { return $this->getData('id'); } @@ -27,7 +27,7 @@ public function getId() * * @return string */ - public function getLastViewVersion() + public function getLastViewVersion() : ?string { return $this->getData('last_viewed_in_version'); } diff --git a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php index 00309f9527015..7819f2f017a01 100644 --- a/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/AdminAnalytics/Test/Unit/Condition/CanViewNotificationTest.php @@ -12,6 +12,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\App\CacheInterface; +/** + * Class CanViewNotificationTest + */ class CanViewNotificationTest extends \PHPUnit\Framework\TestCase { /** @var CanViewNotification */ diff --git a/app/code/Magento/AdminAnalytics/etc/module.xml b/app/code/Magento/AdminAnalytics/etc/module.xml index e27c90db11e29..f0990b114e25f 100644 --- a/app/code/Magento/AdminAnalytics/etc/module.xml +++ b/app/code/Magento/AdminAnalytics/etc/module.xml @@ -8,4 +8,3 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_AdminAnalytics"/> </config> - From 86c936941e5c4614fa7400e14f4f5b1ea61d2dcb Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 14:22:27 -0500 Subject: [PATCH 0329/2437] MC-17593: handle mtf and mftf failures - edited CanViewNotification,HttpContentProvider,Urlbuilder, config --- .../Backend/Test/Page/AdminAuthLogin.php | 23 ++++++------------ .../Magento/Ui/Test/Block/Adminhtml/Modal.php | 24 +++++++++++++++++++ .../UpdateAdminUserRoleEntityTest.php | 2 ++ .../Test/TestStep/LoginUserOnBackendStep.php | 2 ++ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php index 8154f4bf1f20c..b9c6140971409 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php @@ -43,7 +43,7 @@ class AdminAuthLogin extends Page /** * Admin Analytics selector */ - protected $adminUsageSelector ='.action-secondary'; + protected $adminUsageSelector ='.modal-inner-wrap'; /** * Constructor. @@ -87,6 +87,11 @@ public function getMessagesBlock() return Factory::getBlockFactory()->getMagentoBackendMessages($this->browser->find($this->messagesBlock)); } + public function getModalBlock() + { + return Factory::getBlockFactory()->getMagentoUiAdminhtmlModal($this->browser->find('.modal-inner-wrap')); + } + /** * Wait for Header block is visible in the page. * @@ -106,20 +111,6 @@ function () use ($browser, $selector) { public function dismissAdminUsageNotification() { - $browser = $this->browser; - $selector = $this->adminUsageSelector; - $browser->waitUntil( - function () use ($browser, $selector) { - $item = $browser->find($selector); - if ($item->isVisible()) { - return true; - } - usleep(200000); - return true; - } - ); - if ($this->browser->find($selector)->isVisible()) { - $this->browser->find($selector)->click(); - } + $this->getModalBlock()->dismissIfModalAppears(); } } diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/Modal.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/Modal.php index 5c27776c09620..eb949dccb7ffc 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/Modal.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/Modal.php @@ -163,6 +163,30 @@ function () { ); } + /** + * Dismiss the modal if it appears + * + * @return void + */ + public function dismissIfModalAppears() + { + $browser = $this->browser; + $selector = $this->dismissWarningSelector; + $browser->waitUntil( + function () use ($browser, $selector) { + $item = $browser->find($selector); + if ($item->isVisible()) { + return true; + } + $this->waitModalAnimationFinished(); + return true; + } + ); + if ($this->browser->find($selector)->isVisible()) { + $this->browser->find($selector)->click(); + } + } + /** * Waiting until CSS animation is done. * Transition-duration is set at this file: "<magento_root>/lib/web/css/source/components/_modals.less" diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php index 58450abc71633..c7031e9fccbeb 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserRoleEntityTest.php @@ -102,6 +102,8 @@ public function testUpdateAdminUserRolesEntity( $this->adminAuthLogin->open(); $this->adminAuthLogin->getLoginBlock()->fill($user); $this->adminAuthLogin->getLoginBlock()->submit(); + $this->adminAuthLogin->waitForHeaderBlock(); + $this->adminAuthLogin->dismissAdminUsageNotification(); $this->rolePage->open(); $this->rolePage->getRoleGrid()->searchAndOpen($filter); $this->userRoleEditRole->getRoleFormTabs()->fill($role); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php index c244e27d42899..93e5002a34d1b 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php @@ -119,6 +119,8 @@ private function login() { $this->adminAuth->getLoginBlock()->fill($this->user); $this->adminAuth->getLoginBlock()->submit(); + $this->adminAuthLogin->waitForHeaderBlock(); + $this->adminAuthLogin->dismissAdminUsageNotification(); $this->adminAuth->getLoginBlock()->waitFormNotVisible(); } } From c191c7efe133bc5e616e7fdeafb21de8780b519b Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 14:26:33 -0500 Subject: [PATCH 0330/2437] MC-15298: Allow admin to opt out of admin analytics tracking - made disableAdminUsage, enableAdminUsage, markUserNotified public functions --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 4 ++-- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index d0e5cff85d9a4..d5205f27f6ac2 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -66,7 +66,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - private function disableAdminUsage() + public function disableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 0); @@ -78,7 +78,7 @@ private function disableAdminUsage() * * @return ResultInterface */ - private function markUserNotified() : ResultInterface + public function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index be2a8ce1d3ca3..963954d917b4f 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -65,7 +65,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - private function enableAdminUsage() + public function enableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 1); @@ -77,7 +77,7 @@ private function enableAdminUsage() * * @return ResultInterface */ - private function markUserNotified() : ResultInterface + public function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( From 80b848a36567deb2540f4cfd6c3d005dfa0bf25e Mon Sep 17 00:00:00 2001 From: Sergey Dovbenko <sdovbenko@magecom.us> Date: Tue, 6 Aug 2019 20:32:44 +0000 Subject: [PATCH 0331/2437] Added SuppressWarnings to the class --- .../Magento/CatalogGraphQl/Model/Product/Option/DateType.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php index 40d77e36659a2..5205115b772ed 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php +++ b/app/code/Magento/CatalogGraphQl/Model/Product/Option/DateType.php @@ -13,7 +13,10 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; /** - * @inheritdoc + * CatalogGraphQl product option date type. + * + * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class DateType extends ProductDateOptionType { From 7d36b8b02fbdf648d97c9fa1ba8ba332ea715785 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 16:35:22 -0500 Subject: [PATCH 0332/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added declare(strict_types=1) --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 1 + .../Controller/Adminhtml/Config/EnableAdminUsage.php | 1 + .../AdminAnalytics/Model/Condition/CanViewNotification.php | 2 ++ app/code/Magento/AdminAnalytics/Model/Viewer/Log.php | 2 ++ 4 files changed, 6 insertions(+) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index d5205f27f6ac2..3bd5fe8afbebf 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 963954d917b4f..ed55f8556df30 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 76591ffe5cc10..03b99f404e01e 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\AdminAnalytics\Model\Condition; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; diff --git a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php index e97b8f94e12ba..cd29783113d03 100644 --- a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php +++ b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\AdminAnalytics\Model\Viewer; use Magento\Framework\DataObject; From 28eeac6307821addd475798aa73a2383554b367d Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Tue, 6 Aug 2019 17:04:40 -0500 Subject: [PATCH 0333/2437] MC-17593: handle mtf and mftf failures - changed test files --- .../Magento/Backend/Test/Page/AdminAuthLogin.php | 15 +++++++++++++-- .../Test/TestCase/DeleteAdminUserEntityTest.php | 2 ++ .../Test/TestCase/DeleteUserRoleEntityTest.php | 2 ++ .../User/Test/TestCase/UnlockAdminUserTest.php | 2 ++ .../Test/TestCase/UpdateAdminUserEntityTest.php | 2 ++ .../User/Test/TestStep/LoginUserOnBackendStep.php | 4 ++-- 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php index b9c6140971409..4b72bb836523a 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Page/AdminAuthLogin.php @@ -80,16 +80,22 @@ public function getHeaderBlock() /** * Get global messages block. * - * @return \Magento\Backend\Test\Block\Messages + * @return \Magento\Ui\Test\Block\Adminhtml\Modal + */ public function getMessagesBlock() { return Factory::getBlockFactory()->getMagentoBackendMessages($this->browser->find($this->messagesBlock)); } + /** + * Get modal block + * + * @return void + */ public function getModalBlock() { - return Factory::getBlockFactory()->getMagentoUiAdminhtmlModal($this->browser->find('.modal-inner-wrap')); + return Factory::getBlockFactory()->getMagentoUiAdminhtmlModal($this->browser->find($this->adminUsageSelector)); } /** @@ -109,6 +115,11 @@ function () use ($browser, $selector) { ); } + /** + * Dismiss admin usage notification + * + * @return void + */ public function dismissAdminUsageNotification() { $this->getModalBlock()->dismissIfModalAppears(); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.php index 42914cd697bfd..81d9fe8393aee 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteAdminUserEntityTest.php @@ -117,6 +117,8 @@ public function testDeleteAdminUserEntity( $this->adminAuthLogin->open(); $this->adminAuthLogin->getLoginBlock()->fill($user); $this->adminAuthLogin->getLoginBlock()->submit(); + $this->adminAuthLogin->waitForHeaderBlock(); + $this->adminAuthLogin->dismissAdminUsageNotification(); } $this->userIndex->open(); $this->userIndex->getUserGrid()->searchAndOpen($filter); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteUserRoleEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteUserRoleEntityTest.php index 47c5a4095830d..2a1c9feb440b9 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteUserRoleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/DeleteUserRoleEntityTest.php @@ -117,6 +117,8 @@ public function testDeleteAdminUserRole( $this->adminAuthLogin->open(); $this->adminAuthLogin->getLoginBlock()->fill($adminUser); $this->adminAuthLogin->getLoginBlock()->submit(); + $this->adminAuthLogin->waitForHeaderBlock(); + $this->adminAuthLogin->dismissAdminUsageNotification(); } $this->userRoleIndex->open(); $this->userRoleIndex->getRoleGrid()->searchAndOpen($filter); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php index 6a43be4afd422..8db927ccda29b 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php @@ -124,6 +124,8 @@ public function test( $this->adminAuth->open(); $this->adminAuth->getLoginBlock()->fill($incorrectUser); $this->adminAuth->getLoginBlock()->submit(); + $this->adminAuth->waitForHeaderBlock(); + $this->adminAuth->dismissAdminUsageNotification(); } // Test steps diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.php index e8869de13462a..dab4cec22c86d 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.php @@ -147,6 +147,8 @@ public function testUpdateAdminUser( $this->adminAuth->open(); $this->adminAuth->getLoginBlock()->fill($initialUser); $this->adminAuth->getLoginBlock()->submit(); + $this->adminAuth->waitForHeaderBlock(); + $this->adminAuth->dismissAdminUsageNotification(); } $this->userIndex->open(); $this->userIndex->getUserGrid()->searchAndOpen($filter); diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php index 93e5002a34d1b..3e2a3a3d59fc1 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestStep/LoginUserOnBackendStep.php @@ -119,8 +119,8 @@ private function login() { $this->adminAuth->getLoginBlock()->fill($this->user); $this->adminAuth->getLoginBlock()->submit(); - $this->adminAuthLogin->waitForHeaderBlock(); - $this->adminAuthLogin->dismissAdminUsageNotification(); + $this->adminAuth->waitForHeaderBlock(); + $this->adminAuth->dismissAdminUsageNotification(); $this->adminAuth->getLoginBlock()->waitFormNotVisible(); } } From 94a8f6d69c448f50aacdfb5e08ed89dd2f3e5663 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Tue, 6 Aug 2019 23:02:10 -0500 Subject: [PATCH 0334/2437] MC-15298: Allow admin to opt out of admin analytics tracking - MTf fix --- .../app/Magento/User/Test/TestCase/UnlockAdminUserTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php index 8db927ccda29b..6a43be4afd422 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UnlockAdminUserTest.php @@ -124,8 +124,6 @@ public function test( $this->adminAuth->open(); $this->adminAuth->getLoginBlock()->fill($incorrectUser); $this->adminAuth->getLoginBlock()->submit(); - $this->adminAuth->waitForHeaderBlock(); - $this->adminAuth->dismissAdminUsageNotification(); } // Test steps From b9855cb6675d9841fb2221510f7c3a83a92147ef Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 7 Aug 2019 10:07:47 -0500 Subject: [PATCH 0335/2437] MC-15298: Allow admin to opt out of admin analytics tracking - deleted unused files --- .../Model/ContentProviderInterface.php | 24 ------ .../adminhtml/templates/confirm_popup.phtml | 81 ------------------- 2 files changed, 105 deletions(-) delete mode 100644 app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php delete mode 100644 app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml diff --git a/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php b/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php deleted file mode 100644 index ea67be7019e19..0000000000000 --- a/app/code/Magento/AdminAnalytics/Model/ContentProviderInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\AdminAnalytics\Model; - -/** - * Requests the release notification content data from a defined service - */ -interface ContentProviderInterface -{ - /** - * Retrieves the release notification content data. - * - * @param string $version - * @param string $edition - * @param string $locale - * - * @return string|false - */ - public function getContent($version, $edition, $locale); -} diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml deleted file mode 100644 index ddd9b7acf03dd..0000000000000 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/confirm_popup.phtml +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -?> - -<div class="confirmation-modal-content"> - <p>Help us improve Magento Admin by allowing us to collect usage data.</p> - <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> - <p>You can learn more and opt out at any time by following the instructions in <a href="https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html" target="_blank">merchant documentation</a></p> -</div> - -<script> - require([ - 'jquery', - 'Magento_Ui/js/modal/modal' - ], function ($) { - 'use strict'; - - $('.confirmation-modal-content').modal({ - imports: { - logAction: '${ $.provider }:data.logAction' - }, - title: 'Allow admin usage data collection', - autoOpen: true, - type: 'popup', - clickableOverlay: false, - responsive: true, - keyEventHandlers: { - escapeKey: function(){} - }, - opened: function($Event){ - $('.modal-header button.action-close', $Event.srcElement).hide(); - }, - buttons: [ - { - text: $.mage.__(`Don't Allow`), - class: 'action', - click: function(){ - var data = { - 'form_key': window.FORM_KEY - }; - $.ajax({ - type: 'POST', - url: '/magento2ce/admin_michell/adminAnalytics/config/disableAdminUsage', - data: data, - showLoader: true - }).done(function (xhr) { - if (xhr.error) { - self.onError(xhr); - } - }).fail(this.onError); - this.closeModal(); - }, - }, - { - text: $.mage.__('Ok'), - class: 'action', - click: function(){ - var data = { - 'form_key': window.FORM_KEY - }; - $.ajax({ - type: 'POST', - url: '/magento2ce/admin_michell/adminAnalytics/config/enableAdminUsage', - data: data, - showLoader: true - }).done(function (xhr) { - if (xhr.error) { - self.onError(xhr); - } - }).fail(this.onError); - - this.closeModal(); - }, - } - ], - }); - }); -</script> From 7b0b55a021c30d2af52b784b4022f689081e8d0c Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 7 Aug 2019 12:28:43 -0500 Subject: [PATCH 0336/2437] MC-15298: Allow admin to opt out of admin analytics tracking - edited files based on review --- .../{ViewModel => Block}/Notification.php | 21 +- .../Adminhtml/Config/DisableAdminUsage.php | 8 +- .../Adminhtml/Config/EnableAdminUsage.php | 8 +- .../Model/Condition/CanViewNotification.php | 9 +- .../Model/ResourceModel/Viewer/Logger.php | 9 +- .../AdminAnalytics/Model/Viewer/Log.php | 2 +- .../AdminUsageNotificationDataProvider.php | 184 +----------------- .../Magento/AdminAnalytics/etc/db_schema.xml | 2 +- .../layout/adminhtml_dashboard_index.xml | 2 +- 9 files changed, 36 insertions(+), 209 deletions(-) rename app/code/Magento/AdminAnalytics/{ViewModel => Block}/Notification.php (57%) diff --git a/app/code/Magento/AdminAnalytics/ViewModel/Notification.php b/app/code/Magento/AdminAnalytics/Block/Notification.php similarity index 57% rename from app/code/Magento/AdminAnalytics/ViewModel/Notification.php rename to app/code/Magento/AdminAnalytics/Block/Notification.php index 5b4a51c5b6539..7988e2139a3d3 100644 --- a/app/code/Magento/AdminAnalytics/ViewModel/Notification.php +++ b/app/code/Magento/AdminAnalytics/Block/Notification.php @@ -3,32 +3,37 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\AdminAnalytics\ViewModel; +namespace Magento\AdminAnalytics\Block; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\AdminAnalytics\Model\Condition\CanViewNotification as AdminAnalyticsNotification; +use Magento\ReleaseNotification\Model\Condition\CanViewNotification as ReleaseNotification; /** * Class Notification */ -class Notification implements \Magento\Framework\View\Element\Block\ArgumentInterface +class Notification implements ArgumentInterface { /** - * @var \Magento\AdminAnalytics\Model\Condition\CanViewNotification + * @var AdminAnalyticsNotification */ private $canViewNotificationAnalytics; /** - * @var \Magento\ReleaseNotification\Model\Condition\CanViewNotification + * @var ReleaseNotification */ private $canViewNotificationRelease; /** * Notification constructor. - * @param \Magento\AdminAnalytics\Model\Condition\CanViewNotification $canViewNotificationAnalytics - * @param \Magento\ReleaseNotification\Model\Condition\CanViewNotification $canViewNotificationRelease + * @param AdminAnalyticsNotification $canViewNotificationAnalytics + * @param ReleaseNotification $canViewNotificationRelease */ public function __construct( - \Magento\AdminAnalytics\Model\Condition\CanViewNotification $canViewNotificationAnalytics, - \Magento\ReleaseNotification\Model\Condition\CanViewNotification $canViewNotificationRelease + AdminAnalyticsNotification $canViewNotificationAnalytics, + ReleaseNotification $canViewNotificationRelease ) { $this->canViewNotificationAnalytics = $canViewNotificationAnalytics; $this->canViewNotificationRelease = $canViewNotificationRelease; diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 3bd5fe8afbebf..29a9479d72a96 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -8,7 +8,7 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; @@ -67,7 +67,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - public function disableAdminUsage() + private function disableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 0); @@ -79,7 +79,7 @@ public function disableAdminUsage() * * @return ResultInterface */ - public function markUserNotified() : ResultInterface + private function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( @@ -108,7 +108,7 @@ public function execute() * * @return bool */ - public function _isAllowed() + protected function _isAllowed() { $isAllowed = parent::_isAllowed(); return $isAllowed; diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index ed55f8556df30..35113d4a6b737 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -8,7 +8,7 @@ namespace Magento\AdminAnalytics\Controller\Adminhtml\Config; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; @@ -66,7 +66,7 @@ public function __construct( /** * Changes the value of config/admin/usage/enabled */ - public function enableAdminUsage() + private function enableAdminUsage() { $configModel = $this->configFactory->create(); $configModel->setDataByPath('admin/usage/enabled', 1); @@ -78,7 +78,7 @@ public function enableAdminUsage() * * @return ResultInterface */ - public function markUserNotified() : ResultInterface + private function markUserNotified() : ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( @@ -107,7 +107,7 @@ public function execute() * * @return bool */ - public function _isAllowed() + protected function _isAllowed() { $isAllowed = parent::_isAllowed(); return $isAllowed; diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 03b99f404e01e..1012f205d6bf3 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -10,7 +10,6 @@ use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger; use Magento\Framework\View\Layout\Condition\VisibilityConditionInterface; use Magento\Framework\App\CacheInterface; -use function Magento\PAT\Reports\Utils\readResponseTimeReport; /** * Dynamic validator for UI admin analytics notification, manage UI component visibility. @@ -42,8 +41,6 @@ class CanViewNotification implements VisibilityConditionInterface private $cacheStorage; /** - * CanViewNotification constructor. - * * @param Logger $viewerLogger * @param CacheInterface $cacheStorage */ @@ -58,10 +55,10 @@ public function __construct( /** * Validate if notification popup can be shown and set the notification flag * - * @param array $arguments Attributes from element node. + * @param array $arguments Attributes from element node. * @inheritdoc */ - public function isVisible(array $arguments) + public function isVisible(array $arguments): bool { $cacheKey = self::$cachePrefix; $value = $this->cacheStorage->load($cacheKey); @@ -80,7 +77,7 @@ public function isVisible(array $arguments) * * @return string */ - public function getName() + public function getName(): string { return self::$conditionName; } diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index 38fdd2afed7a7..b185e263f4801 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -74,7 +74,7 @@ public function log(string $lastViewVersion) : bool */ public function get() : Log { - return $this->logFactory->create(['data' => $this->loadLogData()]); + return $this->logFactory->create(['data' => $this->loadLatestLogData()]); } /** @@ -84,7 +84,7 @@ public function get() : Log */ public function checkLogExists() : bool { - $data = $this->logFactory->create(['data' => $this->loadLogData()]); + $data = $this->logFactory->create(['data' => $this->loadLatestLogData()]); $lastViewedVersion = $data->getLastViewVersion(); return isset($lastViewedVersion); } @@ -94,11 +94,12 @@ public function checkLogExists() : bool * * @return array */ - private function loadLogData() : array + private function loadLatestLogData() : array { $connection = $this->resource->getConnection(); $select = $connection->select() - ->from($this->resource->getTableName(self::LOG_TABLE_NAME)) + ->from(['log_table' => $this->resource->getTableName(self::LOG_TABLE_NAME)]) + ->order('log_table.id desc') ->limit(['count' => 1]); $data = $connection->fetchRow($select); diff --git a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php index cd29783113d03..0c3b6b81ec811 100644 --- a/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php +++ b/app/code/Magento/AdminAnalytics/Model/Viewer/Log.php @@ -19,7 +19,7 @@ class Log extends DataObject * * @return int */ - public function getId() : int + public function getId() : ?int { return $this->getData('id'); } diff --git a/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php index 5e8bc2d9160d4..961a5663730a2 100644 --- a/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php +++ b/app/code/Magento/AdminAnalytics/Ui/DataProvider/AdminUsageNotificationDataProvider.php @@ -6,81 +6,14 @@ namespace Magento\AdminAnalytics\Ui\DataProvider; -use Magento\Framework\Api\Search\SearchCriteriaInterface; -use Magento\Framework\Api\Search\SearchResultInterface; -use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface; -use Magento\Ui\DataProvider\Modifier\ModifierInterface; -use Magento\Ui\DataProvider\Modifier\PoolInterface; +use Magento\Ui\DataProvider\AbstractDataProvider; +use Magento\Framework\Api\Filter; /** * Data Provider for the Admin usage UI component. */ -class AdminUsageNotificationDataProvider implements DataProviderInterface +class AdminUsageNotificationDataProvider extends AbstractDataProvider { - /** - * @var PoolInterface - */ - private $pool; - - /** - * Search result object. - * - * @var SearchResultInterface - */ - private $searchResult; - - /** - * Search criteria object. - * - * @var SearchCriteriaInterface - */ - private $searchCriteria; - - /** - * Own name of this provider. - * - * @var string - */ - private $name; - - /** - * Provider configuration data. - * - * @var array - */ - private $data; - - /** - * Provider configuration meta. - * - * @var array - */ - private $meta; - - /** - * @param string $name - * @param SearchResultInterface $searchResult - * @param SearchCriteriaInterface $searchCriteria - * @param PoolInterface $pool - * @param array $meta - * @param array $data - */ - public function __construct( - $name, - SearchResultInterface $searchResult, - SearchCriteriaInterface $searchCriteria, - PoolInterface $pool, - array $meta = [], - array $data = [] - ) { - $this->name = $name; - $this->searchResult = $searchResult; - $this->searchCriteria = $searchCriteria; - $this->pool = $pool; - $this->meta = $meta; - $this->data = $data; - } - /** * @inheritdoc */ @@ -92,117 +25,8 @@ public function getData() /** * @inheritdoc */ - public function getMeta() - { - return $this->meta; - } - - /** - * @inheritdoc - */ - public function getName() - { - return $this->name; - } - - /** - * @inheritdoc - */ - public function getConfigData() - { - return $this->data['config'] ?? []; - } - - /** - * @inheritdoc - */ - public function setConfigData($config) - { - $this->data['config'] = $config; - - return true; - } - - /** - * @inheritdoc - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getFieldMetaInfo($fieldSetName, $fieldName) - { - return []; - } - - /** - * @inheritdoc - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getFieldSetMetaInfo($fieldSetName) - { - return []; - } - - /** - * @inheritdoc - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getFieldsMetaInfo($fieldSetName) - { - return []; - } - - /** - * @inheritdoc - */ - public function getPrimaryFieldName() - { - return 'admin_analytics'; - } - - /** - * @inheritdoc - */ - public function getRequestFieldName() - { - return 'admin_analytics'; - } - - /** - * @inheritdoc - */ - public function addFilter(\Magento\Framework\Api\Filter $filter) + public function addFilter(Filter $filter) { return null; } - - /** - * @inheritdoc - */ - public function addOrder($field, $direction) - { - return null; - } - - /** - * @inheritdoc - */ - public function setLimit($offset, $size) - { - return null; - } - - /** - * @inheritdoc - */ - public function getSearchCriteria() - { - return $this->searchCriteria; - } - - /** - * @inheritdoc - */ - public function getSearchResult() - { - return $this->searchResult; - } } diff --git a/app/code/Magento/AdminAnalytics/etc/db_schema.xml b/app/code/Magento/AdminAnalytics/etc/db_schema.xml index b9607ce77d150..ef1a657dc8243 100644 --- a/app/code/Magento/AdminAnalytics/etc/db_schema.xml +++ b/app/code/Magento/AdminAnalytics/etc/db_schema.xml @@ -20,4 +20,4 @@ <column name="last_viewed_in_version"/> </constraint> </table> -</schema> \ No newline at end of file +</schema> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml index 3069db1ecc2bb..829c5ed3f8103 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -14,7 +14,7 @@ </uiComponent> <block name="tracking_notification" as="tracking_notification" template="Magento_AdminAnalytics::notification.phtml"> <arguments> - <argument name="notification" xsi:type="object">Magento\AdminAnalytics\ViewModel\Notification</argument> + <argument name="notification" xsi:type="object">Magento\AdminAnalytics\Block\Notification</argument> </arguments> </block> </referenceContainer> From 6b5c503e5e1795dde824707f951dd3ea1c7fddb4 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Wed, 7 Aug 2019 13:32:53 -0500 Subject: [PATCH 0337/2437] MC-15298: Allow admin to opt out of admin analytics tracking - static and health fix --- .../Adminhtml/Config/DisableAdminUsage.php | 16 ++++--------- .../Adminhtml/Config/EnableAdminUsage.php | 16 ++++--------- composer.lock | 23 +++++++++---------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 29a9479d72a96..01790b4a417d1 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -41,6 +41,11 @@ class DisableAdminUsage extends Action implements HttpPostActionInterface */ private $logger; + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Backend::admin'; + /** * DisableAdminUsage constructor. * @@ -102,15 +107,4 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } - - /** - * Checks if DisableAdminUsage is allowed - * - * @return bool - */ - protected function _isAllowed() - { - $isAllowed = parent::_isAllowed(); - return $isAllowed; - } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 35113d4a6b737..d61f69e484fdd 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -40,6 +40,11 @@ class EnableAdminUsage extends Action implements HttpPostActionInterface */ private $logger; + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Backend::admin'; + /** * MarkUserNotified constructor. * @@ -101,15 +106,4 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } - - /** - * Checks if EnableAdminUsage is allowed - * - * @return bool - */ - protected function _isAllowed() - { - $isAllowed = parent::_isAllowed(); - return $isAllowed; - } } diff --git a/composer.lock b/composer.lock index 768b9fec82f28..675848c495915 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - - "content-hash": "a4299e3f4f0d4dd4915f37a5dde8e2ed", + "content-hash": "b5c5eeedfc8a724202af911b637f7b16", "packages": [ { "name": "braintree/braintree_php", @@ -1553,28 +1552,28 @@ "authors": [ { "name": "Jim Wigginton", - "role": "Lead Developer", - "email": "terrafrost@php.net" + "email": "terrafrost@php.net", + "role": "Lead Developer" }, { "name": "Patrick Monnerat", - "role": "Developer", - "email": "pm@datasphere.ch" + "email": "pm@datasphere.ch", + "role": "Developer" }, { "name": "Andreas Fischer", - "role": "Developer", - "email": "bantu@phpbb.com" + "email": "bantu@phpbb.com", + "role": "Developer" }, { "name": "Hans-Jürgen Petrich", - "role": "Developer", - "email": "petrich@tronic-media.com" + "email": "petrich@tronic-media.com", + "role": "Developer" }, { "name": "Graham Campbell", - "role": "Developer", - "email": "graham@alt-three.com" + "email": "graham@alt-three.com", + "role": "Developer" } ], "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", From 7ebf19be1f1bf6c9814c42df09cbb1c83ceff25f Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Wed, 7 Aug 2019 13:35:34 -0500 Subject: [PATCH 0338/2437] MC-15298: Allow admin to opt out of admin analytics tracking - static and health fix --- .../Controller/Adminhtml/Config/DisableAdminUsage.php | 10 +++++----- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 01790b4a417d1..48937c3019caf 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -21,6 +21,11 @@ */ class DisableAdminUsage extends Action implements HttpPostActionInterface { + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Backend::admin'; + /** * @var Factory */ @@ -41,11 +46,6 @@ class DisableAdminUsage extends Action implements HttpPostActionInterface */ private $logger; - /** - * Authorization level of a basic admin session - */ - const ADMIN_RESOURCE = 'Magento_Backend::admin'; - /** * DisableAdminUsage constructor. * diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index d61f69e484fdd..9d16a789a2aa4 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -21,6 +21,11 @@ */ class EnableAdminUsage extends Action implements HttpPostActionInterface { + /** + * Authorization level of a basic admin session + */ + const ADMIN_RESOURCE = 'Magento_Backend::admin'; + /** * @var Factory */ @@ -40,11 +45,6 @@ class EnableAdminUsage extends Action implements HttpPostActionInterface */ private $logger; - /** - * Authorization level of a basic admin session - */ - const ADMIN_RESOURCE = 'Magento_Backend::admin'; - /** * MarkUserNotified constructor. * From 70c27d391c1aa9b5aa031f2309bf1ad919aceb5f Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Wed, 7 Aug 2019 14:46:13 -0500 Subject: [PATCH 0339/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added protected isAllowed --- .../Adminhtml/Config/DisableAdminUsage.php | 24 +++++++----------- .../Adminhtml/Config/EnableAdminUsage.php | 25 ++++++++----------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 48937c3019caf..13a13e8d24036 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -13,7 +13,6 @@ use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\Controller\ResultInterface; -use Psr\Log\LoggerInterface; use Magento\Config\Model\Config\Factory; /** @@ -21,11 +20,6 @@ */ class DisableAdminUsage extends Action implements HttpPostActionInterface { - /** - * Authorization level of a basic admin session - */ - const ADMIN_RESOURCE = 'Magento_Backend::admin'; - /** * @var Factory */ @@ -41,11 +35,6 @@ class DisableAdminUsage extends Action implements HttpPostActionInterface */ private $notificationLogger; - /** - * @var LoggerInterface - */ - private $logger; - /** * DisableAdminUsage constructor. * @@ -53,20 +42,17 @@ class DisableAdminUsage extends Action implements HttpPostActionInterface * @param ProductMetadataInterface $productMetadata * @param NotificationLogger $notificationLogger * @param Factory $configFactory - * @param LoggerInterface $logger */ public function __construct( Action\Context $context, ProductMetadataInterface $productMetadata, NotificationLogger $notificationLogger, - Factory $configFactory, - LoggerInterface $logger + Factory $configFactory ) { parent::__construct($context); $this->configFactory = $configFactory; $this->productMetadata = $productMetadata; $this->notificationLogger = $notificationLogger; - $this->logger = $logger; } /** @@ -107,4 +93,12 @@ public function execute() $this->disableAdminUsage(); $this->markUserNotified(); } + + /** + * @inheritDoc + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed(static::ADMIN_RESOURCE); + } } diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index 9d16a789a2aa4..aee300c2f6e84 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -13,7 +13,6 @@ use Magento\AdminAnalytics\Model\ResourceModel\Viewer\Logger as NotificationLogger; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\Controller\ResultInterface; -use Psr\Log\LoggerInterface; use Magento\Config\Model\Config\Factory; /** @@ -21,15 +20,11 @@ */ class EnableAdminUsage extends Action implements HttpPostActionInterface { - /** - * Authorization level of a basic admin session - */ - const ADMIN_RESOURCE = 'Magento_Backend::admin'; - /** * @var Factory */ private $configFactory; + /** * @var ProductMetadataInterface */ @@ -40,11 +35,6 @@ class EnableAdminUsage extends Action implements HttpPostActionInterface */ private $notificationLogger; - /** - * @var LoggerInterface - */ - private $logger; - /** * MarkUserNotified constructor. * @@ -52,20 +42,17 @@ class EnableAdminUsage extends Action implements HttpPostActionInterface * @param ProductMetadataInterface $productMetadata * @param NotificationLogger $notificationLogger * @param Factory $configFactory - * @param LoggerInterface $logger */ public function __construct( Action\Context $context, ProductMetadataInterface $productMetadata, NotificationLogger $notificationLogger, - Factory $configFactory, - LoggerInterface $logger + Factory $configFactory ) { parent::__construct($context); $this->configFactory = $configFactory; $this->productMetadata = $productMetadata; $this->notificationLogger = $notificationLogger; - $this->logger = $logger; } /** @@ -106,4 +93,12 @@ public function execute() $this->enableAdminUsage(); $this->markUserNotified(); } + + /** + * @inheritDoc + */ + protected function _isAllowed() + { + return $this->_authorization->isAllowed(static::ADMIN_RESOURCE); + } } From ba2390fe8b468a757fb9e4c8465d0f8577d4deda Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Thu, 8 Aug 2019 09:06:35 -0500 Subject: [PATCH 0340/2437] MC-15298: Allow admin to opt out of admin analytics tracking - added config, rm packaged-lock --- .../LoginAdminWithCredentialsActionGroup.xml | 14 + .../ActionGroup/LoginAsAdminActionGroup.xml | 14 + .../Magento/AdminAnalytics/etc/config.xml | 18 + .../LoginAdminWithCredentialsActionGroup.xml | 1 - .../ActionGroup/LoginAsAdminActionGroup.xml | 1 - package-lock.json | 861 ------------------ 6 files changed, 46 insertions(+), 863 deletions(-) create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml create mode 100644 app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml create mode 100644 app/code/Magento/AdminAnalytics/etc/config.xml delete mode 100644 package-lock.json diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml new file mode 100644 index 0000000000000..d9f5e5dbcb106 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoginAdminWithCredentialsActionGroup"> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml new file mode 100644 index 0000000000000..5cf7be8a6fe11 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoginAsAdmin"> + <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible" before="closeAdminNotification"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/AdminAnalytics/etc/config.xml b/app/code/Magento/AdminAnalytics/etc/config.xml new file mode 100644 index 0000000000000..ba683f13c11e3 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/config.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <admin> + <usage> + <enabled> + 1 + </enabled> + </usage> + </admin> + </default> +</config> \ No newline at end of file diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml index e423c68dd938b..6aaa612b249b6 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAdminWithCredentialsActionGroup.xml @@ -17,7 +17,6 @@ <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser}}" stepKey="fillUsername"/> <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminPassword}}" stepKey="fillPassword"/> <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <closeAdminNotification stepKey="closeAdminNotification"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml index 9c1963119d381..b2fbadcbe38e2 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginAsAdminActionGroup.xml @@ -16,7 +16,6 @@ <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{adminUser.username}}" stepKey="fillUsername"/> <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{adminUser.password}}" stepKey="fillPassword"/> <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> - <conditionalClick selector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" dependentSelector="{{AdminUsageNotificationSection.adminUsageDontAllowButton}}" visible="true" stepKey="clickDontAllowButtonIfVisible"/> <closeAdminNotification stepKey="closeAdminNotification"/> </actionGroup> </actionGroups> diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b3e14ddab5aa6..0000000000000 --- a/package-lock.json +++ /dev/null @@ -1,861 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "acorn": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", - "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==" - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==" - }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz", - "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==", - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^6.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", - "integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==", - "requires": { - "eslint-visitor-keys": "^1.0.0" - } - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" - }, - "espree": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", - "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "requires": { - "flat-cache": "^2.0.1" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" - }, - "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - } - } - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz", - "integrity": "sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==", - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "requires": { - "mkdirp": "^0.5.1" - } - } - } -} From 3b0d7b7ef25e854fba0f437b5ce18b84fb51aa5f Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Thu, 8 Aug 2019 10:46:39 -0500 Subject: [PATCH 0341/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Updated files based on review --- app/code/Magento/AdminAnalytics/Block/Metadata.php | 4 ++-- .../Magento/AdminAnalytics/Block/Notification.php | 1 - .../Adminhtml/Config/DisableAdminUsage.php | 10 +++++----- .../Controller/Adminhtml/Config/EnableAdminUsage.php | 12 +++++------- .../Model/Condition/CanViewNotification.php | 4 ++-- .../Model/ResourceModel/Viewer/Logger.php | 8 ++++---- app/code/Magento/AdminAnalytics/README.md | 2 +- .../Test/Mftf/Section/AdminUsageConfigSection.xml | 1 - 8 files changed, 19 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/Block/Metadata.php b/app/code/Magento/AdminAnalytics/Block/Metadata.php index 2a669829a8211..30a911d996f50 100644 --- a/app/code/Magento/AdminAnalytics/Block/Metadata.php +++ b/app/code/Magento/AdminAnalytics/Block/Metadata.php @@ -5,8 +5,8 @@ */ namespace Magento\AdminAnalytics\Block; -use Magento\Framework\View\Element\Template; -use Magento\Framework\View\Element\Template\Context; +use Magento\Backend\Block\Template; +use Magento\Backend\Block\Template\Context; use Magento\Framework\App\ProductMetadataInterface; use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\State; diff --git a/app/code/Magento/AdminAnalytics/Block/Notification.php b/app/code/Magento/AdminAnalytics/Block/Notification.php index 7988e2139a3d3..1b4314e585b74 100644 --- a/app/code/Magento/AdminAnalytics/Block/Notification.php +++ b/app/code/Magento/AdminAnalytics/Block/Notification.php @@ -27,7 +27,6 @@ class Notification implements ArgumentInterface private $canViewNotificationRelease; /** - * Notification constructor. * @param AdminAnalyticsNotification $canViewNotificationAnalytics * @param ReleaseNotification $canViewNotificationRelease */ diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php index 13a13e8d24036..34a9ef4f75b98 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/DisableAdminUsage.php @@ -38,10 +38,10 @@ class DisableAdminUsage extends Action implements HttpPostActionInterface /** * DisableAdminUsage constructor. * - * @param Action\Context $context + * @param Action\Context $context * @param ProductMetadataInterface $productMetadata - * @param NotificationLogger $notificationLogger - * @param Factory $configFactory + * @param NotificationLogger $notificationLogger + * @param Factory $configFactory */ public function __construct( Action\Context $context, @@ -56,7 +56,7 @@ public function __construct( } /** - * Changes the value of config/admin/usage/enabled + * Change the value of config/admin/usage/enabled */ private function disableAdminUsage() { @@ -70,7 +70,7 @@ private function disableAdminUsage() * * @return ResultInterface */ - private function markUserNotified() : ResultInterface + private function markUserNotified(): ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( diff --git a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php index aee300c2f6e84..f70dd57aa59d6 100644 --- a/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php +++ b/app/code/Magento/AdminAnalytics/Controller/Adminhtml/Config/EnableAdminUsage.php @@ -36,12 +36,10 @@ class EnableAdminUsage extends Action implements HttpPostActionInterface private $notificationLogger; /** - * MarkUserNotified constructor. - * - * @param Action\Context $context + * @param Action\Context $context * @param ProductMetadataInterface $productMetadata - * @param NotificationLogger $notificationLogger - * @param Factory $configFactory + * @param NotificationLogger $notificationLogger + * @param Factory $configFactory */ public function __construct( Action\Context $context, @@ -56,7 +54,7 @@ public function __construct( } /** - * Changes the value of config/admin/usage/enabled + * Change the value of config/admin/usage/enabled */ private function enableAdminUsage() { @@ -70,7 +68,7 @@ private function enableAdminUsage() * * @return ResultInterface */ - private function markUserNotified() : ResultInterface + private function markUserNotified(): ResultInterface { $responseContent = [ 'success' => $this->notificationLogger->log( diff --git a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php index 1012f205d6bf3..222261d4abfb5 100644 --- a/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php +++ b/app/code/Magento/AdminAnalytics/Model/Condition/CanViewNotification.php @@ -12,7 +12,7 @@ use Magento\Framework\App\CacheInterface; /** - * Dynamic validator for UI admin analytics notification, manage UI component visibility. + * Dynamic validator for UI admin analytics notification, control UI component visibility. */ class CanViewNotification implements VisibilityConditionInterface { @@ -41,7 +41,7 @@ class CanViewNotification implements VisibilityConditionInterface private $cacheStorage; /** - * @param Logger $viewerLogger + * @param Logger $viewerLogger * @param CacheInterface $cacheStorage */ public function __construct( diff --git a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php index b185e263f4801..5c225ebfeceb5 100644 --- a/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php +++ b/app/code/Magento/AdminAnalytics/Model/ResourceModel/Viewer/Logger.php @@ -51,7 +51,7 @@ public function __construct( * @param string $lastViewVersion * @return bool */ - public function log(string $lastViewVersion) : bool + public function log(string $lastViewVersion): bool { /** @var \Magento\Framework\DB\Adapter\AdapterInterface $connection */ $connection = $this->resource->getConnection(ResourceConnection::DEFAULT_CONNECTION); @@ -72,7 +72,7 @@ public function log(string $lastViewVersion) : bool * * @return Log */ - public function get() : Log + public function get(): Log { return $this->logFactory->create(['data' => $this->loadLatestLogData()]); } @@ -82,7 +82,7 @@ public function get() : Log * * @return boolean */ - public function checkLogExists() : bool + public function checkLogExists(): bool { $data = $this->logFactory->create(['data' => $this->loadLatestLogData()]); $lastViewedVersion = $data->getLastViewVersion(); @@ -94,7 +94,7 @@ public function checkLogExists() : bool * * @return array */ - private function loadLatestLogData() : array + private function loadLatestLogData(): array { $connection = $this->resource->getConnection(); $select = $connection->select() diff --git a/app/code/Magento/AdminAnalytics/README.md b/app/code/Magento/AdminAnalytics/README.md index 1280e0fcef10f..1aaf204ba04bb 100644 --- a/app/code/Magento/AdminAnalytics/README.md +++ b/app/code/Magento/AdminAnalytics/README.md @@ -1 +1 @@ -The purpose of Magento\AdminAnalytics module is gather information on which features the users uses and sends it to adobe analytics \ No newline at end of file +The purpose of Magento\AdminAnalytics module is to gather information on which features the admins use, to help improve Magento admin experience. \ No newline at end of file diff --git a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml index ddb89f0bfb72c..634ebf855d940 100644 --- a/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml +++ b/app/code/Magento/AdminAnalytics/Test/Mftf/Section/AdminUsageConfigSection.xml @@ -9,7 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminUsageConfigSection"> - <element name="adminUsageHeader" type="text" selector="#admin_usage-head"/> <element name="adminUsageOptions" type="select" selector="#admin_usage_enabled"/> </section> From f96b7aed68d649bdd03293620d4ef0f2948e62cc Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Thu, 8 Aug 2019 11:00:49 -0500 Subject: [PATCH 0342/2437] MC-15298: Allow admin to opt out of admin analytics tracking - Updated README --- app/code/Magento/AdminAnalytics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/AdminAnalytics/README.md b/app/code/Magento/AdminAnalytics/README.md index 1aaf204ba04bb..e905344031ad3 100644 --- a/app/code/Magento/AdminAnalytics/README.md +++ b/app/code/Magento/AdminAnalytics/README.md @@ -1 +1 @@ -The purpose of Magento\AdminAnalytics module is to gather information on which features the admins use, to help improve Magento admin experience. \ No newline at end of file +The Magento\AdminAnalytics module gathers information about the features Magento administrators use. This information will be used to help improve the user experience on the Magento Admin. \ No newline at end of file From e70e30484b746b316575014d0727fbe092a9bec3 Mon Sep 17 00:00:00 2001 From: michellbrito <michellbp@msn.com> Date: Thu, 8 Aug 2019 14:37:42 -0500 Subject: [PATCH 0343/2437] MC-15298: Allow admin to opt out of admin analytics tracking - removed config --- app/code/Magento/AdminAnalytics/etc/config.xml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 app/code/Magento/AdminAnalytics/etc/config.xml diff --git a/app/code/Magento/AdminAnalytics/etc/config.xml b/app/code/Magento/AdminAnalytics/etc/config.xml deleted file mode 100644 index ba683f13c11e3..0000000000000 --- a/app/code/Magento/AdminAnalytics/etc/config.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> - <default> - <admin> - <usage> - <enabled> - 1 - </enabled> - </usage> - </admin> - </default> -</config> \ No newline at end of file From 912f8c5a963383655584a074c7d7c0cf1a9057bc Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Thu, 8 Aug 2019 16:24:59 -0500 Subject: [PATCH 0344/2437] MC-15298: Allow admin to opt out of admin analytics tracking --- .../{Block => ViewModel}/Metadata.php | 16 ++++------------ .../{Block => ViewModel}/Notification.php | 4 ++-- .../layout/adminhtml_dashboard_index.xml | 2 +- .../view/adminhtml/layout/default.xml | 3 ++- .../view/adminhtml/templates/tracking.phtml | 6 +++--- 5 files changed, 12 insertions(+), 19 deletions(-) rename app/code/Magento/AdminAnalytics/{Block => ViewModel}/Metadata.php (81%) rename app/code/Magento/AdminAnalytics/{Block => ViewModel}/Notification.php (93%) diff --git a/app/code/Magento/AdminAnalytics/Block/Metadata.php b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php similarity index 81% rename from app/code/Magento/AdminAnalytics/Block/Metadata.php rename to app/code/Magento/AdminAnalytics/ViewModel/Metadata.php index 30a911d996f50..9b1accbe0c823 100644 --- a/app/code/Magento/AdminAnalytics/Block/Metadata.php +++ b/app/code/Magento/AdminAnalytics/ViewModel/Metadata.php @@ -3,20 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\AdminAnalytics\Block; +namespace Magento\AdminAnalytics\ViewModel; -use Magento\Backend\Block\Template; -use Magento\Backend\Block\Template\Context; use Magento\Framework\App\ProductMetadataInterface; use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\State; +use Magento\Framework\View\Element\Block\ArgumentInterface; /** * Gets user version and mode - * - * @api */ -class Metadata extends Template +class Metadata implements ArgumentInterface { /** * @var State @@ -34,23 +31,18 @@ class Metadata extends Template private $productMetadata; /** - * @param Context $context * @param ProductMetadataInterface $productMetadata * @param Session $authSession * @param State $appState - * @param array $data */ public function __construct( - Context $context, ProductMetadataInterface $productMetadata, Session $authSession, - State $appState, - array $data = [] + State $appState ) { $this->productMetadata = $productMetadata; $this->authSession = $authSession; $this->appState = $appState; - parent::__construct($context, $data); } /** diff --git a/app/code/Magento/AdminAnalytics/Block/Notification.php b/app/code/Magento/AdminAnalytics/ViewModel/Notification.php similarity index 93% rename from app/code/Magento/AdminAnalytics/Block/Notification.php rename to app/code/Magento/AdminAnalytics/ViewModel/Notification.php index 1b4314e585b74..030e027b83fce 100644 --- a/app/code/Magento/AdminAnalytics/Block/Notification.php +++ b/app/code/Magento/AdminAnalytics/ViewModel/Notification.php @@ -5,14 +5,14 @@ */ declare(strict_types=1); -namespace Magento\AdminAnalytics\Block; +namespace Magento\AdminAnalytics\ViewModel; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\AdminAnalytics\Model\Condition\CanViewNotification as AdminAnalyticsNotification; use Magento\ReleaseNotification\Model\Condition\CanViewNotification as ReleaseNotification; /** - * Class Notification + * Control display of admin analytics and release notification modals */ class Notification implements ArgumentInterface { diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml index 829c5ed3f8103..3069db1ecc2bb 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/adminhtml_dashboard_index.xml @@ -14,7 +14,7 @@ </uiComponent> <block name="tracking_notification" as="tracking_notification" template="Magento_AdminAnalytics::notification.phtml"> <arguments> - <argument name="notification" xsi:type="object">Magento\AdminAnalytics\Block\Notification</argument> + <argument name="notification" xsi:type="object">Magento\AdminAnalytics\ViewModel\Notification</argument> </arguments> </block> </referenceContainer> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml index 05f7df8284164..7e379a17c78d7 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/layout/default.xml @@ -8,9 +8,10 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" > <body> <referenceContainer name="header"> - <block class="Magento\AdminAnalytics\Block\Metadata" name="tracking" as="tracking" template="Magento_AdminAnalytics::tracking.phtml" ifconfig="admin/usage/enabled"> + <block name="tracking" as="tracking" template="Magento_AdminAnalytics::tracking.phtml" ifconfig="admin/usage/enabled"> <arguments> <argument name="tracking_url" xsi:type="string">//assets.adobedtm.com/launch-EN30eb7ffa064444f1b8b0368ef38fd3a9.min.js</argument> + <argument name="metadata" xsi:type="object">Magento\AdminAnalytics\ViewModel\Metadata</argument> </arguments> </block> </referenceContainer> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml index 03c4c848aa44a..0ea5c753c9337 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/templates/tracking.phtml @@ -8,8 +8,8 @@ <script src="<?= $block->escapeUrl($block->getTrackingUrl()) ?>" async></script> <script> var adminAnalyticsMetadata = { - "version": "<?= $block->escapeJs($block->getMagentoVersion()) ?>", - "user": "<?= $block->escapeJs($block->getCurrentUser()) ?>", - "mode": "<?= $block->escapeJs($block->getMode()) ?>" + "version": "<?= $block->escapeJs($block->getMetadata()->getMagentoVersion()) ?>", + "user": "<?= $block->escapeJs($block->getMetadata()->getCurrentUser()) ?>", + "mode": "<?= $block->escapeJs($block->getMetadata()->getMode()) ?>" }; </script> From fa4957fea80bdd3b675d5c0816fc33ece100cba9 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Tue, 6 Aug 2019 18:21:46 +0300 Subject: [PATCH 0345/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Unit tests for MC-6387 --- .../Test/Unit/Model/DirectpostTest.php | 590 ++++++++++-------- 1 file changed, 340 insertions(+), 250 deletions(-) diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php index 95c67f67852da..50cf7e86e8fce 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php @@ -3,8 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Authorizenet\Test\Unit\Model; +use Magento\Authorizenet\Helper\Backend\Data; +use Magento\Authorizenet\Helper\Data as HelperData; +use Magento\Authorizenet\Model\Directpost\Response; +use Magento\Authorizenet\Model\Directpost\Response\Factory as ResponseFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\Payment\Model\InfoInterface; +use Magento\Payment\Model\Method\ConfigInterface; use Magento\Sales\Api\PaymentFailuresInterface; use Magento\Framework\Simplexml\Element; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; @@ -13,118 +23,118 @@ use Magento\Authorizenet\Model\Request; use Magento\Authorizenet\Model\Directpost\Request\Factory; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Model\Order\Payment\Transaction; use Magento\Sales\Model\Order\Payment\Transaction\Repository as TransactionRepository; +use PHPUnit\Framework\MockObject_MockBuilder; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject; +use ReflectionClass; /** * Class DirectpostTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DirectpostTest extends \PHPUnit\Framework\TestCase +class DirectpostTest extends TestCase { const TOTAL_AMOUNT = 100.02; const INVOICE_NUM = '00000001'; const TRANSACTION_ID = '41a23x34fd124'; /** - * @var \Magento\Authorizenet\Model\Directpost + * @var Directpost */ protected $directpost; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|PHPUnit_Framework_MockObject_MockObject */ protected $scopeConfigMock; /** - * @var \Magento\Payment\Model\InfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InfoInterface|PHPUnit_Framework_MockObject_MockObject */ protected $paymentMock; /** - * @var \Magento\Authorizenet\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var HelperData|PHPUnit_Framework_MockObject_MockObject */ protected $dataHelperMock; /** - * @var \Magento\Authorizenet\Model\Directpost\Response\Factory|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseFactory|PHPUnit_Framework_MockObject_MockObject */ protected $responseFactoryMock; /** - * @var TransactionRepository|\PHPUnit_Framework_MockObject_MockObject + * @var TransactionRepository|PHPUnit_Framework_MockObject_MockObject */ protected $transactionRepositoryMock; /** - * @var \Magento\Authorizenet\Model\Directpost\Response|\PHPUnit_Framework_MockObject_MockObject + * @var Response|PHPUnit_Framework_MockObject_MockObject */ protected $responseMock; /** - * @var TransactionService|\PHPUnit_Framework_MockObject_MockObject + * @var TransactionService|PHPUnit_Framework_MockObject_MockObject */ protected $transactionServiceMock; /** - * @var \Magento\Framework\HTTP\ZendClient|\PHPUnit_Framework_MockObject_MockObject + * @var ZendClient|PHPUnit_Framework_MockObject_MockObject */ protected $httpClientMock; /** - * @var \Magento\Authorizenet\Model\Directpost\Request\Factory|\PHPUnit_Framework_MockObject_MockObject + * @var Factory|PHPUnit_Framework_MockObject_MockObject */ protected $requestFactory; /** - * @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject + * @var PaymentFailuresInterface|PHPUnit_Framework_MockObject_MockObject */ private $paymentFailures; + /** + * @var ZendClientFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $httpClientFactoryMock; + /** * @inheritdoc */ protected function setUp() { - $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) - ->getMock(); - $this->paymentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getOrder', 'getId', 'setAdditionalInformation', 'getAdditionalInformation', - 'setIsTransactionDenied', 'setIsTransactionClosed', 'decrypt', 'getCcLast4', - 'getParentTransactionId', 'getPoNumber' - ]) - ->getMock(); - $this->dataHelperMock = $this->getMockBuilder(\Magento\Authorizenet\Helper\Data::class) - ->disableOriginalConstructor() - ->getMock(); - + $this->initPaymentMock(); $this->initResponseFactoryMock(); + $this->initHttpClientMock(); - $this->transactionRepositoryMock = $this->getMockBuilder( - \Magento\Sales\Model\Order\Payment\Transaction\Repository::class - ) + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)->getMock(); + $this->dataHelperMock = $this->getMockBuilder(HelperData::class)->disableOriginalConstructor()->getMock(); + $this->transactionRepositoryMock = $this->getMockBuilder(TransactionRepository::class) ->disableOriginalConstructor() ->setMethods(['getByTransactionId']) ->getMock(); - - $this->transactionServiceMock = $this->getMockBuilder(\Magento\Authorizenet\Model\TransactionService::class) + $this->transactionServiceMock = $this->getMockBuilder(TransactionService::class) ->disableOriginalConstructor() ->setMethods(['getTransactionDetails']) ->getMock(); - - $this->paymentFailures = $this->getMockBuilder( - PaymentFailuresInterface::class - ) + $this->paymentFailures = $this->getMockBuilder(PaymentFailuresInterface::class) ->disableOriginalConstructor() ->getMock(); - - $this->requestFactory = $this->getRequestFactoryMock(); - $httpClientFactoryMock = $this->getHttpClientFactoryMock(); + $this->requestFactory = $this->getMockBuilder(Factory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->httpClientFactoryMock = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); $helper = new ObjectManagerHelper($this); $this->directpost = $helper->getObject( - \Magento\Authorizenet\Model\Directpost::class, + Directpost::class, [ 'scopeConfig' => $this->scopeConfigMock, 'dataHelper' => $this->dataHelperMock, @@ -132,18 +142,97 @@ protected function setUp() 'responseFactory' => $this->responseFactoryMock, 'transactionRepository' => $this->transactionRepositoryMock, 'transactionService' => $this->transactionServiceMock, - 'httpClientFactory' => $httpClientFactoryMock, + 'httpClientFactory' => $this->httpClientFactoryMock, 'paymentFailures' => $this->paymentFailures, ] ); } + /** + * Create mock for response factory + * + * @return void + */ + private function initResponseFactoryMock() + { + $this->responseFactoryMock = $this->getMockBuilder(ResponseFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->responseMock = $this->getMockBuilder(Response::class) + ->setMethods( + [ + 'isValidHash', + 'getXTransId', + 'getXResponseCode', + 'getXResponseReasonCode', + 'getXResponseReasonText', + 'getXAmount', + 'setXResponseCode', + 'setXResponseReasonCode', + 'setXAvsCode', + 'setXResponseReasonText', + 'setXTransId', + 'setXInvoiceNum', + 'setXAmount', + 'setXMethod', + 'setXType', + 'setData', + 'getData', + 'setXAccountNumber', + '__wakeup' + ] + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->responseFactoryMock->expects($this->any())->method('create')->willReturn($this->responseMock); + } + + /** + * Create mock for payment + * + * @return void + */ + private function initPaymentMock() + { + $this->paymentMock = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getOrder', + 'setAmount', + 'setAnetTransType', + 'setXTransId', + 'getId', + 'setAdditionalInformation', + 'getAdditionalInformation', + 'setIsTransactionDenied', + 'setIsTransactionClosed', + 'decrypt', + 'getCcLast4', + 'getParentTransactionId', + 'getPoNumber' + ] + ) + ->getMock(); + } + + /** + * Create a mock for http client + * + * @return void + */ + private function initHttpClientMock() + { + $this->httpClientMock = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->setMethods(['request', 'getBody', '__wakeup']) + ->getMock(); + } + public function testGetConfigInterface() { - $this->assertInstanceOf( - \Magento\Payment\Model\Method\ConfigInterface::class, - $this->directpost->getConfigInterface() - ); + $this->assertInstanceOf(ConfigInterface::class, $this->directpost->getConfigInterface()); } public function testGetConfigValue() @@ -162,7 +251,7 @@ public function testSetDataHelper() $storeId = 'store-id'; $expectedResult = 'relay-url'; - $helperDataMock = $this->getMockBuilder(\Magento\Authorizenet\Helper\Backend\Data::class) + $helperDataMock = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); @@ -179,7 +268,7 @@ public function testAuthorize() { $paymentAction = 'some_action'; - $this->scopeConfigMock->expects($this->any()) + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/authorizenet_directpost/payment_action', 'store', null) ->willReturn($paymentAction); @@ -190,11 +279,141 @@ public function testAuthorize() $this->directpost->authorize($this->paymentMock, 10); } + /** + * @dataProvider dataProviderCaptureWithInvalidAmount + * @expectedExceptionMessage Invalid amount for capture. + * @expectedException \Magento\Framework\Exception\LocalizedException + * + * @param int $invalidAmount + */ + public function testCaptureWithInvalidAmount($invalidAmount) + { + $this->directpost->capture($this->paymentMock, $invalidAmount); + } + + /** + * @return array + */ + public function dataProviderCaptureWithInvalidAmount() + { + return [ + [0], + [0.000], + [-1.000], + [-1], + [null], + ]; + } + + /** + * @@expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testCaptureHasParentTransactionId() + { + $amount = 10; + + $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); + $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(1); + $this->paymentMock->expects($this->once())->method('setAnetTransType')->willReturn('PRIOR_AUTH_CAPTURE'); + + $this->paymentMock->expects($this->once())->method('getId')->willReturn(1); + $orderMock = $this->getOrderMock(); + $orderMock->expects($this->once())->method('getId')->willReturn(1); + $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); + + $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); + $this->transactionRepositoryMock->expects($this->once()) + ->method('getByTransactionId') + ->with(1, 1, 1) + ->willReturn($transactionMock); + + $this->paymentMock->expects($this->once())->method('setXTransId'); + $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); + + $this->directpost->capture($this->paymentMock, 10); + } + + /** + * @@expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testCaptureWithoutParentTransactionId() + { + $amount = 10; + + $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); + $this->paymentMock->expects($this->once())->method('getParentTransactionId')->willReturn(null); + $this->responseMock->expects($this->once())->method('getData')->willReturn([1]); + + $this->directpost->capture($this->paymentMock, 10); + } + + public function testCaptureWithoutParentTransactionIdWithoutData() + { + $amount = 10; + + $this->paymentMock->expects($this->once())->method('setAmount')->with($amount); + $this->paymentMock->expects($this->exactly(2))->method('getParentTransactionId')->willReturn(null); + $this->responseMock->expects($this->once())->method('getData')->willReturn([]); + + $this->paymentMock->expects($this->once()) + ->method('setIsTransactionClosed') + ->with(0) + ->willReturnSelf(); + + $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); + $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); + + $this->buildRequestTest(); + $this->postRequestTest(); + + $this->directpost->capture($this->paymentMock, 10); + } + + private function buildRequestTest() + { + $orderMock = $this->getOrderMock(); + $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); + $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); + $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); + + $this->addRequestMockToRequestFactoryMock(); + } + + private function postRequestTest() + { + $this->httpClientFactoryMock->expects($this->once())->method('create')->willReturn($this->httpClientMock); + $this->httpClientMock->expects($this->once())->method('request')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXResponseCode')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXResponseReasonCode')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXResponseReasonText')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXAvsCode')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXTransId')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXInvoiceNum')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXAmount')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXMethod')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setXType')->willReturnSelf(); + $this->responseMock->expects($this->once())->method('setData')->willReturnSelf(); + + $response = $this->getRefundResponseBody( + Directpost::RESPONSE_CODE_APPROVED, + Directpost::RESPONSE_REASON_CODE_APPROVED, + 'Successful' + ); + $this->httpClientMock->expects($this->once())->method('getBody')->willReturn($response); + $this->responseMock->expects($this->once()) + ->method('getXResponseCode') + ->willReturn(Directpost::RESPONSE_CODE_APPROVED); + $this->responseMock->expects($this->once()) + ->method('getXResponseReasonCode') + ->willReturn(Directpost::RESPONSE_REASON_CODE_APPROVED); + $this->dataHelperMock->expects($this->never())->method('wrapGatewayError'); + } + public function testGetCgiUrl() { $url = 'cgi/url'; - $this->scopeConfigMock->expects($this->any()) + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/authorizenet_directpost/cgi_url', 'store', null) ->willReturn($url); @@ -204,7 +423,7 @@ public function testGetCgiUrl() public function testGetCgiUrlWithEmptyConfigValue() { - $this->scopeConfigMock->expects($this->any()) + $this->scopeConfigMock->expects($this->once()) ->method('getValue') ->with('payment/authorizenet_directpost/cgi_url', 'store', null) ->willReturn(null); @@ -218,7 +437,7 @@ public function testGetRelayUrl() $url = 'relay/url'; $this->directpost->setData('store', $storeId); - $this->dataHelperMock->expects($this->any()) + $this->dataHelperMock->expects($this->exactly(2)) ->method('getRelayUrl') ->with($storeId) ->willReturn($url); @@ -268,7 +487,7 @@ public function testValidateResponseFailure() */ protected function prepareTestValidateResponse($transMd5, $login, $isValidHash) { - $this->scopeConfigMock->expects($this->any()) + $this->scopeConfigMock->expects($this->exactly(2)) ->method('getValue') ->willReturnMap( [ @@ -276,7 +495,7 @@ protected function prepareTestValidateResponse($transMd5, $login, $isValidHash) ['payment/authorizenet_directpost/login', 'store', null, $login] ] ); - $this->responseMock->expects($this->any()) + $this->responseMock->expects($this->exactly(1)) ->method('isValidHash') ->with($transMd5, $login) ->willReturn($isValidHash); @@ -328,6 +547,23 @@ public function checkResponseCodeSuccessDataProvider() ]; } + /** + * Checks response failures behaviour. + * + * @param int $responseCode + * @param int $failuresHandlerCalls + * @return void + * + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testCheckResponseCodeFailureDefault(): void + { + $responseCode = 999999; + $this->responseMock->expects($this->once())->method('getXResponseCode')->willReturn($responseCode); + + $this->directpost->checkResponseCode(); + } + /** * Checks response failures behaviour. * @@ -338,34 +574,24 @@ public function checkResponseCodeSuccessDataProvider() * @expectedException \Magento\Framework\Exception\LocalizedException * @dataProvider checkResponseCodeFailureDataProvider */ - public function testCheckResponseCodeFailure(int $responseCode, int $failuresHandlerCalls): void + public function testCheckResponseCodeFailureDeclinedOrError(int $responseCode, int $failuresHandlerCalls): void { $reasonText = 'reason text'; $this->responseMock->expects($this->once()) ->method('getXResponseCode') ->willReturn($responseCode); - $this->responseMock->expects($this->any()) - ->method('getXResponseReasonText') - ->willReturn($reasonText); - $this->dataHelperMock->expects($this->any()) + $this->responseMock->expects($this->once())->method('getXResponseReasonText')->willReturn($reasonText); + $this->dataHelperMock->expects($this->once()) ->method('wrapGatewayError') ->with($reasonText) ->willReturn(__('Gateway error: %1', $reasonText)); - $orderMock = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); - - $orderMock->expects($this->exactly($failuresHandlerCalls)) - ->method('getQuoteId') - ->willReturn(1); - - $this->paymentFailures->expects($this->exactly($failuresHandlerCalls)) - ->method('handle') - ->with(1); + $this->paymentFailures->expects($this->exactly($failuresHandlerCalls))->method('handle')->with(1); + $orderMock = $this->getOrderMock($failuresHandlerCalls); - $reflection = new \ReflectionClass($this->directpost); + $orderMock->expects($this->exactly($failuresHandlerCalls))->method('getQuoteId')->willReturn(1); + $reflection = new ReflectionClass($this->directpost); $order = $reflection->getProperty('order'); $order->setAccessible(true); $order->setValue($this->directpost, $orderMock); @@ -381,7 +607,6 @@ public function checkResponseCodeFailureDataProvider(): array return [ ['responseCode' => Directpost::RESPONSE_CODE_DECLINED, 1], ['responseCode' => Directpost::RESPONSE_CODE_ERROR, 1], - ['responseCode' => 999999, 0], ]; } @@ -417,7 +642,7 @@ public function testCanCapture($isGatewayActionsLocked, $canCapture) { $this->directpost->setData('info_instance', $this->paymentMock); - $this->paymentMock->expects($this->any()) + $this->paymentMock->expects($this->once()) ->method('getAdditionalInformation') ->with(Directpost::GATEWAY_ACTIONS_LOCKED_STATE_KEY) ->willReturn($isGatewayActionsLocked); @@ -452,30 +677,16 @@ public function testFetchVoidedTransactionInfo($transactionId, $resultStatus, $r $paymentId = 36; $orderId = 36; - $this->paymentMock->expects(static::once()) - ->method('getId') - ->willReturn($paymentId); - - $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->disableOriginalConstructor() - ->setMethods(['getId', '__wakeup']) - ->getMock(); - $orderMock->expects(static::once()) - ->method('getId') - ->willReturn($orderId); + $this->paymentMock->expects($this->once())->method('getId')->willReturn($paymentId); - $this->paymentMock->expects(static::once()) - ->method('getOrder') - ->willReturn($orderMock); - - $transactionMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment\Transaction::class) - ->disableOriginalConstructor() - ->getMock(); - $this->transactionRepositoryMock->expects(static::once()) + $orderMock = $this->getOrderMock(); + $orderMock->expects($this->once())->method('getId')->willReturn($orderId); + $this->paymentMock->expects($this->once())->method('getOrder')->willReturn($orderMock); + $transactionMock = $this->getMockBuilder(Transaction::class)->disableOriginalConstructor()->getMock(); + $this->transactionRepositoryMock->expects($this->once()) ->method('getByTransactionId') ->with($transactionId, $paymentId, $orderId) ->willReturn($transactionMock); - $document = $this->getTransactionXmlDocument( $transactionId, TransactionService::PAYMENT_UPDATE_STATUS_CODE_SUCCESS, @@ -483,20 +694,15 @@ public function testFetchVoidedTransactionInfo($transactionId, $resultStatus, $r $responseStatus, $responseCode ); - $this->transactionServiceMock->expects(static::once()) + $this->transactionServiceMock->expects($this->once()) ->method('getTransactionDetails') ->with($this->directpost, $transactionId) ->willReturn($document); // transaction should be closed - $this->paymentMock->expects(static::once()) - ->method('setIsTransactionDenied') - ->with(true); - $this->paymentMock->expects(static::once()) - ->method('setIsTransactionClosed') - ->with(true); - $transactionMock->expects(static::once()) - ->method('close'); + $this->paymentMock->expects($this->once())->method('setIsTransactionDenied')->with(true); + $this->paymentMock->expects($this->once())->method('setIsTransactionClosed')->with(true); + $transactionMock->expects($this->once())->method('close'); $this->directpost->fetchTransactionInfo($this->paymentMock, $transactionId); } @@ -509,60 +715,41 @@ public function testSuccessRefund() { $card = 1111; - $this->paymentMock->expects(static::exactly(2)) - ->method('getCcLast4') - ->willReturn($card); - $this->paymentMock->expects(static::once()) - ->method('decrypt') - ->willReturn($card); - $this->paymentMock->expects(static::exactly(3)) + $this->paymentMock->expects($this->exactly(1))->method('getCcLast4')->willReturn($card); + $this->paymentMock->expects($this->once())->method('decrypt')->willReturn($card); + $this->paymentMock->expects($this->exactly(3)) ->method('getParentTransactionId') ->willReturn(self::TRANSACTION_ID . '-capture'); - $this->paymentMock->expects(static::once()) - ->method('getPoNumber') - ->willReturn(self::INVOICE_NUM); - $this->paymentMock->expects(static::once()) + $this->paymentMock->expects($this->once())->method('getPoNumber')->willReturn(self::INVOICE_NUM); + $this->paymentMock->expects($this->once()) ->method('setIsTransactionClosed') ->with(true) ->willReturnSelf(); + $this->addRequestMockToRequestFactoryMock(); + $orderMock = $this->getOrderMock(); - $this->paymentMock->expects(static::exactly(2)) - ->method('getOrder') - ->willReturn($orderMock); + $orderMock->expects($this->once())->method('getId')->willReturn(1); + $orderMock->expects($this->exactly(2))->method('getIncrementId')->willReturn(self::INVOICE_NUM); + $orderMock->expects($this->once())->method('getStoreId')->willReturn(1); + + $this->paymentMock->expects($this->exactly(2))->method('getOrder')->willReturn($orderMock); - $transactionMock = $this->getMockBuilder(Order\Payment\Transaction::class) + $transactionMock = $this->getMockBuilder(Transaction::class) ->disableOriginalConstructor() ->setMethods(['getAdditionalInformation']) ->getMock(); - $transactionMock->expects(static::once()) + $transactionMock->expects($this->once()) ->method('getAdditionalInformation') ->with(Directpost::REAL_TRANSACTION_ID_KEY) ->willReturn(self::TRANSACTION_ID); - $this->transactionRepositoryMock->expects(static::once()) + $this->transactionRepositoryMock->expects($this->once()) ->method('getByTransactionId') ->willReturn($transactionMock); - $response = $this->getRefundResponseBody( - Directpost::RESPONSE_CODE_APPROVED, - Directpost::RESPONSE_REASON_CODE_APPROVED, - 'Successful' - ); - $this->httpClientMock->expects(static::once()) - ->method('getBody') - ->willReturn($response); - - $this->responseMock->expects(static::once()) - ->method('getXResponseCode') - ->willReturn(Directpost::RESPONSE_CODE_APPROVED); - $this->responseMock->expects(static::once()) - ->method('getXResponseReasonCode') - ->willReturn(Directpost::RESPONSE_REASON_CODE_APPROVED); - - $this->dataHelperMock->expects(static::never()) - ->method('wrapGatewayError'); + $this->postRequestTest(); $this->directpost->refund($this->paymentMock, self::TOTAL_AMOUNT); } @@ -583,65 +770,6 @@ public function dataProviderTransaction() ]; } - /** - * Create mock for response factory - * @return void - */ - private function initResponseFactoryMock() - { - $this->responseFactoryMock = $this->getMockBuilder( - \Magento\Authorizenet\Model\Directpost\Response\Factory::class - )->disableOriginalConstructor()->getMock(); - $this->responseMock = $this->getMockBuilder(\Magento\Authorizenet\Model\Directpost\Response::class) - ->setMethods( - [ - 'isValidHash', - 'getXTransId', 'getXResponseCode', 'getXResponseReasonCode', 'getXResponseReasonText', 'getXAmount', - 'setXResponseCode', 'setXResponseReasonCode', 'setXAvsCode', 'setXResponseReasonText', - 'setXTransId', 'setXInvoiceNum', 'setXAmount', 'setXMethod', 'setXType', 'setData', - 'setXAccountNumber', - '__wakeup' - ] - ) - ->disableOriginalConstructor() - ->getMock(); - - $this->responseMock->expects(static::any()) - ->method('setXResponseCode') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXResponseReasonCode') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXResponseReasonText') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXAvsCode') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXTransId') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXInvoiceNum') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXAmount') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXMethod') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setXType') - ->willReturnSelf(); - $this->responseMock->expects(static::any()) - ->method('setData') - ->willReturnSelf(); - - $this->responseFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->responseMock); - } - /** * Get transaction data * @param $transactionId @@ -694,80 +822,40 @@ private function getTransactionXmlDocument( /** * Get mock for authorize.net request factory - * @return \PHPUnit\Framework\MockObject_MockBuilder */ - private function getRequestFactoryMock() + private function addRequestMockToRequestFactoryMock() { - $requestFactory = $this->getMockBuilder(Factory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); $request = $this->getMockBuilder(Request::class) ->disableOriginalConstructor() ->setMethods(['__wakeup']) ->getMock(); - $requestFactory->expects(static::any()) + $this->requestFactory->expects($this->once()) ->method('create') ->willReturn($request); - return $requestFactory; } /** * Get mock for order - * @return \PHPUnit_Framework_MockObject_MockObject + * @return PHPUnit_Framework_MockObject_MockObject */ private function getOrderMock() { - $orderMock = $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->setMethods([ - 'getId', 'getIncrementId', 'getStoreId', 'getBillingAddress', 'getShippingAddress', - 'getBaseCurrencyCode', 'getBaseTaxAmount', '__wakeup' - ]) - ->getMock(); - - $orderMock->expects(static::once()) - ->method('getId') - ->willReturn(1); - - $orderMock->expects(static::exactly(2)) - ->method('getIncrementId') - ->willReturn(self::INVOICE_NUM); - - $orderMock->expects(static::once()) - ->method('getStoreId') - ->willReturn(1); - - $orderMock->expects(static::once()) - ->method('getBaseCurrencyCode') - ->willReturn('USD'); - return $orderMock; - } - - /** - * Create and return mock for http client factory - * @return \PHPUnit_Framework_MockObject_MockObject - */ - private function getHttpClientFactoryMock() - { - $this->httpClientMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClient::class) + return $this->getMockBuilder(Order::class) ->disableOriginalConstructor() - ->setMethods(['request', 'getBody', '__wakeup']) - ->getMock(); - - $this->httpClientMock->expects(static::any()) - ->method('request') - ->willReturnSelf(); - - $httpClientFactoryMock = $this->getMockBuilder(\Magento\Framework\HTTP\ZendClientFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) + ->setMethods( + [ + 'getId', + 'getQuoteId', + 'getIncrementId', + 'getStoreId', + 'getBillingAddress', + 'getShippingAddress', + 'getBaseCurrencyCode', + 'getBaseTaxAmount', + '__wakeup' + ] + ) ->getMock(); - - $httpClientFactoryMock->expects(static::any()) - ->method('create') - ->willReturn($this->httpClientMock); - return $httpClientFactoryMock; } /** @@ -788,7 +876,9 @@ private function getRefundResponseBody($code, $reasonCode, $reasonText) $result[9] = self::TOTAL_AMOUNT; // XAmount $result[10] = Directpost::REQUEST_METHOD_CC; // XMethod $result[11] = Directpost::REQUEST_TYPE_CREDIT; // XType + // @codingStandardsIgnoreStart $result[37] = md5(self::TRANSACTION_ID); // x_MD5_Hash + // @codingStandardsIgnoreEnd $result[50] = '48329483921'; // setXAccountNumber return implode(Directpost::RESPONSE_DELIM_CHAR, $result); } From 6af74d9925c94280f8a7e773dc7d07b49abaf197 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 9 Aug 2019 15:48:03 +0300 Subject: [PATCH 0346/2437] MC-19023: PayPal Express Checkout Payflow Edition don't work (Internal Error) --- .../frontend/web/js/action/set-payment-information-extended.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js b/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js index 4085da82f4151..263d5747f2842 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js @@ -21,6 +21,8 @@ define([ var serviceUrl, payload; + delete paymentData.__disableTmpl; + skipBilling = skipBilling || false; payload = { cartId: quote.getQuoteId(), From faddcf9e32093dbbf94e684144d5e6272533294b Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Fri, 9 Aug 2019 15:19:30 -0500 Subject: [PATCH 0347/2437] MC-19115: Admin Analytics tracking should be enabled by default - Enabling tracking by Default --- app/code/Magento/AdminAnalytics/etc/config.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/code/Magento/AdminAnalytics/etc/config.xml diff --git a/app/code/Magento/AdminAnalytics/etc/config.xml b/app/code/Magento/AdminAnalytics/etc/config.xml new file mode 100644 index 0000000000000..ba683f13c11e3 --- /dev/null +++ b/app/code/Magento/AdminAnalytics/etc/config.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <admin> + <usage> + <enabled> + 1 + </enabled> + </usage> + </admin> + </default> +</config> \ No newline at end of file From 16aa234c85476ffa7f68b91ff72c412b6fdb1cc5 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Fri, 9 Aug 2019 15:20:24 -0500 Subject: [PATCH 0348/2437] MC-18977: Revert change of ENGCOM-3260 --- .../Model/ReportXml/ModuleIterator.php | 2 +- .../Model/ReportXml/ModuleIteratorTest.php | 2 +- .../Backend/Block/Dashboard/Orders/Grid.php | 6 +-- .../Magento/Backend/Block/Dashboard/Sales.php | 6 +-- .../Block/Dashboard/Tab/Products/Ordered.php | 6 +-- .../Backend/Block/Dashboard/Totals.php | 6 +-- app/code/Magento/Backend/Model/Menu/Item.php | 6 +-- .../Block/Checkout/Cart/Item/Renderer.php | 4 +- .../Model/ResourceModel/Indexer/Price.php | 40 +++++++++---------- .../ResourceModel/Selection/Collection.php | 4 +- .../Block/Adminhtml/Helper/Form/Wysiwyg.php | 6 +-- .../Product/Edit/Tab/Alerts/Price.php | 6 +-- .../Product/Edit/Tab/Alerts/Stock.php | 6 +-- .../Adminhtml/Product/Edit/Tab/Inventory.php | 6 +-- .../Edit/Tab/Price/Group/AbstractGroup.php | 6 +-- .../Block/Adminhtml/Product/Edit/Tabs.php | 8 ++-- .../Catalog/Block/Adminhtml/Product/Grid.php | 6 +-- .../Block/Product/ProductList/Related.php | 6 +-- .../Block/Product/ProductList/Upsell.php | 6 +-- app/code/Magento/Catalog/Model/Product.php | 6 +-- .../ResourceModel/Product/Collection.php | 6 +-- .../Product/Compare/Item/Collection.php | 5 ++- .../Product/Indexer/Price/DefaultPrice.php | 6 +-- .../Indexer/Price/Query/BaseFinalPrice.php | 6 +-- .../Product/Edit/Tab/InventoryTest.php | 2 +- .../Catalog/Test/Unit/Model/ProductTest.php | 4 +- .../ResourceModel/Product/CollectionTest.php | 2 +- .../Form/Modifier/AdvancedPricingTest.php | 2 +- .../Product/Form/Modifier/AdvancedPricing.php | 2 +- .../ResourceModel/Advanced/Collection.php | 4 +- .../ResourceModel/Fulltext/Collection.php | 4 +- .../Model/ResourceModel/Search/Collection.php | 4 +- .../Checkout/Block/Cart/Item/Renderer.php | 6 +-- app/code/Magento/Checkout/Block/Cart/Link.php | 6 +-- app/code/Magento/Checkout/Block/Link.php | 6 +-- .../Model/Layout/DepersonalizePluginTest.php | 2 +- .../Config/Structure/AbstractElement.php | 12 ++---- .../Structure/Element/AbstractComposite.php | 4 +- .../Model/Config/Structure/Element/Field.php | 4 +- .../Model/Config/Structure/Element/Group.php | 4 +- .../Config/Structure/Element/Section.php | 4 +- .../Element/AbstractCompositeTest.php | 4 +- .../Model/Product/Type/Plugin.php | 8 ++-- .../Magento/Customer/Block/Form/Register.php | 6 +-- .../Helper/Session/CurrentCustomer.php | 4 +- .../Customer/Model/Customer/Source/Group.php | 2 +- .../Test/Unit/Block/Form/RegisterTest.php | 2 +- .../Helper/Session/CurrentCustomerTest.php | 4 +- .../Unit/Model/Customer/Source/GroupTest.php | 6 +-- .../Magento/Deploy/Collector/Collector.php | 12 +++--- .../Block/Checkout/Cart/Item/Renderer.php | 4 +- .../Model/Product/Type/Plugin.php | 8 ++-- .../Grouped/AssociatedProductsCollection.php | 4 +- .../Test/Unit/Model/ProductTest.php | 11 ++--- .../Model/Export/Config/Converter.php | 6 +-- .../Model/Import/Config/Converter.php | 8 ++-- .../Model/Export/Config/ConverterTest.php | 2 +- .../Model/Import/Config/ConverterTest.php | 2 +- ...ductAttributeFormBuildFrontTabObserver.php | 8 ++-- .../ProductAttributeGridBuildObserver.php | 8 ++-- .../Model/Module/Collect.php | 8 ++-- .../Test/Unit/Model/Module/CollectTest.php | 1 + .../PageCache/Model/DepersonalizeChecker.php | 6 +-- .../Unit/Model/DepersonalizeCheckerTest.php | 4 +- .../Model/Layout/DepersonalizePluginTest.php | 14 ++----- .../ResourceModel/Product/Collection.php | 4 +- .../Index/Collection/AbstractCollection.php | 4 +- .../Product/Lowstock/Collection.php | 4 +- .../ResourceModel/Product/CollectionTest.php | 2 +- .../Review/Block/Adminhtml/Product/Grid.php | 4 +- .../Review/Model/ResourceModel/Rating.php | 6 +-- .../Review/Product/Collection.php | 4 +- .../Product/Form/Modifier/ReviewTest.php | 2 +- .../Product/Form/Modifier/Review.php | 2 +- .../Search/Block/Adminhtml/Dashboard/Last.php | 6 +-- .../Search/Block/Adminhtml/Dashboard/Top.php | 6 +-- .../Observer/AddFieldsToAttributeObserver.php | 8 ++-- .../AddSwatchAttributeTypeObserver.php | 8 ++-- .../AddFieldsToAttributeObserverTest.php | 2 +- .../AddSwatchAttributeTypeObserverTest.php | 2 +- .../Tax/Model/App/Action/ContextPlugin.php | 6 +-- .../Tax/Observer/AfterAddressSaveObserver.php | 8 ++-- .../Tax/Observer/CustomerLoggedInObserver.php | 8 ++-- .../Unit/App/Action/ContextPluginTest.php | 4 +- .../Observer/AfterAddressSaveObserverTest.php | 8 ++-- .../Observer/CustomerLoggedInObserverTest.php | 4 +- .../layout/sales_email_item_price.xml | 12 +----- .../Weee/Model/App/Action/ContextPlugin.php | 6 +-- .../Weee/Observer/AfterAddressSave.php | 8 ++-- .../Weee/Observer/CustomerLoggedIn.php | 6 +-- .../Unit/App/Action/ContextPluginTest.php | 4 +- .../Unit/Observer/AfterAddressSaveTest.php | 10 ++--- .../Unit/Observer/CustomerLoggedInTest.php | 4 +- .../Wishlist/Test/Unit/Helper/RssTest.php | 4 +- app/etc/di.xml | 1 - .../Block/Account/Dashboard/AddressTest.php | 2 +- .../Test/Legacy/_files/obsolete_methods.php | 4 +- .../Framework/App/Helper/AbstractHelper.php | 4 +- .../Magento/Framework/App/Helper/Context.php | 8 ++-- .../Magento/Framework/Module/Manager.php | 16 ++++++-- .../Module/ModuleManagerInterface.php | 26 ------------ .../Module/Test/Unit/ManagerTest.php | 3 +- .../Unit/Plugin/DbStatusValidatorTest.php | 2 +- .../File/Collector/Decorator/ModuleOutput.php | 6 +-- 104 files changed, 283 insertions(+), 326 deletions(-) delete mode 100644 lib/internal/Magento/Framework/Module/ModuleManagerInterface.php diff --git a/app/code/Magento/Analytics/Model/ReportXml/ModuleIterator.php b/app/code/Magento/Analytics/Model/ReportXml/ModuleIterator.php index 4d62344197405..fecbf2033c1ba 100644 --- a/app/code/Magento/Analytics/Model/ReportXml/ModuleIterator.php +++ b/app/code/Magento/Analytics/Model/ReportXml/ModuleIterator.php @@ -5,7 +5,7 @@ */ namespace Magento\Analytics\Model\ReportXml; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; /** * Iterator for ReportXml modules diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php index b08d41ac829b7..5288bcd306af9 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/ReportXml/ModuleIteratorTest.php @@ -7,7 +7,7 @@ namespace Magento\Analytics\Test\Unit\Model\ReportXml; use Magento\Analytics\Model\ReportXml\ModuleIterator; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** diff --git a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php index bca7f13b0cee3..0a73430aad0f3 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php +++ b/app/code/Magento/Backend/Block/Dashboard/Orders/Grid.php @@ -19,21 +19,21 @@ class Grid extends \Magento\Backend\Block\Dashboard\Grid protected $_collectionFactory; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, array $data = [] ) { diff --git a/app/code/Magento/Backend/Block/Dashboard/Sales.php b/app/code/Magento/Backend/Block/Dashboard/Sales.php index 3455ff087a799..b388339460102 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Sales.php +++ b/app/code/Magento/Backend/Block/Dashboard/Sales.php @@ -18,20 +18,20 @@ class Sales extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/salebar.phtml'; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Block/Dashboard/Tab/Products/Ordered.php b/app/code/Magento/Backend/Block/Dashboard/Tab/Products/Ordered.php index 7dc897a62a320..a0b1571bd17bb 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Tab/Products/Ordered.php +++ b/app/code/Magento/Backend/Block/Dashboard/Tab/Products/Ordered.php @@ -19,21 +19,21 @@ class Ordered extends \Magento\Backend\Block\Dashboard\Grid protected $_collectionFactory; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Sales\Model\ResourceModel\Report\Bestsellers\CollectionFactory $collectionFactory * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Sales\Model\ResourceModel\Report\Bestsellers\CollectionFactory $collectionFactory, array $data = [] ) { diff --git a/app/code/Magento/Backend/Block/Dashboard/Totals.php b/app/code/Magento/Backend/Block/Dashboard/Totals.php index e57a6249af47d..20bcfebe31a8d 100644 --- a/app/code/Magento/Backend/Block/Dashboard/Totals.php +++ b/app/code/Magento/Backend/Block/Dashboard/Totals.php @@ -22,20 +22,20 @@ class Totals extends \Magento\Backend\Block\Dashboard\Bar protected $_template = 'Magento_Backend::dashboard/totalbar.phtml'; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Reports\Model\ResourceModel\Order\CollectionFactory $collectionFactory, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_moduleManager = $moduleManager; diff --git a/app/code/Magento/Backend/Model/Menu/Item.php b/app/code/Magento/Backend/Model/Menu/Item.php index d535e9c84df24..67c6216cbbc06 100644 --- a/app/code/Magento/Backend/Model/Menu/Item.php +++ b/app/code/Magento/Backend/Model/Menu/Item.php @@ -145,7 +145,7 @@ class Item protected $_moduleList; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $_moduleManager; @@ -163,7 +163,7 @@ class Item * @param \Magento\Backend\Model\MenuFactory $menuFactory * @param \Magento\Backend\Model\UrlInterface $urlModel * @param \Magento\Framework\Module\ModuleListInterface $moduleList - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( @@ -173,7 +173,7 @@ public function __construct( \Magento\Backend\Model\MenuFactory $menuFactory, \Magento\Backend\Model\UrlInterface $urlModel, \Magento\Framework\Module\ModuleListInterface $moduleList, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_validator = $validator; diff --git a/app/code/Magento/Bundle/Block/Checkout/Cart/Item/Renderer.php b/app/code/Magento/Bundle/Block/Checkout/Cart/Item/Renderer.php index c0a2d9d43034d..863f273225693 100644 --- a/app/code/Magento/Bundle/Block/Checkout/Cart/Item/Renderer.php +++ b/app/code/Magento/Bundle/Block/Checkout/Cart/Item/Renderer.php @@ -32,7 +32,7 @@ class Renderer extends \Magento\Checkout\Block\Cart\Item\Renderer * @param \Magento\Framework\Url\Helper\Data $urlHelper * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param PriceCurrencyInterface $priceCurrency - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param InterpretationStrategyInterface $messageInterpretationStrategy * @param Configuration $bundleProductConfiguration * @param array $data @@ -46,7 +46,7 @@ public function __construct( \Magento\Framework\Url\Helper\Data $urlHelper, \Magento\Framework\Message\ManagerInterface $messageManager, PriceCurrencyInterface $priceCurrency, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, InterpretationStrategyInterface $messageInterpretationStrategy, Configuration $bundleProductConfiguration, array $data = [] diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php index b71853cde41ac..077ebd4422aab 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php @@ -85,7 +85,7 @@ class Price implements DimensionalIndexerInterface private $eventManager; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManager; @@ -97,7 +97,7 @@ class Price implements DimensionalIndexerInterface * @param BasePriceModifier $basePriceModifier * @param JoinAttributeProcessor $joinAttributeProcessor * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param bool $fullReindexAction * @param string $connectionName * @@ -111,7 +111,7 @@ public function __construct( BasePriceModifier $basePriceModifier, JoinAttributeProcessor $joinAttributeProcessor, \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, $fullReindexAction = false, $connectionName = 'indexer' ) { @@ -139,16 +139,16 @@ public function executeByDimensions(array $dimensions, \Traversable $entityIds) $temporaryPriceTable = $this->indexTableStructureFactory->create( [ - 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), - 'entityField' => 'entity_id', - 'customerGroupField' => 'customer_group_id', - 'websiteField' => 'website_id', - 'taxClassField' => 'tax_class_id', - 'originalPriceField' => 'price', - 'finalPriceField' => 'final_price', - 'minPriceField' => 'min_price', - 'maxPriceField' => 'max_price', - 'tierPriceField' => 'tier_price', + 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions), + 'entityField' => 'entity_id', + 'customerGroupField' => 'customer_group_id', + 'websiteField' => 'website_id', + 'taxClassField' => 'tax_class_id', + 'originalPriceField' => 'price', + 'finalPriceField' => 'final_price', + 'minPriceField' => 'min_price', + 'maxPriceField' => 'max_price', + 'tierPriceField' => 'tier_price', ] ); @@ -335,9 +335,9 @@ private function prepareBundlePriceByType($priceType, array $dimensions, $entity ); $finalPrice = $connection->getLeastSql( [ - $price, - $connection->getIfNullSql($specialPriceExpr, $price), - $connection->getIfNullSql($tierPrice, $price), + $price, + $connection->getIfNullSql($specialPriceExpr, $price), + $connection->getIfNullSql($tierPrice, $price), ] ); } else { @@ -477,8 +477,8 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) $priceExpr = $connection->getLeastSql( [ - $priceExpr, - $connection->getIfNullSql($tierExpr, $priceExpr), + $priceExpr, + $connection->getIfNullSql($tierExpr, $priceExpr), ] ); } else { @@ -495,8 +495,8 @@ private function calculateBundleSelectionPrice($dimensions, $priceType) ); $priceExpr = $connection->getLeastSql( [ - $specialExpr, - $connection->getIfNullSql($tierExpr, $price), + $specialExpr, + $connection->getIfNullSql($tierExpr, $price), ] ); } diff --git a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php index 21ba1f75ba90b..7b3f6dd8bbefa 100644 --- a/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php +++ b/app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php @@ -61,7 +61,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -88,7 +88,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Helper/Form/Wysiwyg.php b/app/code/Magento/Catalog/Block/Adminhtml/Helper/Form/Wysiwyg.php index a829c058d89bf..c58ed58370e3a 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Helper/Form/Wysiwyg.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Helper/Form/Wysiwyg.php @@ -26,7 +26,7 @@ class Wysiwyg extends \Magento\Framework\Data\Form\Element\Textarea /** * Catalog data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager = null; @@ -46,7 +46,7 @@ class Wysiwyg extends \Magento\Framework\Data\Form\Element\Textarea * @param \Magento\Framework\Escaper $escaper * @param \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig * @param \Magento\Framework\View\LayoutInterface $layout - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Backend\Helper\Data $backendData * @param array $data */ @@ -56,7 +56,7 @@ public function __construct( \Magento\Framework\Escaper $escaper, \Magento\Cms\Model\Wysiwyg\Config $wysiwygConfig, \Magento\Framework\View\LayoutInterface $layout, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Backend\Helper\Data $backendData, array $data = [] ) { diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Price.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Price.php index e754ab9700517..386fe1333a7e9 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Price.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Price.php @@ -19,7 +19,7 @@ class Price extends Extended /** * Catalog data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -32,14 +32,14 @@ class Price extends Extended * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper * @param \Magento\ProductAlert\Model\PriceFactory $priceFactory - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, \Magento\ProductAlert\Model\PriceFactory $priceFactory, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_priceFactory = $priceFactory; diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Stock.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Stock.php index 2c6647fd57be6..ede478cabe783 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Stock.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Alerts/Stock.php @@ -19,7 +19,7 @@ class Stock extends Extended /** * Catalog data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -32,14 +32,14 @@ class Stock extends Extended * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper * @param \Magento\ProductAlert\Model\StockFactory $stockFactory - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, \Magento\ProductAlert\Model\StockFactory $stockFactory, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_stockFactory = $stockFactory; diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Inventory.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Inventory.php index 9278b84362e77..782147e1e8ef6 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Inventory.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Inventory.php @@ -18,7 +18,7 @@ class Inventory extends \Magento\Backend\Block\Widget protected $_template = 'Magento_Catalog::catalog/product/tab/inventory.phtml'; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -53,7 +53,7 @@ class Inventory extends \Magento\Backend\Block\Widget * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\CatalogInventory\Model\Source\Backorders $backorders * @param \Magento\CatalogInventory\Model\Source\Stock $stock - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Framework\Registry $coreRegistry * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration @@ -63,7 +63,7 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\CatalogInventory\Model\Source\Backorders $backorders, \Magento\CatalogInventory\Model\Source\Stock $stock, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Framework\Registry $coreRegistry, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php index 42990116e933f..5ffd3d1dda38d 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Price/Group/AbstractGroup.php @@ -41,7 +41,7 @@ abstract class AbstractGroup extends Widget implements RendererInterface /** * Catalog data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -81,7 +81,7 @@ abstract class AbstractGroup extends Widget implements RendererInterface * @param \Magento\Backend\Block\Template\Context $context * @param GroupRepositoryInterface $groupRepository * @param \Magento\Directory\Helper\Data $directoryHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Framework\Registry $registry * @param GroupManagementInterface $groupManagement * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder @@ -92,7 +92,7 @@ public function __construct( \Magento\Backend\Block\Template\Context $context, GroupRepositoryInterface $groupRepository, \Magento\Directory\Helper\Data $directoryHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Framework\Registry $registry, GroupManagementInterface $groupManagement, \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tabs.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tabs.php index 51c326763b09c..37ad3f4bea20e 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tabs.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tabs.php @@ -14,7 +14,7 @@ use Magento\Catalog\Helper\Data; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; use Magento\Framework\Json\EncoderInterface; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Registry; use Magento\Framework\Translate\InlineInterface; @@ -65,7 +65,7 @@ class Tabs extends WidgetTabs protected $_collectionFactory; /** - * @var ModuleManagerInterface + * @var Manager */ protected $_moduleManager; @@ -78,7 +78,7 @@ class Tabs extends WidgetTabs * @param Context $context * @param EncoderInterface $jsonEncoder * @param Session $authSession - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param CollectionFactory $collectionFactory * @param Catalog $helperCatalog * @param Data $catalogData @@ -91,7 +91,7 @@ public function __construct( Context $context, EncoderInterface $jsonEncoder, Session $authSession, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, CollectionFactory $collectionFactory, Catalog $helperCatalog, Data $catalogData, diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Grid.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Grid.php index 7e43f2fc064ad..01408ade56432 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Grid.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Grid.php @@ -16,7 +16,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -59,7 +59,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended * @param \Magento\Catalog\Model\Product\Type $type * @param \Magento\Catalog\Model\Product\Attribute\Source\Status $status * @param \Magento\Catalog\Model\Product\Visibility $visibility - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data * * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -73,7 +73,7 @@ public function __construct( \Magento\Catalog\Model\Product\Type $type, \Magento\Catalog\Model\Product\Attribute\Source\Status $status, \Magento\Catalog\Model\Product\Visibility $visibility, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_websiteFactory = $websiteFactory; diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php index 088619511545f..6de70bb971367 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php @@ -46,7 +46,7 @@ class Related extends \Magento\Catalog\Block\Product\AbstractProduct implements protected $_checkoutCart; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -55,7 +55,7 @@ class Related extends \Magento\Catalog\Block\Product\AbstractProduct implements * @param \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart * @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( @@ -63,7 +63,7 @@ public function __construct( \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart, \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility, \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_checkoutCart = $checkoutCart; diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php index d888f44a6fbfb..24822447ae915 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php @@ -60,7 +60,7 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements protected $_checkoutCart; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -69,7 +69,7 @@ class Upsell extends \Magento\Catalog\Block\Product\AbstractProduct implements * @param \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart * @param \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param array $data */ public function __construct( @@ -77,7 +77,7 @@ public function __construct( \Magento\Checkout\Model\ResourceModel\Cart $checkoutCart, \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility, \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, array $data = [] ) { $this->_checkoutCart = $checkoutCart; diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index fc9fffb2a7e9a..fc3cc11cade54 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -177,7 +177,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements protected $_catalogProduct = null; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -381,7 +381,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements * @param Product\Attribute\Source\Status $catalogProductStatus * @param Product\Media\Config $catalogProductMediaConfig * @param Product\Type $catalogProductType - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Helper\Product $catalogProduct * @param ResourceModel\Product $resource * @param ResourceModel\Product\Collection $resourceCollection @@ -422,7 +422,7 @@ public function __construct( \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus, \Magento\Catalog\Model\Product\Media\Config $catalogProductMediaConfig, Product\Type $catalogProductType, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Helper\Product $catalogProduct, \Magento\Catalog\Model\ResourceModel\Product $resource, \Magento\Catalog\Model\ResourceModel\Product\Collection $resourceCollection, diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index dbd6a7a2e1094..384b6ddcefc31 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -191,7 +191,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac /** * Catalog data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager = null; @@ -315,7 +315,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -344,7 +344,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php index dc3411743a066..92741cf9ba88e 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Compare/Item/Collection.php @@ -64,7 +64,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -76,6 +76,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Product\Compare\Item $catalogProductCompareItem * @param \Magento\Catalog\Helper\Product\Compare $catalogProductCompare * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -89,7 +90,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php index 9643f4c3a7181..b64cca4ff1b26 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/DefaultPrice.php @@ -40,7 +40,7 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface /** * Core data * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -73,7 +73,7 @@ class DefaultPrice extends AbstractIndexer implements PriceInterface * @param \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Framework\Event\ManagerInterface $eventManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param string|null $connectionName * @param IndexTableStructureFactory $indexTableStructureFactory * @param PriceModifierInterface[] $priceModifiers @@ -83,7 +83,7 @@ public function __construct( \Magento\Framework\Indexer\Table\StrategyInterface $tableStrategy, \Magento\Eav\Model\Config $eavConfig, \Magento\Framework\Event\ManagerInterface $eventManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, $connectionName = null, IndexTableStructureFactory $indexTableStructureFactory = null, array $priceModifiers = [] diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index a3f463d53e7a8..77407ed699fbd 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -37,7 +37,7 @@ class BaseFinalPrice private $joinAttributeProcessor; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManager; @@ -69,7 +69,7 @@ class BaseFinalPrice /** * @param \Magento\Framework\App\ResourceConnection $resource * @param JoinAttributeProcessor $joinAttributeProcessor - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param string $connectionName @@ -77,7 +77,7 @@ class BaseFinalPrice public function __construct( \Magento\Framework\App\ResourceConnection $resource, JoinAttributeProcessor $joinAttributeProcessor, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Framework\EntityManager\MetadataPool $metadataPool, $connectionName = 'indexer' diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Tab/InventoryTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Tab/InventoryTest.php index 2008d0b9414c5..19c578e976cdd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Tab/InventoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Edit/Tab/InventoryTest.php @@ -85,7 +85,7 @@ protected function setUp() $this->backordersMock = $this->createMock(\Magento\CatalogInventory\Model\Source\Backorders::class); $this->stockMock = $this->createMock(\Magento\CatalogInventory\Model\Source\Stock::class); $this->coreRegistryMock = $this->createMock(\Magento\Framework\Registry::class); - $this->moduleManager = $this->createMock(\Magento\Framework\Module\ModuleManagerInterface::class); + $this->moduleManager = $this->createMock(\Magento\Framework\Module\Manager::class); $this->storeManagerMock = $this->getMockForAbstractClass( \Magento\Store\Model\StoreManagerInterface::class, [], diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 8bf8473080c54..2ef848ca5aada 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -40,7 +40,7 @@ class ProductTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManager; @@ -215,7 +215,7 @@ protected function setUp() $this->categoryIndexerMock = $this->getMockForAbstractClass(\Magento\Framework\Indexer\IndexerInterface::class); $this->moduleManager = $this->createPartialMock( - \Magento\Framework\Module\ModuleManagerInterface::class, + \Magento\Framework\Module\Manager::class, ['isEnabled'] ); $this->extensionAttributes = $this->getMockBuilder(\Magento\Framework\Api\ExtensionAttributesInterface::class) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index 6370a4a7a27e2..0316b2e374d2f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -98,7 +98,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['getStore', 'getId', 'getWebsiteId']) ->getMockForAbstractClass(); - $moduleManager = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $moduleManager = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); $catalogProductFlatState = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Flat\State::class) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AdvancedPricingTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AdvancedPricingTest.php index e9f9349100f15..e455ad47ee626 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AdvancedPricingTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AdvancedPricingTest.php @@ -11,7 +11,7 @@ use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Directory\Helper\Data as DirectoryHelper; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 9ad75b5fda923..00132c6ad89e8 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -14,7 +14,7 @@ use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Ui\Component\Container; use Magento\Ui\Component\Form\Element\DataType\Number; use Magento\Ui\Component\Form\Element\DataType\Price; diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php index 1946dd35b8d37..a37cd80056b00 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php @@ -125,7 +125,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param Product\OptionFactory $productOptionFactory @@ -160,7 +160,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 4f84f3868c6a3..a596354a0c8cd 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -146,7 +146,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -185,7 +185,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php index 6cdcc7c55a26f..e625ccbe51fe3 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Search/Collection.php @@ -50,7 +50,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -74,7 +74,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php index 4941bf8451bf8..c99c9041941b1 100644 --- a/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php +++ b/app/code/Magento/Checkout/Block/Cart/Item/Renderer.php @@ -85,7 +85,7 @@ class Renderer extends \Magento\Framework\View\Element\Template implements protected $priceCurrency; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ public $moduleManager; @@ -105,7 +105,7 @@ class Renderer extends \Magento\Framework\View\Element\Template implements * @param \Magento\Framework\Url\Helper\Data $urlHelper * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param PriceCurrencyInterface $priceCurrency - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param InterpretationStrategyInterface $messageInterpretationStrategy * @param array $data * @param ItemResolverInterface|null $itemResolver @@ -120,7 +120,7 @@ public function __construct( \Magento\Framework\Url\Helper\Data $urlHelper, \Magento\Framework\Message\ManagerInterface $messageManager, PriceCurrencyInterface $priceCurrency, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, InterpretationStrategyInterface $messageInterpretationStrategy, array $data = [], ItemResolverInterface $itemResolver = null diff --git a/app/code/Magento/Checkout/Block/Cart/Link.php b/app/code/Magento/Checkout/Block/Cart/Link.php index 6ea5137521106..9e6db1754d9e4 100644 --- a/app/code/Magento/Checkout/Block/Cart/Link.php +++ b/app/code/Magento/Checkout/Block/Cart/Link.php @@ -13,7 +13,7 @@ class Link extends \Magento\Framework\View\Element\Html\Link { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -24,14 +24,14 @@ class Link extends \Magento\Framework\View\Element\Html\Link /** * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Checkout\Helper\Cart $cartHelper * @param array $data * @codeCoverageIgnore */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Checkout\Helper\Cart $cartHelper, array $data = [] ) { diff --git a/app/code/Magento/Checkout/Block/Link.php b/app/code/Magento/Checkout/Block/Link.php index 3d0740181f4a5..4ab2981e9185e 100644 --- a/app/code/Magento/Checkout/Block/Link.php +++ b/app/code/Magento/Checkout/Block/Link.php @@ -13,7 +13,7 @@ class Link extends \Magento\Framework\View\Element\Html\Link { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -24,14 +24,14 @@ class Link extends \Magento\Framework\View\Element\Html\Link /** * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Checkout\Helper\Data $checkoutHelper * @param array $data * @codeCoverageIgnore */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Checkout\Helper\Data $checkoutHelper, array $data = [] ) { diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 3cc80e14fd026..350f9954208fa 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -43,7 +43,7 @@ protected function setUp() ); $this->checkoutSessionMock = $this->createPartialMock(\Magento\Checkout\Model\Session::class, ['clearStorage']); $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\ModuleManagerInterface::class); + $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); $this->cacheConfigMock = $this->createMock(\Magento\PageCache\Model\Config::class); $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); diff --git a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php index db815ec87ed76..23a3dea1a7029 100644 --- a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php +++ b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php @@ -40,7 +40,7 @@ abstract class AbstractElement implements StructureElementInterface protected $_storeManager; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -50,15 +50,11 @@ abstract class AbstractElement implements StructureElementInterface private $elementVisibility; /** - * Construct. - * * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager */ - public function __construct( - StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager - ) { + public function __construct(StoreManagerInterface $storeManager, \Magento\Framework\Module\Manager $moduleManager) + { $this->_storeManager = $storeManager; $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/AbstractComposite.php b/app/code/Magento/Config/Model/Config/Structure/Element/AbstractComposite.php index efb918226aa31..a14114a116e78 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/AbstractComposite.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/AbstractComposite.php @@ -23,12 +23,12 @@ abstract class AbstractComposite extends \Magento\Config\Model\Config\Structure\ /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param Iterator $childrenIterator */ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, Iterator $childrenIterator ) { parent::__construct($storeManager, $moduleManager); diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Field.php index 6a8cc6e767466..834b2a9e17e37 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Field.php @@ -56,7 +56,7 @@ class Field extends \Magento\Config\Model\Config\Structure\AbstractElement /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Config\Model\Config\BackendFactory $backendFactory * @param \Magento\Config\Model\Config\SourceFactory $sourceFactory * @param \Magento\Config\Model\Config\CommentFactory $commentFactory @@ -65,7 +65,7 @@ class Field extends \Magento\Config\Model\Config\Structure\AbstractElement */ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Config\Model\Config\BackendFactory $backendFactory, \Magento\Config\Model\Config\SourceFactory $sourceFactory, \Magento\Config\Model\Config\CommentFactory $commentFactory, diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Group.php b/app/code/Magento/Config/Model/Config/Structure/Element/Group.php index db479e8b795a0..1ca0afa942f8d 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Group.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Group.php @@ -29,14 +29,14 @@ class Group extends AbstractComposite /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param Iterator\Field $childrenIterator * @param \Magento\Config\Model\Config\BackendClone\Factory $cloneModelFactory * @param Dependency\Mapper $dependencyMapper */ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Config\Model\Config\Structure\Element\Iterator\Field $childrenIterator, \Magento\Config\Model\Config\BackendClone\Factory $cloneModelFactory, \Magento\Config\Model\Config\Structure\Element\Dependency\Mapper $dependencyMapper diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Section.php b/app/code/Magento/Config/Model/Config/Structure/Element/Section.php index 134411fbd87ca..80c029dfea2d0 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Section.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Section.php @@ -22,13 +22,13 @@ class Section extends AbstractComposite /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param Iterator $childrenIterator * @param \Magento\Framework\AuthorizationInterface $authorization */ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, Iterator $childrenIterator, \Magento\Framework\AuthorizationInterface $authorization ) { diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/AbstractCompositeTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/AbstractCompositeTest.php index e448b628ef020..8c54a02a491c0 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/AbstractCompositeTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/AbstractCompositeTest.php @@ -29,7 +29,7 @@ class AbstractCompositeTest extends \PHPUnit\Framework\TestCase protected $_iteratorMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface | \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager | \PHPUnit_Framework_MockObject_MockObject */ protected $moduleManagerMock; @@ -56,7 +56,7 @@ protected function setUp() ->getMockForAbstractClass(); $this->_iteratorMock = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Iterator::class); $this->_storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManager::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\ModuleManagerInterface::class); + $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); $this->_model = $this->getMockForAbstractClass( \Magento\Config\Model\Config\Structure\Element\AbstractComposite::class, [$this->_storeManagerMock, $this->moduleManagerMock, $this->_iteratorMock] diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Plugin.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Plugin.php index e8b7299a03db9..cb4ac975cd582 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Plugin.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Plugin.php @@ -6,7 +6,7 @@ */ namespace Magento\ConfigurableProduct\Model\Product\Type; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; /** * Type plugin. @@ -14,14 +14,14 @@ class Plugin { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/Customer/Block/Form/Register.php b/app/code/Magento/Customer/Block/Form/Register.php index a190ccde50b5a..59966768a2eda 100644 --- a/app/code/Magento/Customer/Block/Form/Register.php +++ b/app/code/Magento/Customer/Block/Form/Register.php @@ -23,7 +23,7 @@ class Register extends \Magento\Directory\Block\Data protected $_customerSession; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -41,7 +41,7 @@ class Register extends \Magento\Directory\Block\Data * @param \Magento\Framework\App\Cache\Type\Config $configCacheType * @param \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Customer\Model\Url $customerUrl * @param array $data @@ -55,7 +55,7 @@ public function __construct( \Magento\Framework\App\Cache\Type\Config $configCacheType, \Magento\Directory\Model\ResourceModel\Region\CollectionFactory $regionCollectionFactory, \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $countryCollectionFactory, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Customer\Model\Session $customerSession, \Magento\Customer\Model\Url $customerUrl, array $data = [] diff --git a/app/code/Magento/Customer/Helper/Session/CurrentCustomer.php b/app/code/Magento/Customer/Helper/Session/CurrentCustomer.php index 5cd09aca9f873..d48ff5918c3f3 100644 --- a/app/code/Magento/Customer/Helper/Session/CurrentCustomer.php +++ b/app/code/Magento/Customer/Helper/Session/CurrentCustomer.php @@ -10,7 +10,7 @@ use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ViewInterface; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Framework\View\LayoutInterface; /** @@ -45,7 +45,7 @@ class CurrentCustomer protected $request; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; diff --git a/app/code/Magento/Customer/Model/Customer/Source/Group.php b/app/code/Magento/Customer/Model/Customer/Source/Group.php index efcc7d0fe93a4..1064152b20fd5 100644 --- a/app/code/Magento/Customer/Model/Customer/Source/Group.php +++ b/app/code/Magento/Customer/Model/Customer/Source/Group.php @@ -6,7 +6,7 @@ namespace Magento\Customer\Model\Customer\Source; use Magento\Customer\Api\Data\GroupSearchResultsInterface; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Customer\Api\Data\GroupInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; diff --git a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php index b93b9f40d75b2..d234ebfb334d6 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Form/RegisterTest.php @@ -40,7 +40,7 @@ class RegisterTest extends \PHPUnit\Framework\TestCase /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Model\Session */ private $_customerSession; - /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Module\ModuleManagerInterface */ + /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Module\Manager */ private $_moduleManager; /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Customer\Model\Url */ diff --git a/app/code/Magento/Customer/Test/Unit/Helper/Session/CurrentCustomerTest.php b/app/code/Magento/Customer/Test/Unit/Helper/Session/CurrentCustomerTest.php index 03158d05db8e4..15ced1ce66d06 100644 --- a/app/code/Magento/Customer/Test/Unit/Helper/Session/CurrentCustomerTest.php +++ b/app/code/Magento/Customer/Test/Unit/Helper/Session/CurrentCustomerTest.php @@ -47,7 +47,7 @@ class CurrentCustomerTest extends \PHPUnit\Framework\TestCase protected $requestMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManagerMock; @@ -80,7 +80,7 @@ protected function setUp() $this->customerDataMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\ModuleManagerInterface::class); + $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); $this->viewMock = $this->createMock(\Magento\Framework\App\View::class); $this->currentCustomer = new \Magento\Customer\Helper\Session\CurrentCustomer( diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/Source/GroupTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/Source/GroupTest.php index 9128d7c675262..bc4c19bc23ac1 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/Source/GroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/Source/GroupTest.php @@ -6,7 +6,7 @@ namespace Magento\Customer\Test\Unit\Model\Customer\Source; use Magento\Customer\Model\Customer\Source\Group; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SearchCriteria; @@ -23,7 +23,7 @@ class GroupTest extends \PHPUnit\Framework\TestCase private $model; /** - * @var ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|\PHPUnit_Framework_MockObject_MockObject */ private $moduleManagerMock; @@ -49,7 +49,7 @@ class GroupTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->moduleManagerMock = $this->getMockBuilder(ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(Manager::class) ->disableOriginalConstructor() ->getMock(); $this->groupRepositoryMock = $this->getMockBuilder(GroupRepositoryInterface::class) diff --git a/app/code/Magento/Deploy/Collector/Collector.php b/app/code/Magento/Deploy/Collector/Collector.php index 7742f2971a2fe..b09001a7ac04c 100644 --- a/app/code/Magento/Deploy/Collector/Collector.php +++ b/app/code/Magento/Deploy/Collector/Collector.php @@ -9,7 +9,7 @@ use Magento\Deploy\Package\Package; use Magento\Deploy\Package\PackageFactory; use Magento\Deploy\Package\PackageFile; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\View\Asset\PreProcessor\FileNameResolver; /** @@ -45,8 +45,8 @@ class Collector implements CollectorInterface * @var PackageFactory */ private $packageFactory; - - /** @var \Magento\Framework\Module\ModuleManagerInterface */ + + /** @var \Magento\Framework\Module\Manager */ private $moduleManager; /** @@ -66,19 +66,19 @@ class Collector implements CollectorInterface * @param SourcePool $sourcePool * @param FileNameResolver $fileNameResolver * @param PackageFactory $packageFactory - * @param ModuleManagerInterface|null $moduleManager + * @param Manager|null $moduleManager */ public function __construct( SourcePool $sourcePool, FileNameResolver $fileNameResolver, PackageFactory $packageFactory, - ModuleManagerInterface $moduleManager = null + Manager $moduleManager = null ) { $this->sourcePool = $sourcePool; $this->fileNameResolver = $fileNameResolver; $this->packageFactory = $packageFactory; $this->moduleManager = $moduleManager ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Module\ModuleManagerInterface::class); + ->get(\Magento\Framework\Module\Manager::class); } /** diff --git a/app/code/Magento/Downloadable/Block/Checkout/Cart/Item/Renderer.php b/app/code/Magento/Downloadable/Block/Checkout/Cart/Item/Renderer.php index 51efc74738043..8b8a9b6bf2895 100644 --- a/app/code/Magento/Downloadable/Block/Checkout/Cart/Item/Renderer.php +++ b/app/code/Magento/Downloadable/Block/Checkout/Cart/Item/Renderer.php @@ -37,7 +37,7 @@ class Renderer extends \Magento\Checkout\Block\Cart\Item\Renderer * @param \Magento\Framework\Url\Helper\Data $urlHelper * @param \Magento\Framework\Message\ManagerInterface $messageManager * @param PriceCurrencyInterface $priceCurrency - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param InterpretationStrategyInterface $messageInterpretationStrategy * @param \Magento\Downloadable\Helper\Catalog\Product\Configuration $downloadableProductConfiguration * @param array $data @@ -51,7 +51,7 @@ public function __construct( \Magento\Framework\Url\Helper\Data $urlHelper, \Magento\Framework\Message\ManagerInterface $messageManager, PriceCurrencyInterface $priceCurrency, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, InterpretationStrategyInterface $messageInterpretationStrategy, \Magento\Downloadable\Helper\Catalog\Product\Configuration $downloadableProductConfiguration, array $data = [] diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Plugin.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Plugin.php index 4777b2bae07a5..350edb5b8495e 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Plugin.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Plugin.php @@ -6,7 +6,7 @@ */ namespace Magento\GroupedProduct\Model\Product\Type; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; /** * Plugin. @@ -14,14 +14,14 @@ class Plugin { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Type/Grouped/AssociatedProductsCollection.php b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Type/Grouped/AssociatedProductsCollection.php index 519da20510815..c492939232b15 100644 --- a/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Type/Grouped/AssociatedProductsCollection.php +++ b/app/code/Magento/GroupedProduct/Model/ResourceModel/Product/Type/Grouped/AssociatedProductsCollection.php @@ -41,7 +41,7 @@ class AssociatedProductsCollection extends \Magento\Catalog\Model\ResourceModel\ * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -67,7 +67,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php index cec7931c1c61f..78fa2445ff583 100644 --- a/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php @@ -30,7 +30,7 @@ class ProductTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManager; @@ -159,14 +159,9 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->categoryIndexerMock = $this->getMockForAbstractClass( - \Magento\Framework\Indexer\IndexerInterface::class - ); + $this->categoryIndexerMock = $this->getMockForAbstractClass(\Magento\Framework\Indexer\IndexerInterface::class); - $this->moduleManager = $this->createPartialMock( - \Magento\Framework\Module\ModuleManagerInterface::class, - ['isEnabled'] - ); + $this->moduleManager = $this->createPartialMock(\Magento\Framework\Module\Manager::class, ['isEnabled']); $this->stockItemFactoryMock = $this->createPartialMock( \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory::class, ['create'] diff --git a/app/code/Magento/ImportExport/Model/Export/Config/Converter.php b/app/code/Magento/ImportExport/Model/Export/Config/Converter.php index 20ab81ec1cd5b..298f63d18f88d 100644 --- a/app/code/Magento/ImportExport/Model/Export/Config/Converter.php +++ b/app/code/Magento/ImportExport/Model/Export/Config/Converter.php @@ -5,7 +5,7 @@ */ namespace Magento\ImportExport\Model\Export\Config; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\App\Utility\Classes; /** @@ -14,14 +14,14 @@ class Converter implements \Magento\Framework\Config\ConverterInterface { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/ImportExport/Model/Import/Config/Converter.php b/app/code/Magento/ImportExport/Model/Import/Config/Converter.php index f2d1596ec3d9d..a1eb8470b4dd6 100644 --- a/app/code/Magento/ImportExport/Model/Import/Config/Converter.php +++ b/app/code/Magento/ImportExport/Model/Import/Config/Converter.php @@ -5,7 +5,7 @@ */ namespace Magento\ImportExport\Model\Import\Config; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\App\Utility\Classes; /** @@ -14,14 +14,14 @@ class Converter implements \Magento\Framework\Config\ConverterInterface { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/ConverterTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/ConverterTest.php index c888c6b447348..e08f382d94003 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/ConverterTest.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Export/Config/ConverterTest.php @@ -21,7 +21,7 @@ class ConverterTest extends \PHPUnit\Framework\TestCase protected $filePath; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManager; diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/ConverterTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/ConverterTest.php index b29a04322ce4f..69118d2e2a319 100644 --- a/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/ConverterTest.php +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Import/Config/ConverterTest.php @@ -21,7 +21,7 @@ class ConverterTest extends \PHPUnit\Framework\TestCase protected $filePath; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManager; diff --git a/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php b/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php index 0b9cb377d1d08..ce618f97883b0 100644 --- a/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php +++ b/app/code/Magento/LayeredNavigation/Observer/Edit/Tab/Front/ProductAttributeFormBuildFrontTabObserver.php @@ -8,7 +8,7 @@ namespace Magento\LayeredNavigation\Observer\Edit\Tab\Front; use Magento\Config\Model\Config\Source; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Event\ObserverInterface; /** @@ -22,15 +22,15 @@ class ProductAttributeFormBuildFrontTabObserver implements ObserverInterface protected $optionList; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Source\Yesno $optionList */ - public function __construct(ModuleManagerInterface $moduleManager, Source\Yesno $optionList) + public function __construct(Manager $moduleManager, Source\Yesno $optionList) { $this->optionList = $optionList; $this->moduleManager = $moduleManager; diff --git a/app/code/Magento/LayeredNavigation/Observer/Grid/ProductAttributeGridBuildObserver.php b/app/code/Magento/LayeredNavigation/Observer/Grid/ProductAttributeGridBuildObserver.php index b98230c1ebe3c..57a20cf17371d 100644 --- a/app/code/Magento/LayeredNavigation/Observer/Grid/ProductAttributeGridBuildObserver.php +++ b/app/code/Magento/LayeredNavigation/Observer/Grid/ProductAttributeGridBuildObserver.php @@ -7,7 +7,7 @@ */ namespace Magento\LayeredNavigation\Observer\Grid; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Event\ObserverInterface; /** @@ -16,16 +16,16 @@ class ProductAttributeGridBuildObserver implements ObserverInterface { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** * Construct. * - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/NewRelicReporting/Model/Module/Collect.php b/app/code/Magento/NewRelicReporting/Model/Module/Collect.php index 0d8a94fbed940..fe5389e258aa5 100644 --- a/app/code/Magento/NewRelicReporting/Model/Module/Collect.php +++ b/app/code/Magento/NewRelicReporting/Model/Module/Collect.php @@ -6,7 +6,7 @@ namespace Magento\NewRelicReporting\Model\Module; use Magento\Framework\Module\FullModuleList; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Module\ModuleListInterface; use Magento\NewRelicReporting\Model\Config; use Magento\NewRelicReporting\Model\Module; @@ -22,7 +22,7 @@ class Collect protected $moduleList; /** - * @var ModuleManagerInterface + * @var Manager */ protected $moduleManager; @@ -46,14 +46,14 @@ class Collect * * @param ModuleListInterface $moduleList * @param FullModuleList $fullModuleList - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param \Magento\NewRelicReporting\Model\ModuleFactory $moduleFactory * @param \Magento\NewRelicReporting\Model\ResourceModel\Module\CollectionFactory $moduleCollectionFactory */ public function __construct( ModuleListInterface $moduleList, FullModuleList $fullModuleList, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, \Magento\NewRelicReporting\Model\ModuleFactory $moduleFactory, \Magento\NewRelicReporting\Model\ResourceModel\Module\CollectionFactory $moduleCollectionFactory ) { diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php index 3c30d95b77de0..4286406d6e9ab 100644 --- a/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Model/Module/CollectTest.php @@ -8,6 +8,7 @@ use Magento\NewRelicReporting\Model\Module\Collect; use Magento\Framework\Module\FullModuleList; use Magento\Framework\Module\ModuleListInterface; +use Magento\Framework\Module\Manager; use Magento\NewRelicReporting\Model\Module; /** diff --git a/app/code/Magento/PageCache/Model/DepersonalizeChecker.php b/app/code/Magento/PageCache/Model/DepersonalizeChecker.php index 4012499d5da5a..3023efb7a71a6 100644 --- a/app/code/Magento/PageCache/Model/DepersonalizeChecker.php +++ b/app/code/Magento/PageCache/Model/DepersonalizeChecker.php @@ -20,7 +20,7 @@ class DepersonalizeChecker /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManager; @@ -33,12 +33,12 @@ class DepersonalizeChecker /** * @param \Magento\Framework\App\RequestInterface $request - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param Config $cacheConfig */ public function __construct( \Magento\Framework\App\RequestInterface $request, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, Config $cacheConfig ) { $this->request = $request; diff --git a/app/code/Magento/PageCache/Test/Unit/Model/DepersonalizeCheckerTest.php b/app/code/Magento/PageCache/Test/Unit/Model/DepersonalizeCheckerTest.php index 6857c637bab84..8cc933853a492 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/DepersonalizeCheckerTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/DepersonalizeCheckerTest.php @@ -18,7 +18,7 @@ class DepersonalizeCheckerTest extends \PHPUnit\Framework\TestCase private $requestMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ private $moduleManagerMock; @@ -30,7 +30,7 @@ class DepersonalizeCheckerTest extends \PHPUnit\Framework\TestCase public function setup() { $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\ModuleManagerInterface::class); + $this->moduleManagerMock = $this->createMock(\Magento\Framework\Module\Manager::class); $this->cacheConfigMock = $this->createMock(\Magento\PageCache\Model\Config::class); } diff --git a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php index 5ba0182ecc57a..9731811ea8a97 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/Layout/DepersonalizePluginTest.php @@ -46,17 +46,9 @@ protected function setUp() $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->moduleManagerMock = $this->createPartialMock( - \Magento\Framework\Module\ModuleManagerInterface::class, - ['isEnabled'] - ); - $this->cacheConfigMock = $this->createPartialMock( - \Magento\PageCache\Model\Config::class, - ['isEnabled'] - ); - $this->depersonalizeCheckerMock = $this->createMock( - \Magento\PageCache\Model\DepersonalizeChecker::class - ); + $this->moduleManagerMock = $this->createPartialMock(\Magento\Framework\Module\Manager::class, ['isEnabled']); + $this->cacheConfigMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['isEnabled']); + $this->depersonalizeCheckerMock = $this->createMock(\Magento\PageCache\Model\DepersonalizeChecker::class); $this->plugin = $this->objectManager->getObject( \Magento\Persistent\Model\Layout\DepersonalizePlugin::class, diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php index 8c985238efe47..ab76cad8da5c4 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Collection.php @@ -77,7 +77,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -111,7 +111,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php index ec514f45ff65a..5b4cf39d65def 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Index/Collection/AbstractCollection.php @@ -44,7 +44,7 @@ abstract class AbstractCollection extends \Magento\Catalog\Model\ResourceModel\P * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -69,7 +69,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php index c02de01117a74..39d673911111f 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Lowstock/Collection.php @@ -63,7 +63,7 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -94,7 +94,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index 3f1857b352dbc..23067457081a6 100644 --- a/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -29,7 +29,7 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\Event\ManagerInterface; -use Magento\Framework\Module\ModuleManagerInterface as Manager; +use Magento\Framework\Module\Manager as Manager; use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; diff --git a/app/code/Magento/Review/Block/Adminhtml/Product/Grid.php b/app/code/Magento/Review/Block/Adminhtml/Product/Grid.php index 509e826e6f7d3..d3bbdf9a7eb40 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Product/Grid.php +++ b/app/code/Magento/Review/Block/Adminhtml/Product/Grid.php @@ -29,7 +29,7 @@ class Grid extends \Magento\Catalog\Block\Adminhtml\Product\Grid * @param \Magento\Catalog\Model\Product\Type $type * @param \Magento\Catalog\Model\Product\Attribute\Source\Status $status * @param \Magento\Catalog\Model\Product\Visibility $visibility - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Store\Model\ResourceModel\Website\CollectionFactory $websitesFactory * @param array $data * @@ -44,7 +44,7 @@ public function __construct( \Magento\Catalog\Model\Product\Type $type, \Magento\Catalog\Model\Product\Attribute\Source\Status $status, \Magento\Catalog\Model\Product\Visibility $visibility, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Store\Model\ResourceModel\Website\CollectionFactory $websitesFactory, array $data = [] ) { diff --git a/app/code/Magento/Review/Model/ResourceModel/Rating.php b/app/code/Magento/Review/Model/ResourceModel/Rating.php index 42c14e16a50e2..37a93d40b1107 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Rating.php +++ b/app/code/Magento/Review/Model/ResourceModel/Rating.php @@ -29,7 +29,7 @@ class Rating extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb protected $_storeManager; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -46,7 +46,7 @@ class Rating extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param Review\Summary $reviewSummary * @param string $connectionName @@ -55,7 +55,7 @@ class Rating extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Review\Model\ResourceModel\Review\Summary $reviewSummary, $connectionName = null, diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php index 7175baa92a2f8..ab264ef1b6179 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Product/Collection.php @@ -75,7 +75,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory @@ -103,7 +103,7 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, diff --git a/app/code/Magento/Review/Test/Unit/Ui/DataProvider/Product/Form/Modifier/ReviewTest.php b/app/code/Magento/Review/Test/Unit/Ui/DataProvider/Product/Form/Modifier/ReviewTest.php index e1e5503ad475f..1000821dd1897 100644 --- a/app/code/Magento/Review/Test/Unit/Ui/DataProvider/Product/Form/Modifier/ReviewTest.php +++ b/app/code/Magento/Review/Test/Unit/Ui/DataProvider/Product/Form/Modifier/ReviewTest.php @@ -8,7 +8,7 @@ use Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier\AbstractModifierTest; use Magento\Framework\UrlInterface; use Magento\Review\Ui\DataProvider\Product\Form\Modifier\Review; -use \Magento\Framework\Module\Manager as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Ui\DataProvider\Modifier\ModifierInterface; /** diff --git a/app/code/Magento/Review/Ui/DataProvider/Product/Form/Modifier/Review.php b/app/code/Magento/Review/Ui/DataProvider/Product/Form/Modifier/Review.php index 433be1c860988..5f1401a201e3f 100644 --- a/app/code/Magento/Review/Ui/DataProvider/Product/Form/Modifier/Review.php +++ b/app/code/Magento/Review/Ui/DataProvider/Product/Form/Modifier/Review.php @@ -12,7 +12,7 @@ use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Ui\Component\Form; use Magento\Framework\UrlInterface; -use \Magento\Framework\Module\ModuleManagerInterface as ModuleManager; +use Magento\Framework\Module\Manager as ModuleManager; use Magento\Framework\App\ObjectManager; /** diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php index ad8d247b2a6fb..ff49f4a15b06a 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php +++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Last.php @@ -24,7 +24,7 @@ class Last extends \Magento\Backend\Block\Dashboard\Grid protected $_queriesFactory; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -36,14 +36,14 @@ class Last extends \Magento\Backend\Block\Dashboard\Grid /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Search\Model\ResourceModel\Query\CollectionFactory $queriesFactory * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Search\Model\ResourceModel\Query\CollectionFactory $queriesFactory, array $data = [] ) { diff --git a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php index 63893f788673d..3e10e1137d6b6 100644 --- a/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php +++ b/app/code/Magento/Search/Block/Adminhtml/Dashboard/Top.php @@ -24,7 +24,7 @@ class Top extends \Magento\Backend\Block\Dashboard\Grid protected $_queriesFactory; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -36,14 +36,14 @@ class Top extends \Magento\Backend\Block\Dashboard\Grid /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Search\Model\ResourceModel\Query\CollectionFactory $queriesFactory * @param array $data */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Backend\Helper\Data $backendHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Search\Model\ResourceModel\Query\CollectionFactory $queriesFactory, array $data = [] ) { diff --git a/app/code/Magento/Swatches/Observer/AddFieldsToAttributeObserver.php b/app/code/Magento/Swatches/Observer/AddFieldsToAttributeObserver.php index 3ef202af95a1a..303f1eda2e40a 100644 --- a/app/code/Magento/Swatches/Observer/AddFieldsToAttributeObserver.php +++ b/app/code/Magento/Swatches/Observer/AddFieldsToAttributeObserver.php @@ -6,7 +6,7 @@ namespace Magento\Swatches\Observer; use Magento\Config\Model\Config\Source; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; @@ -21,15 +21,15 @@ class AddFieldsToAttributeObserver implements ObserverInterface protected $yesNo; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Source\Yesno $yesNo */ - public function __construct(ModuleManagerInterface $moduleManager, Source\Yesno $yesNo) + public function __construct(Manager $moduleManager, Source\Yesno $yesNo) { $this->moduleManager = $moduleManager; $this->yesNo = $yesNo; diff --git a/app/code/Magento/Swatches/Observer/AddSwatchAttributeTypeObserver.php b/app/code/Magento/Swatches/Observer/AddSwatchAttributeTypeObserver.php index ca75da3321698..fb8c185cc545b 100644 --- a/app/code/Magento/Swatches/Observer/AddSwatchAttributeTypeObserver.php +++ b/app/code/Magento/Swatches/Observer/AddSwatchAttributeTypeObserver.php @@ -6,7 +6,7 @@ namespace Magento\Swatches\Observer; use Magento\Config\Model\Config\Source; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; @@ -16,14 +16,14 @@ class AddSwatchAttributeTypeObserver implements ObserverInterface { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; /** - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager */ - public function __construct(ModuleManagerInterface $moduleManager) + public function __construct(Manager $moduleManager) { $this->moduleManager = $moduleManager; } diff --git a/app/code/Magento/Swatches/Test/Unit/Observer/AddFieldsToAttributeObserverTest.php b/app/code/Magento/Swatches/Test/Unit/Observer/AddFieldsToAttributeObserverTest.php index f8ba5c20250ad..45c680366264b 100644 --- a/app/code/Magento/Swatches/Test/Unit/Observer/AddFieldsToAttributeObserverTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Observer/AddFieldsToAttributeObserverTest.php @@ -10,7 +10,7 @@ */ class AddFieldsToAttributeObserverTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManagerMock; /** @var \Magento\Config\Model\Config\Source\Yesno|\PHPUnit_Framework_MockObject_MockObject */ diff --git a/app/code/Magento/Swatches/Test/Unit/Observer/AddSwatchAttributeTypeObserverTest.php b/app/code/Magento/Swatches/Test/Unit/Observer/AddSwatchAttributeTypeObserverTest.php index 24afa1045e5cb..f78797d93cb0d 100644 --- a/app/code/Magento/Swatches/Test/Unit/Observer/AddSwatchAttributeTypeObserverTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Observer/AddSwatchAttributeTypeObserverTest.php @@ -10,7 +10,7 @@ */ class AddSwatchAttributeTypeObserverTest extends \PHPUnit\Framework\TestCase { - /** @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManagerMock; /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject */ diff --git a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php index bba9bc3f3ebe7..4eb3995448709 100644 --- a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php @@ -34,7 +34,7 @@ class ContextPlugin /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManager; @@ -50,7 +50,7 @@ class ContextPlugin * @param \Magento\Framework\App\Http\Context $httpContext * @param \Magento\Tax\Model\Calculation\Proxy $calculation * @param \Magento\Tax\Helper\Data $taxHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\PageCache\Model\Config $cacheConfig */ public function __construct( @@ -58,7 +58,7 @@ public function __construct( \Magento\Framework\App\Http\Context $httpContext, \Magento\Tax\Model\Calculation\Proxy $calculation, \Magento\Tax\Helper\Data $taxHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\PageCache\Model\Config $cacheConfig ) { $this->customerSession = $customerSession; diff --git a/app/code/Magento/Tax/Observer/AfterAddressSaveObserver.php b/app/code/Magento/Tax/Observer/AfterAddressSaveObserver.php index ef84eac32e95a..cf5d939b35c01 100644 --- a/app/code/Magento/Tax/Observer/AfterAddressSaveObserver.php +++ b/app/code/Magento/Tax/Observer/AfterAddressSaveObserver.php @@ -8,7 +8,7 @@ use Magento\Customer\Model\Address; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\PageCache\Model\Config; use Magento\Tax\Api\TaxAddressManagerInterface; use Magento\Tax\Helper\Data; @@ -26,7 +26,7 @@ class AfterAddressSaveObserver implements ObserverInterface /** * Module manager * - * @var ModuleManagerInterface + * @var Manager */ private $moduleManager; @@ -46,13 +46,13 @@ class AfterAddressSaveObserver implements ObserverInterface /** * @param Data $taxHelper - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Config $cacheConfig * @param TaxAddressManagerInterface $addressManager */ public function __construct( Data $taxHelper, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, Config $cacheConfig, TaxAddressManagerInterface $addressManager ) { diff --git a/app/code/Magento/Tax/Observer/CustomerLoggedInObserver.php b/app/code/Magento/Tax/Observer/CustomerLoggedInObserver.php index 00b3a9f9e09ad..c1e4ad66d75d7 100644 --- a/app/code/Magento/Tax/Observer/CustomerLoggedInObserver.php +++ b/app/code/Magento/Tax/Observer/CustomerLoggedInObserver.php @@ -9,7 +9,7 @@ use Magento\Customer\Model\Session; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\PageCache\Model\Config; use Magento\Tax\Api\TaxAddressManagerInterface; use Magento\Tax\Helper\Data; @@ -33,7 +33,7 @@ class CustomerLoggedInObserver implements ObserverInterface /** * Module manager * - * @var ModuleManagerInterface + * @var Manager */ private $moduleManager; @@ -60,7 +60,7 @@ class CustomerLoggedInObserver implements ObserverInterface * @param GroupRepositoryInterface $groupRepository * @param Session $customerSession * @param Data $taxHelper - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Config $cacheConfig * @param TaxAddressManagerInterface $addressManager */ @@ -68,7 +68,7 @@ public function __construct( GroupRepositoryInterface $groupRepository, Session $customerSession, Data $taxHelper, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, Config $cacheConfig, TaxAddressManagerInterface $addressManager ) { diff --git a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php index 020baa0c30ec5..a6c7e9bb8685a 100644 --- a/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php +++ b/app/code/Magento/Tax/Test/Unit/App/Action/ContextPluginTest.php @@ -38,7 +38,7 @@ class ContextPluginTest extends \PHPUnit\Framework\TestCase /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManagerMock; @@ -89,7 +89,7 @@ protected function setUp() ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Tax/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Tax/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 2e957e528e294..571cc7173bc92 100644 --- a/app/code/Magento/Tax/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Tax/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -6,7 +6,7 @@ namespace Magento\Tax\Test\Unit\Observer; use Magento\Framework\Event\Observer; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\PageCache\Model\Config; use Magento\Tax\Api\TaxAddressManagerInterface; @@ -31,7 +31,7 @@ class AfterAddressSaveObserverTest extends \PHPUnit\Framework\TestCase /** * Module manager * - * @var ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|\PHPUnit_Framework_MockObject_MockObject */ private $moduleManagerMock; @@ -65,7 +65,7 @@ protected function setUp() ->setMethods(['getCustomerAddress']) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); @@ -77,7 +77,7 @@ protected function setUp() ->setMethods(['isCatalogPriceDisplayAffectedByTax']) ->disableOriginalConstructor() ->getMock(); - + $this->addressManagerMock = $this->getMockBuilder(TaxAddressManagerInterface::class) ->setMethods(['setDefaultAddressAfterSave', 'setDefaultAddressAfterLogIn']) ->disableOriginalConstructor() diff --git a/app/code/Magento/Tax/Test/Unit/Observer/CustomerLoggedInObserverTest.php b/app/code/Magento/Tax/Test/Unit/Observer/CustomerLoggedInObserverTest.php index facbb6733b5c8..c577f1727552f 100644 --- a/app/code/Magento/Tax/Test/Unit/Observer/CustomerLoggedInObserverTest.php +++ b/app/code/Magento/Tax/Test/Unit/Observer/CustomerLoggedInObserverTest.php @@ -31,7 +31,7 @@ class CustomerLoggedInObserverTest extends \PHPUnit\Framework\TestCase /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManagerMock; @@ -82,7 +82,7 @@ protected function setUp() ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Tax/view/frontend/layout/sales_email_item_price.xml b/app/code/Magento/Tax/view/frontend/layout/sales_email_item_price.xml index 5dc1e5c72313d..35bcfc265eafc 100644 --- a/app/code/Magento/Tax/view/frontend/layout/sales_email_item_price.xml +++ b/app/code/Magento/Tax/view/frontend/layout/sales_email_item_price.xml @@ -5,8 +5,7 @@ * See COPYING.txt for license details. */ --> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="items"> <block class="Magento\Tax\Block\Item\Price\Renderer" name="item_price" template="Magento_Tax::email/items/price/row.phtml"> @@ -16,11 +15,4 @@ </block> </referenceBlock> </body> -</page> - - - - - - - +</page> \ No newline at end of file diff --git a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php index 5d5426660d8f1..aae6f769eb500 100644 --- a/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Weee/Model/App/Action/ContextPlugin.php @@ -33,7 +33,7 @@ class ContextPlugin protected $weeeHelper; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManager; @@ -63,7 +63,7 @@ class ContextPlugin * @param \Magento\Weee\Model\Tax $weeeTax * @param \Magento\Tax\Helper\Data $taxHelper * @param \Magento\Weee\Helper\Data $weeeHelper - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\PageCache\Model\Config $cacheConfig * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -74,7 +74,7 @@ public function __construct( \Magento\Weee\Model\Tax $weeeTax, \Magento\Tax\Helper\Data $taxHelper, \Magento\Weee\Helper\Data $weeeHelper, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\PageCache\Model\Config $cacheConfig, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig diff --git a/app/code/Magento/Weee/Observer/AfterAddressSave.php b/app/code/Magento/Weee/Observer/AfterAddressSave.php index 9acea506adf67..ba15854b2dff4 100644 --- a/app/code/Magento/Weee/Observer/AfterAddressSave.php +++ b/app/code/Magento/Weee/Observer/AfterAddressSave.php @@ -8,7 +8,7 @@ use Magento\Customer\Model\Address; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\PageCache\Model\Config; use Magento\Tax\Api\TaxAddressManagerInterface; use Magento\Weee\Helper\Data; @@ -26,7 +26,7 @@ class AfterAddressSave implements ObserverInterface /** * Module manager * - * @var ModuleManagerInterface + * @var Manager */ private $moduleManager; @@ -46,13 +46,13 @@ class AfterAddressSave implements ObserverInterface /** * @param Data $weeeHelper - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Config $cacheConfig * @param TaxAddressManagerInterface $addressManager */ public function __construct( Data $weeeHelper, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, Config $cacheConfig, TaxAddressManagerInterface $addressManager ) { diff --git a/app/code/Magento/Weee/Observer/CustomerLoggedIn.php b/app/code/Magento/Weee/Observer/CustomerLoggedIn.php index 95299d96cabd2..0b22c24d7fa25 100644 --- a/app/code/Magento/Weee/Observer/CustomerLoggedIn.php +++ b/app/code/Magento/Weee/Observer/CustomerLoggedIn.php @@ -8,7 +8,7 @@ use Magento\Customer\Model\Session; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\PageCache\Model\Config; use Magento\Tax\Api\TaxAddressManagerInterface; use Magento\Weee\Helper\Data; @@ -52,13 +52,13 @@ class CustomerLoggedIn implements ObserverInterface /** * @param Data $weeeHelper - * @param ModuleManagerInterface $moduleManager + * @param Manager $moduleManager * @param Config $cacheConfig * @param TaxAddressManagerInterface $addressManager */ public function __construct( Data $weeeHelper, - ModuleManagerInterface $moduleManager, + Manager $moduleManager, Config $cacheConfig, TaxAddressManagerInterface $addressManager ) { diff --git a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php index b720f42378fa9..c829b524527a6 100644 --- a/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php +++ b/app/code/Magento/Weee/Test/Unit/App/Action/ContextPluginTest.php @@ -39,7 +39,7 @@ class ContextPluginTest extends \PHPUnit\Framework\TestCase protected $taxCalculationMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $moduleManagerMock; @@ -93,7 +93,7 @@ protected function setUp() ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Weee/Test/Unit/Observer/AfterAddressSaveTest.php b/app/code/Magento/Weee/Test/Unit/Observer/AfterAddressSaveTest.php index a7b88f5727126..868d603f34b8c 100644 --- a/app/code/Magento/Weee/Test/Unit/Observer/AfterAddressSaveTest.php +++ b/app/code/Magento/Weee/Test/Unit/Observer/AfterAddressSaveTest.php @@ -18,7 +18,7 @@ class AfterAddressSaveTest extends \PHPUnit\Framework\TestCase * @var ObjectManager */ private $objectManager; - + /** * @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject */ @@ -27,7 +27,7 @@ class AfterAddressSaveTest extends \PHPUnit\Framework\TestCase /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ private $moduleManagerMock; @@ -42,7 +42,7 @@ class AfterAddressSaveTest extends \PHPUnit\Framework\TestCase * @var \Magento\Weee\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ private $weeeHelperMock; - + /** * @var TaxAddressManagerInterface|MockObject */ @@ -61,7 +61,7 @@ protected function setUp() ->setMethods(['getCustomerAddress']) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); @@ -129,7 +129,7 @@ public function testExecute( $this->addressManagerMock->expects($isNeedSetAddress ? $this->once() : $this->never()) ->method('setDefaultAddressAfterSave') ->with($address); - + $this->session->execute($this->observerMock); } diff --git a/app/code/Magento/Weee/Test/Unit/Observer/CustomerLoggedInTest.php b/app/code/Magento/Weee/Test/Unit/Observer/CustomerLoggedInTest.php index 06d1dbedcfd80..af8c2e70a8ff6 100644 --- a/app/code/Magento/Weee/Test/Unit/Observer/CustomerLoggedInTest.php +++ b/app/code/Magento/Weee/Test/Unit/Observer/CustomerLoggedInTest.php @@ -21,7 +21,7 @@ class CustomerLoggedInTest extends \PHPUnit\Framework\TestCase /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManagerMock; @@ -59,7 +59,7 @@ protected function setUp() ) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php b/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php index b55766d77fee3..d0397be83fac7 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Helper/RssTest.php @@ -46,7 +46,7 @@ class RssTest extends \PHPUnit\Framework\TestCase protected $customerRepositoryMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ protected $moduleManagerMock; @@ -80,7 +80,7 @@ protected function setUp() $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) ->getMock(); - $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\ModuleManagerInterface::class) + $this->moduleManagerMock = $this->getMockBuilder(\Magento\Framework\Module\Manager::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/etc/di.xml b/app/etc/di.xml index edb206a83c00a..2863c5d4708df 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -65,7 +65,6 @@ <preference for="Magento\Framework\Config\CacheInterface" type="Magento\Framework\App\Cache\Type\Config" /> <preference for="Magento\Framework\Config\ValidationStateInterface" type="Magento\Framework\App\Arguments\ValidationState" /> <preference for="Magento\Framework\Module\ModuleListInterface" type="Magento\Framework\Module\ModuleList" /> - <preference for="Magento\Framework\Module\ModuleManagerInterface" type="Magento\Framework\Module\Manager" /> <preference for="Magento\Framework\Component\ComponentRegistrarInterface" type="Magento\Framework\Component\ComponentRegistrar"/> <preference for="Magento\Framework\Event\ConfigInterface" type="Magento\Framework\Event\Config" /> <preference for="Magento\Framework\Event\InvokerInterface" type="Magento\Framework\Event\Invoker\InvokerDefault" /> diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Account/Dashboard/AddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/Dashboard/AddressTest.php index b159dceadfb77..c5b76807f1ff9 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Account/Dashboard/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/Dashboard/AddressTest.php @@ -69,7 +69,7 @@ public function testGetCustomer() public function testGetCustomerMissingCustomer() { - $moduleManager = $this->objectManager->get(\Magento\Framework\Module\ModuleManagerInterface::class); + $moduleManager = $this->objectManager->get(\Magento\Framework\Module\Manager::class); if ($moduleManager->isEnabled('Magento_PageCache')) { $customerDataFactory = $this->objectManager->create( \Magento\Customer\Api\Data\CustomerInterfaceFactory::class diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index 9417baea67ba9..ff8e7db0f4260 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -1982,8 +1982,8 @@ ['_escapeDefaultValue', 'Magento\Framework\Code\Generator\EntityAbstract'], ['urlEncode', 'Magento\Framework\App\Helper\AbstractHelper', 'Magento\Framework\Url\EncoderInterface::encode'], ['urlDecode', 'Magento\Framework\App\Helper\AbstractHelper', 'Magento\Framework\Url\DecoderInterface::decode'], - ['isModuleEnabled', 'Magento\Framework\App\Helper\AbstractHelper', '\Magento\Framework\Module\ModuleManagerInterface::isEnabled()'], - ['isModuleOutputEnabled', 'Magento\Framework\App\Helper\AbstractHelper', '\Magento\Framework\Module\ModuleManagerInterface::isOutputEnabled()'], + ['isModuleEnabled', 'Magento\Framework\App\Helper\AbstractHelper', 'Magento\Framework\Module\Manager::isEnabled()'], + ['isModuleOutputEnabled', 'Magento\Framework\App\Helper\AbstractHelper', 'Magento\Framework\Module\Manager::isOutputEnabled()'], ['_packToTar', 'Magento\Framework\Archive\Tar'], ['_parseHeader', 'Magento\Framework\Archive\Tar'], ['getIdentities', 'Magento\Wishlist\Block\Link'], diff --git a/lib/internal/Magento/Framework/App/Helper/AbstractHelper.php b/lib/internal/Magento/Framework/App/Helper/AbstractHelper.php index 7875e0c8885f5..1a76610bc0811 100644 --- a/lib/internal/Magento/Framework/App/Helper/AbstractHelper.php +++ b/lib/internal/Magento/Framework/App/Helper/AbstractHelper.php @@ -27,7 +27,7 @@ abstract class AbstractHelper protected $_request; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -125,7 +125,7 @@ protected function _getModuleName() * * @param string $moduleName Full module name * @return boolean - * use \Magento\Framework\Module\ModuleManagerInterface::isOutputEnabled() + * use \Magento\Framework\Module\Manager::isOutputEnabled() */ public function isModuleOutputEnabled($moduleName = null) { diff --git a/lib/internal/Magento/Framework/App/Helper/Context.php b/lib/internal/Magento/Framework/App/Helper/Context.php index cc57f109cc8ec..5b90df8c55205 100644 --- a/lib/internal/Magento/Framework/App/Helper/Context.php +++ b/lib/internal/Magento/Framework/App/Helper/Context.php @@ -21,7 +21,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface { /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ protected $_moduleManager; @@ -79,7 +79,7 @@ class Context implements \Magento\Framework\ObjectManager\ContextInterface * @param \Magento\Framework\Url\EncoderInterface $urlEncoder * @param \Magento\Framework\Url\DecoderInterface $urlDecoder * @param \Psr\Log\LoggerInterface $logger - * @param \Magento\Framework\Module\ModuleManagerInterface $moduleManager + * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\Framework\App\RequestInterface $httpRequest * @param \Magento\Framework\Cache\ConfigInterface $cacheConfig * @param \Magento\Framework\Event\ManagerInterface $eventManager @@ -94,7 +94,7 @@ public function __construct( \Magento\Framework\Url\EncoderInterface $urlEncoder, \Magento\Framework\Url\DecoderInterface $urlDecoder, \Psr\Log\LoggerInterface $logger, - \Magento\Framework\Module\ModuleManagerInterface $moduleManager, + \Magento\Framework\Module\Manager $moduleManager, \Magento\Framework\App\RequestInterface $httpRequest, \Magento\Framework\Cache\ConfigInterface $cacheConfig, \Magento\Framework\Event\ManagerInterface $eventManager, @@ -119,7 +119,7 @@ public function __construct( /** * Get module manager. * - * @return \Magento\Framework\Module\ModuleManagerInterface + * @return \Magento\Framework\Module\Manager */ public function getModuleManager() { diff --git a/lib/internal/Magento/Framework/Module/Manager.php b/lib/internal/Magento/Framework/Module/Manager.php index 659ada3c20ac8..b47349631a033 100644 --- a/lib/internal/Magento/Framework/Module/Manager.php +++ b/lib/internal/Magento/Framework/Module/Manager.php @@ -12,9 +12,14 @@ namespace Magento\Framework\Module; /** - * @inheritdoc + * Module status manager + * + * Usage: + * ```php + * $manager->isEnabled('Vendor_Module'); + * ``` */ -class Manager implements ModuleManagerInterface +class Manager { /** * @var Output\ConfigInterface @@ -49,9 +54,12 @@ public function __construct( } /** - * @inheritdoc + * Whether a module is enabled in the configuration or not + * + * @param string $moduleName Fully-qualified module name + * @return boolean */ - public function isEnabled(string $moduleName): bool + public function isEnabled($moduleName) { return $this->moduleList->has($moduleName); } diff --git a/lib/internal/Magento/Framework/Module/ModuleManagerInterface.php b/lib/internal/Magento/Framework/Module/ModuleManagerInterface.php deleted file mode 100644 index decc91200354d..0000000000000 --- a/lib/internal/Magento/Framework/Module/ModuleManagerInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Framework\Module; - -/** - * Module status manager - * - * Usage: - * ```php - * $manager->isEnabled('Vendor_Module'); - * ``` - */ -interface ModuleManagerInterface -{ - /** - * Retrieve whether or not a module is enabled by configuration - * - * @param string $moduleName Fully-qualified module name, e.g. Magento_Config - * @return boolean Whether or not the module is enabled in the configuration - */ - public function isEnabled(string $moduleName): bool; -} diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php index e4cf4c41599e9..748474943a436 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ManagerTest.php @@ -16,7 +16,7 @@ class ManagerTest extends \PHPUnit\Framework\TestCase const XML_PATH_OUTPUT_ENABLED = 'custom/is_module_output_enabled'; /** - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $_model; @@ -73,7 +73,6 @@ public function testIsEnabled() public function testIsOutputEnabledReturnsFalseForDisabledModule() { - $this->_moduleList->expects($this->once())->method('has')->with('Disabled_Module')->willReturn(false); $this->_outputConfig->expects($this->any())->method('isSetFlag')->will($this->returnValue(true)); $this->assertFalse($this->_model->isOutputEnabled('Disabled_Module')); } diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php index 7a631eed1adbf..bfd916dbfba5b 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/Plugin/DbStatusValidatorTest.php @@ -35,7 +35,7 @@ class DbStatusValidatorTest extends \PHPUnit\Framework\TestCase protected $requestMock; /** - * @var \Magento\Framework\Module\ModuleManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject */ private $moduleManager; diff --git a/lib/internal/Magento/Framework/View/File/Collector/Decorator/ModuleOutput.php b/lib/internal/Magento/Framework/View/File/Collector/Decorator/ModuleOutput.php index 71fa8d2c0ae6b..34f32b2f6b7b2 100644 --- a/lib/internal/Magento/Framework/View/File/Collector/Decorator/ModuleOutput.php +++ b/lib/internal/Magento/Framework/View/File/Collector/Decorator/ModuleOutput.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\File\Collector\Decorator; -use Magento\Framework\Module\ModuleManagerInterface; +use Magento\Framework\Module\Manager; use Magento\Framework\View\Design\ThemeInterface; use Magento\Framework\View\File; use Magento\Framework\View\File\CollectorInterface; @@ -26,7 +26,7 @@ class ModuleOutput implements CollectorInterface /** * Module manager * - * @var \Magento\Framework\Module\ModuleManagerInterface + * @var \Magento\Framework\Module\Manager */ private $moduleManager; @@ -38,7 +38,7 @@ class ModuleOutput implements CollectorInterface */ public function __construct( CollectorInterface $subject, - ModuleManagerInterface $moduleManager + Manager $moduleManager ) { $this->subject = $subject; $this->moduleManager = $moduleManager; From 9687ccf68d7d6c8b6059e3bc018e28ac60915962 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 9 Aug 2019 15:37:29 -0500 Subject: [PATCH 0349/2437] MSI-2471: Fix Elasticsearch support for MSI --- .../Indexer/Fulltext/Action/DataProvider.php | 81 ------------ .../Plugin/StockedProductsFilterPlugin.php | 91 +++++++++++++ .../StockedProductsFilterPluginTest.php | 125 ++++++++++++++++++ app/code/Magento/CatalogSearch/etc/di.xml | 3 + 4 files changed, 219 insertions(+), 81 deletions(-) create mode 100644 app/code/Magento/CatalogSearch/Model/Indexer/Plugin/StockedProductsFilterPlugin.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 09d4f0068459a..cd2529a8fd725 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -7,14 +7,9 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status; -use Magento\CatalogInventory\Api\Data\StockStatusInterface; -use Magento\CatalogInventory\Api\StockConfigurationInterface; -use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; -use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; use Magento\Store\Model\Store; -use Magento\Framework\App\ObjectManager; /** * Catalog search full test search data provider. @@ -129,16 +124,6 @@ class DataProvider */ private $antiGapMultiplier; - /** - * @var StockConfigurationInterface - */ - private $stockConfiguration; - - /** - * @var StockStatusRepositoryInterface - */ - private $stockStatusRepository; - /** * @param ResourceConnection $resource * @param \Magento\Catalog\Model\Product\Type $catalogProductType @@ -563,8 +548,6 @@ public function prepareProductIndex($indexData, $productData, $storeId) { $index = []; - $indexData = $this->filterOutOfStockProducts($indexData, $storeId); - foreach ($this->getSearchableAttributes('static') as $attribute) { $attributeCode = $attribute->getAttributeCode(); @@ -689,68 +672,4 @@ private function filterAttributeValue($value) { return preg_replace('/\s+/iu', ' ', trim(strip_tags($value))); } - - /** - * Filter out of stock products for products. - * - * @param array $indexData - * @param int $storeId - * @return array - */ - private function filterOutOfStockProducts($indexData, $storeId): array - { - if (!$this->getStockConfiguration()->isShowOutOfStock($storeId)) { - $productIds = array_keys($indexData); - $stockStatusCriteria = $this->createStockStatusCriteria(); - $stockStatusCriteria->setProductsFilter($productIds); - $stockStatusCollection = $this->getStockStatusRepository()->getList($stockStatusCriteria); - $stockStatuses = $stockStatusCollection->getItems(); - $stockStatuses = array_filter( - $stockStatuses, - function (StockStatusInterface $stockStatus) { - return StockStatusInterface::STATUS_IN_STOCK == $stockStatus->getStockStatus(); - } - ); - $indexData = array_intersect_key($indexData, $stockStatuses); - } - return $indexData; - } - - /** - * Get stock configuration. - * - * @return StockConfigurationInterface - */ - private function getStockConfiguration() - { - if (null === $this->stockConfiguration) { - $this->stockConfiguration = ObjectManager::getInstance()->get(StockConfigurationInterface::class); - } - return $this->stockConfiguration; - } - - /** - * Create stock status criteria. - * - * Substitution of autogenerated factory in backward compatibility reasons. - * - * @return StockStatusCriteriaInterface - */ - private function createStockStatusCriteria() - { - return ObjectManager::getInstance()->create(StockStatusCriteriaInterface::class); - } - - /** - * Get stock status repository. - * - * @return StockStatusRepositoryInterface - */ - private function getStockStatusRepository() - { - if (null === $this->stockStatusRepository) { - $this->stockStatusRepository = ObjectManager::getInstance()->get(StockStatusRepositoryInterface::class); - } - return $this->stockStatusRepository; - } } diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Plugin/StockedProductsFilterPlugin.php b/app/code/Magento/CatalogSearch/Model/Indexer/Plugin/StockedProductsFilterPlugin.php new file mode 100644 index 0000000000000..02e48c5d8a1c0 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Plugin/StockedProductsFilterPlugin.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Indexer\Plugin; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; +use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider; + +/** + * Plugin for filtering child products that are out of stock for preventing their saving to catalog search index. + * + * This plugin reverts changes introduced in commit 9ab466d8569ea556cb01393989579c3aac53d9a3 which break extensions + * relying on stocks. Plugin location is changed for consistency purposes. + */ +class StockedProductsFilterPlugin +{ + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var StockStatusRepositoryInterface + */ + private $stockStatusRepository; + + /** + * @var StockStatusCriteriaInterfaceFactory + */ + private $stockStatusCriteriaFactory; + + /** + * @param StockConfigurationInterface $stockConfiguration + * @param StockStatusRepositoryInterface $stockStatusRepository + * @param StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory + */ + public function __construct( + StockConfigurationInterface $stockConfiguration, + StockStatusRepositoryInterface $stockStatusRepository, + StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory + ) { + $this->stockConfiguration = $stockConfiguration; + $this->stockStatusRepository = $stockStatusRepository; + $this->stockStatusCriteriaFactory = $stockStatusCriteriaFactory; + } + + /** + * Filter out of stock options for configurable product. + * + * @param DataProvider $dataProvider + * @param array $indexData + * @param array $productData + * @param int $storeId + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforePrepareProductIndex( + DataProvider $dataProvider, + array $indexData, + array $productData, + int $storeId + ): array { + if (!$this->stockConfiguration->isShowOutOfStock($storeId)) { + $productIds = array_keys($indexData); + $stockStatusCriteria = $this->stockStatusCriteriaFactory->create(); + $stockStatusCriteria->setProductsFilter($productIds); + $stockStatusCollection = $this->stockStatusRepository->getList($stockStatusCriteria); + $stockStatuses = $stockStatusCollection->getItems(); + $stockStatuses = array_filter( + $stockStatuses, + function (StockStatusInterface $stockStatus) { + return StockStatusInterface::STATUS_IN_STOCK == $stockStatus->getStockStatus(); + } + ); + $indexData = array_intersect_key($indexData, $stockStatuses); + } + + return [ + $indexData, + $productData, + $storeId, + ]; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php new file mode 100644 index 0000000000000..eb3060c16a143 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Test\Unit\Model\Indexer\Plugin; + +use Magento\CatalogSearch\Model\Indexer\Plugin\StockedProductsFilterPlugin; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Api\StockStatusRepositoryInterface; +use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory; +use Magento\CatalogInventory\Api\StockStatusCriteriaInterface; +use Magento\CatalogInventory\Api\Data\StockStatusCollectionInterface; +use Magento\CatalogInventory\Api\Data\StockStatusInterface; +use Magento\CatalogInventory\Model\Stock; +use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider; + +/** + * Test for Magento\CatalogSearch\Model\Indexer\Plugin\StockedProductsFilterPlugin class. + * + * This plugin reverts changes introduced in commit 9ab466d8569ea556cb01393989579c3aac53d9a3 which break extensions + * relying on stocks. Plugin location is changed for consistency purposes. + */ +class StockedProductsFilterPluginTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $stockConfigurationMock; + + /** + * @var StockStatusRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $stockStatusRepositoryMock; + + /** + * @var StockStatusCriteriaInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $stockStatusCriteriaFactoryMock; + + /** + * @var StockedProductsFilterPlugin + */ + private $plugin; + + /** + * {@inheritdoc} + */ + protected function setUp() + { + $this->stockConfigurationMock = $this->getMockBuilder(StockConfigurationInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->stockStatusRepositoryMock = $this->getMockBuilder(StockStatusRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->stockStatusCriteriaFactoryMock = $this->getMockBuilder(StockStatusCriteriaInterfaceFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->plugin = new StockedProductsFilterPlugin( + $this->stockConfigurationMock, + $this->stockStatusRepositoryMock, + $this->stockStatusCriteriaFactoryMock + ); + } + + /** + * @return void + */ + public function testBeforePrepareProductIndex(): void + { + /** @var DataProvider|\PHPUnit_Framework_MockObject_MockObject $dataProviderMock */ + $dataProviderMock = $this->getMockBuilder(DataProvider::class)->disableOriginalConstructor()->getMock(); + $indexData = [ + 1 => [], + 2 => [], + ]; + $productData = []; + $storeId = 1; + + $this->stockConfigurationMock + ->expects($this->once()) + ->method('isShowOutOfStock') + ->willReturn(false); + + $stockStatusCriteriaMock = $this->getMockBuilder(StockStatusCriteriaInterface::class)->getMock(); + $stockStatusCriteriaMock + ->expects($this->once()) + ->method('setProductsFilter') + ->willReturn(true); + $this->stockStatusCriteriaFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($stockStatusCriteriaMock); + + $stockStatusMock = $this->getMockBuilder(StockStatusInterface::class)->getMock(); + $stockStatusMock->expects($this->atLeastOnce()) + ->method('getStockStatus') + ->willReturnOnConsecutiveCalls(Stock::STOCK_IN_STOCK, Stock::STOCK_OUT_OF_STOCK); + $stockStatusCollectionMock = $this->getMockBuilder(StockStatusCollectionInterface::class)->getMock(); + $stockStatusCollectionMock + ->expects($this->once()) + ->method('getItems') + ->willReturn([ + 1 => $stockStatusMock, + 2 => $stockStatusMock, + ]); + $this->stockStatusRepositoryMock + ->expects($this->once()) + ->method('getList') + ->willReturn($stockStatusCollectionMock); + + list ($indexData, $productData, $storeId) = $this->plugin->beforePrepareProductIndex( + $dataProviderMock, + $indexData, + $productData, + $storeId + ); + + $this->assertEquals([1], array_keys($indexData)); + } +} diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 28d5035308dee..63de2dc00926b 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -373,4 +373,7 @@ </argument> </arguments> </type> + <type name="Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider"> + <plugin name="stockedProductsFilterPlugin" type="Magento\CatalogSearch\Model\Indexer\Plugin\StockedProductsFilterPlugin"/> + </type> </config> From fa628ed3f3134dcd319a0e57a961ff6323f6ef30 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 9 Aug 2019 16:59:37 -0500 Subject: [PATCH 0350/2437] MSI-2471: Fixed static test failure --- .../Model/Indexer/Plugin/StockedProductsFilterPluginTest.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php index eb3060c16a143..b9909ec2c74b2 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Indexer/Plugin/StockedProductsFilterPluginTest.php @@ -104,10 +104,7 @@ public function testBeforePrepareProductIndex(): void $stockStatusCollectionMock ->expects($this->once()) ->method('getItems') - ->willReturn([ - 1 => $stockStatusMock, - 2 => $stockStatusMock, - ]); + ->willReturn([1 => $stockStatusMock, 2 => $stockStatusMock]); $this->stockStatusRepositoryMock ->expects($this->once()) ->method('getList') From fd73c392026c990721600e5e49f4b096973d99c7 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 12 Aug 2019 15:35:51 +0300 Subject: [PATCH 0351/2437] MC-19075: Html text are visible on frontend --- .../Checkout/view/frontend/templates/onepage/review/item.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml index 2a7ccc38e9d83..8e83526b4ceaa 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml @@ -30,7 +30,7 @@ $taxDataHelper = $this->helper(Magento\Tax\Helper\Data::class); <?php if (isset($_formatedOptionValue['full_view'])) :?> <?= $block->escapeHtml($_formatedOptionValue['full_view']) ?> <?php else :?> - <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> <?php endif; ?> </dd> <?php endforeach; ?> From ee853472c01c0d7e02da6f8659d0f13657a47854 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Mon, 12 Aug 2019 18:18:55 +0300 Subject: [PATCH 0352/2437] MC-19140: [DHL] You are still on New Shipment page after creating the shipment --- .../view/adminhtml/templates/order/packaging/popup.phtml | 2 +- .../Magento/Shipping/view/adminhtml/templates/view/form.phtml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml index cd25cb919adb5..28322d9534926 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml @@ -31,7 +31,7 @@ $girthEnabled = $block->isDisplayGirthValue() && $block->isGirthAllowed() ? 1 : packaging.sendCreateLabelRequest(); }); packaging.setLabelCreatedCallback(function(response){ - setLocation("<?php $block->escapeJs($block->escapeUrl($block->getUrl( + setLocation("<?= $block->escapeJs($block->escapeUrl($block->getUrl( 'sales/order/view', ['order_id' => $block->getShipment()->getOrderId()] ))); ?>"); diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml index f105562151082..44fe4b9ccd353 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml @@ -85,7 +85,7 @@ $order = $block->getShipment()->getOrder(); window.packaging.sendCreateLabelRequest(); }); window.packaging.setLabelCreatedCallback(function () { - setLocation("<?php $block->escapeUrl($block->getUrl('adminhtml/order_shipment/view', ['shipment_id' => $block->getShipment()->getId()])); ?>"); + setLocation("<?= $block->escapeUrl($block->getUrl('adminhtml/order_shipment/view', ['shipment_id' => $block->getShipment()->getId()])); ?>"); }); }; From f49f1f5592c9263e339bf42f2e842cc1fcfe63e6 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Mon, 12 Aug 2019 11:48:59 -0500 Subject: [PATCH 0353/2437] MC-19061: Revert ticket MC-17003 --- .../Order/Creditmemo/Create/Adjustments.php | 16 ----- .../Order/Creditmemo/Create/Items.php | 17 +---- ...dminCheckingCreditMemoUpdateTotalsTest.xml | 3 + .../order/creditmemo/create/items.phtml | 3 - .../web/css/source/module/order/_total.less | 4 -- .../Adminhtml/Order/Creditmemo/Totals.php | 68 ------------------- .../Test/TestStep/CreateCreditMemoStep.php | 32 +-------- 7 files changed, 5 insertions(+), 138 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php index 1210391f70ddb..50d29c195968c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php @@ -111,20 +111,4 @@ public function getShippingLabel() } return $label; } - - /** - * Get update totals url. - * - * @return string - */ - public function getUpdateTotalsUrl(): string - { - return $this->getUrl( - 'sales/*/updateQty', - [ - 'order_id' => $this->getSource()->getOrderId(), - 'invoice_id' => $this->getRequest()->getParam('invoice_id', null), - ] - ); - } } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Items.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Items.php index 389c29bedf4c3..65163f9ed5d82 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Items.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Items.php @@ -56,12 +56,7 @@ protected function _prepareLayout() $this->addChild( 'update_button', \Magento\Backend\Block\Widget\Button::class, - ['label' => __('Update Qty\'s'), 'class' => 'update-button secondary', 'onclick' => $onclick] - ); - $this->addChild( - 'update_totals_button', - \Magento\Backend\Block\Widget\Button::class, - ['label' => __('Update Totals'), 'class' => 'update-totals-button secondary', 'onclick' => $onclick] + ['label' => __('Update Qty\'s'), 'class' => 'update-button', 'onclick' => $onclick] ); if ($this->getCreditmemo()->canRefund()) { @@ -181,16 +176,6 @@ public function getUpdateButtonHtml() return $this->getChildHtml('update_button'); } - /** - * Get update totals button html - * - * @return string - */ - public function getUpdateTotalsButtonHtml(): string - { - return $this->getChildHtml('update_totals_button'); - } - /** * Get update url * diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml index 8cd2b8ee60edd..45ea09a06ed26 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-18159"/> <useCaseId value="MC-17003"/> <group value="sales"/> + <skip> + <issueId value="MC-17003"/> + </skip> </annotations> <before> <!--Create product--> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml index 31aefd8d2ca57..16ab1750efeb6 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml @@ -100,7 +100,6 @@ <span class="title"><?= $block->escapeHtml(__('Refund Totals')) ?></span> </div> <?= $block->getChildHtml('creditmemo_totals') ?> - <div class="totals-actions"><?= $block->getUpdateTotalsButtonHtml() ?></div> <div class="order-totals-actions"> <div class="field choice admin__field admin__field-option field-append-comments"> <input id="notify_customer" @@ -140,8 +139,6 @@ require(['jquery'], function(jQuery){ //<![CDATA[ var submitButtons = jQuery('.submit-button'); -var updateButtons = jQuery('.update-button,.update-totals-button'); -var fields = jQuery('.qty-input,.order-subtotal-table input[type="text"]'); function enableButtons(buttons) { buttons.removeClass('disabled').prop('disabled', false); diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less index 6e663b15c89cc..f2369ad8f35e1 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less @@ -22,10 +22,6 @@ } } -.totals-actions { - text-align: right; -} - .order-totals-actions { margin-top: @indent__s; .actions { diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php index 28bb00757dac1..d98c5696c81f8 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php @@ -27,34 +27,6 @@ class Totals extends \Magento\Sales\Test\Block\Adminhtml\Order\Totals */ protected $capture = '[name="invoice[capture_case]"]'; - /** - * Refund Shipping css selector. - * - * @var string - */ - private $refundShippingSelector = '#shipping_amount'; - - /** - * Adjustment Refund css selector. - * - * @var string - */ - private $adjustmentRefundSelector = '#adjustment_positive'; - - /** - * Adjustment Fee css selector. - * - * @var string - */ - private $adjustmentFeeSelector = '#adjustment_negative'; - - /** - * Update Totals button css selector. - * - * @var string - */ - private $updateTotalsSelector = '.update-totals-button'; - /** * Submit invoice. * @@ -85,44 +57,4 @@ public function setCaptureOption($option) { $this->_rootElement->find($this->capture, Locator::SELECTOR_CSS, 'select')->setValue($option); } - - /** - * Get Refund Shipping input element. - * - * @return \Magento\Mtf\Client\ElementInterface - */ - public function getRefundShippingElement() - { - return $this->_rootElement->find($this->refundShippingSelector, Locator::SELECTOR_CSS); - } - - /** - * Get Adjustment Refund input element. - * - * @return \Magento\Mtf\Client\ElementInterface - */ - public function getAdjustmentRefundElement() - { - return $this->_rootElement->find($this->adjustmentRefundSelector, Locator::SELECTOR_CSS); - } - - /** - * Get Adjustment Fee input element. - * - * @return \Magento\Mtf\Client\ElementInterface - */ - public function getAdjustmentFeeElement() - { - return $this->_rootElement->find($this->adjustmentFeeSelector, Locator::SELECTOR_CSS); - } - - /** - * Click update totals button. - * - * @return void - */ - public function clickUpdateTotals() - { - $this->_rootElement->find($this->updateTotalsSelector)->click(); - } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php index 25b576a06dd3f..45298c5898c25 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php @@ -95,12 +95,8 @@ public function run() if ($this->compare($items, $refundData)) { $this->orderCreditMemoNew->getFormBlock()->updateQty(); } - $hasChangeTotals = $this->isTotalsDataChanged($refundData); - $this->orderCreditMemoNew->getFormBlock()->fillFormData($refundData); - if ($hasChangeTotals) { - $this->orderCreditMemoNew->getTotalsBlock()->clickUpdateTotals(); - } + $this->orderCreditMemoNew->getFormBlock()->fillFormData($refundData); $this->orderCreditMemoNew->getFormBlock()->submit(); } @@ -120,30 +116,4 @@ protected function getCreditMemoIds() $this->salesOrderView->getOrderForm()->openTab('creditmemos'); return $this->salesOrderView->getOrderForm()->getTab('creditmemos')->getGridBlock()->getIds(); } - - /** - * Is totals data changed. - * - * @param array $data - * @return bool - */ - private function isTotalsDataChanged(array $data): bool - { - $compareData = [ - 'shipping_amount' => - $this->orderCreditMemoNew->getTotalsBlock()->getRefundShippingElement()->getValue(), - 'adjustment_positive' => - $this->orderCreditMemoNew->getTotalsBlock()->getAdjustmentRefundElement()->getValue(), - 'adjustment_negative' => - $this->orderCreditMemoNew->getTotalsBlock()->getAdjustmentFeeElement()->getValue(), - ]; - - foreach ($compareData as $fieldName => $fieldValue) { - if (isset($data['form_data'][$fieldName]) && $fieldValue != $data['form_data'][$fieldName]) { - return true; - } - } - - return false; - } } From 09ec6714cc137b411d8facb3025164d94429e910 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Mon, 12 Aug 2019 14:08:56 -0500 Subject: [PATCH 0354/2437] MC-19115: Admin Analytics tracking should be enabled by default - Added caching in product metadata to improve performance --- .../Magento/Framework/App/ProductMetadata.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php index c9fde94352a71..3683d7ecf61d9 100644 --- a/lib/internal/Magento/Framework/App/ProductMetadata.php +++ b/lib/internal/Magento/Framework/App/ProductMetadata.php @@ -28,6 +28,11 @@ class ProductMetadata implements ProductMetadataInterface */ const PRODUCT_NAME = 'Magento'; + /** + * Magento version cache prefix + */ + const CACHE_PREFIX = 'mage-version'; + /** * Product version * @@ -47,11 +52,21 @@ class ProductMetadata implements ProductMetadataInterface private $composerInformation; /** + * @var CacheInterface + */ + private $cache; + + /** + * ProductMetadata constructor. * @param ComposerJsonFinder $composerJsonFinder + * @param \Magento\Framework\App\CacheInterface $cache */ - public function __construct(ComposerJsonFinder $composerJsonFinder) - { + public function __construct( + ComposerJsonFinder $composerJsonFinder, + CacheInterface $cache = null + ) { $this->composerJsonFinder = $composerJsonFinder; + $this->cache = $cache ?? ObjectManager::getInstance()->get(CacheInterface::class); } /** @@ -61,6 +76,8 @@ public function __construct(ComposerJsonFinder $composerJsonFinder) */ public function getVersion() { + $versionFromCache = $this->cache->load(self::CACHE_PREFIX); + $this->version = $this->version ?: $versionFromCache; if (!$this->version) { if (!($this->version = $this->getSystemPackageVersion())) { if ($this->getComposerInformation()->isMagentoRoot()) { @@ -68,6 +85,7 @@ public function getVersion() } else { $this->version = 'UNKNOWN'; } + $this->cache->save($this->version, self::CACHE_PREFIX, [Config::CACHE_TAG]); } } return $this->version; From f1b284b478356cb7b3a544049b35b8e3f5062eb5 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Mon, 12 Aug 2019 15:51:45 -0500 Subject: [PATCH 0355/2437] MC-18977: Revert change of ENGCOM-3260 - fix static failures --- app/code/Magento/Catalog/Block/Product/ProductList/Related.php | 1 + app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php | 1 + app/code/Magento/Catalog/Model/Product.php | 1 + app/code/Magento/Tax/Model/App/Action/ContextPlugin.php | 2 ++ 4 files changed, 5 insertions(+) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php index 6de70bb971367..24811d61a7715 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Related.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Related.php @@ -141,6 +141,7 @@ public function getIdentities() { $identities = []; foreach ($this->getItems() as $item) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $identities = array_merge($identities, $item->getIdentities()); } return $identities; diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php index 24822447ae915..fa1beaf6e0ea8 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Upsell.php @@ -264,6 +264,7 @@ public function getIdentities() { $identities = []; foreach ($this->getItems() as $item) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $identities = array_merge($identities, $item->getIdentities()); } return $identities; diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index fc3cc11cade54..2af54b8086a73 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -837,6 +837,7 @@ public function getStoreIds() } foreach ($websiteIds as $websiteId) { $websiteStores = $this->_storeManager->getWebsite($websiteId)->getStoreIds(); + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $storeIds = array_merge($storeIds, $websiteStores); } } diff --git a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php index 4eb3995448709..992f206279b26 100644 --- a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php @@ -52,6 +52,8 @@ class ContextPlugin * @param \Magento\Tax\Helper\Data $taxHelper * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\PageCache\Model\Config $cacheConfig + * + * phpcs:ignore Magento2.Classes.DiscouragedDependencies */ public function __construct( \Magento\Customer\Model\Session $customerSession, From 1ab19302128366b99a6847af3133de23be2ad5c8 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Mon, 12 Aug 2019 16:07:56 -0500 Subject: [PATCH 0356/2437] MC-19161: Revert ENGCOM-2983 --- .../Config/ConfigOptionsListConstants.php | 19 ---- .../Setup/Controller/DatabaseCheck.php | 36 +------- .../Magento/Setup/Model/ConfigGenerator.php | 14 +-- .../Magento/Setup/Model/ConfigOptionsList.php | 48 +--------- .../Model/ConfigOptionsList/DriverOptions.php | 72 --------------- setup/src/Magento/Setup/Model/Installer.php | 30 +------ .../Setup/Model/RequestDataConverter.php | 8 -- .../Test/Unit/Model/ConfigGeneratorTest.php | 12 --- .../Test/Unit/Model/ConfigOptionsListTest.php | 21 +---- .../Test/Unit/Module/ConfigGeneratorTest.php | 9 +- .../Validator/AdminCredentialsValidator.php | 15 +--- .../Magento/Setup/Validator/DbValidator.php | 22 ----- setup/view/magento/setup/add-database.phtml | 88 ------------------- 13 files changed, 11 insertions(+), 383 deletions(-) delete mode 100644 setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php diff --git a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php index ab32504baaa0e..6bdb74ef7b89a 100644 --- a/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php +++ b/lib/internal/Magento/Framework/Config/ConfigOptionsListConstants.php @@ -21,7 +21,6 @@ class ConfigOptionsListConstants const CONFIG_PATH_CRYPT_KEY = 'crypt/key'; const CONFIG_PATH_SESSION_SAVE = 'session/save'; const CONFIG_PATH_RESOURCE_DEFAULT_SETUP = 'resource/default_setup/connection'; - const CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS = 'db/connection/default/driver_options'; const CONFIG_PATH_DB_CONNECTION_DEFAULT = 'db/connection/default'; const CONFIG_PATH_DB_CONNECTIONS = 'db/connection'; const CONFIG_PATH_DB_PREFIX = 'db/table_prefix'; @@ -65,10 +64,6 @@ class ConfigOptionsListConstants const INPUT_KEY_DB_MODEL = 'db-model'; const INPUT_KEY_DB_INIT_STATEMENTS = 'db-init-statements'; const INPUT_KEY_DB_ENGINE = 'db-engine'; - const INPUT_KEY_DB_SSL_KEY = 'db-ssl-key'; - const INPUT_KEY_DB_SSL_CERT = 'db-ssl-cert'; - const INPUT_KEY_DB_SSL_CA = 'db-ssl-ca'; - const INPUT_KEY_DB_SSL_VERIFY = 'db-ssl-verify'; const INPUT_KEY_RESOURCE = 'resource'; const INPUT_KEY_SKIP_DB_VALIDATION = 'skip-db-validation'; const INPUT_KEY_CACHE_HOSTS = 'http-cache-hosts'; @@ -109,20 +104,6 @@ class ConfigOptionsListConstants const KEY_MODEL = 'model'; const KEY_INIT_STATEMENTS = 'initStatements'; const KEY_ACTIVE = 'active'; - const KEY_DRIVER_OPTIONS = 'driver_options'; - /**#@-*/ - - /**#@+ - * Array keys for database driver options configurations - */ - const KEY_MYSQL_SSL_KEY = \PDO::MYSQL_ATTR_SSL_KEY; - const KEY_MYSQL_SSL_CERT = \PDO::MYSQL_ATTR_SSL_CERT; - const KEY_MYSQL_SSL_CA = \PDO::MYSQL_ATTR_SSL_CA; - /** - * Constant \PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT cannot be used as it was introduced in PHP 7.1.4 - * and Magento 2 is currently supporting PHP 7.1.3. - */ - const KEY_MYSQL_SSL_VERIFY = 1014; /**#@-*/ /** diff --git a/setup/src/Magento/Setup/Controller/DatabaseCheck.php b/setup/src/Magento/Setup/Controller/DatabaseCheck.php index 4b88a8732d2c7..4511abccaf09b 100644 --- a/setup/src/Magento/Setup/Controller/DatabaseCheck.php +++ b/setup/src/Magento/Setup/Controller/DatabaseCheck.php @@ -5,7 +5,6 @@ */ namespace Magento\Setup\Controller; -use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Setup\Validator\DbValidator; use Zend\Json\Json; use Zend\Mvc\Controller\AbstractActionController; @@ -41,25 +40,7 @@ public function indexAction() try { $params = Json::decode($this->getRequest()->getContent(), Json::TYPE_ARRAY); $password = isset($params['password']) ? $params['password'] : ''; - $driverOptions = []; - if ($this->isDriverOptionsGiven($params)) { - if (empty($params['driverOptionsSslVerify'])) { - $params['driverOptionsSslVerify'] = 0; - } - $driverOptions = [ - ConfigOptionsListConstants::KEY_MYSQL_SSL_KEY => $params['driverOptionsSslKey'], - ConfigOptionsListConstants::KEY_MYSQL_SSL_CERT => $params['driverOptionsSslCert'], - ConfigOptionsListConstants::KEY_MYSQL_SSL_CA => $params['driverOptionsSslCa'], - ConfigOptionsListConstants::KEY_MYSQL_SSL_VERIFY => (int) $params['driverOptionsSslVerify'], - ]; - } - $this->dbValidator->checkDatabaseConnectionWithDriverOptions( - $params['name'], - $params['host'], - $params['user'], - $password, - $driverOptions - ); + $this->dbValidator->checkDatabaseConnection($params['name'], $params['host'], $params['user'], $password); $tablePrefix = isset($params['tablePrefix']) ? $params['tablePrefix'] : ''; $this->dbValidator->checkDatabaseTablePrefix($tablePrefix); return new JsonModel(['success' => true]); @@ -67,19 +48,4 @@ public function indexAction() return new JsonModel(['success' => false, 'error' => $e->getMessage()]); } } - - /** - * Is Driver Options Given - * - * @param array $params - * @return bool - */ - private function isDriverOptionsGiven($params) - { - return !( - empty($params['driverOptionsSslKey']) || - empty($params['driverOptionsSslCert']) || - empty($params['driverOptionsSslCa']) - ); - } } diff --git a/setup/src/Magento/Setup/Model/ConfigGenerator.php b/setup/src/Magento/Setup/Model/ConfigGenerator.php index 88421797e59df..80a20ff2e80f3 100644 --- a/setup/src/Magento/Setup/Model/ConfigGenerator.php +++ b/setup/src/Magento/Setup/Model/ConfigGenerator.php @@ -14,7 +14,6 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Framework\App\State; use Magento\Framework\Math\Random; -use Magento\Setup\Model\ConfigOptionsList\DriverOptions; /** * Creates deployment config data based on user input array @@ -63,11 +62,6 @@ class ConfigGenerator */ private $cryptKeyGenerator; - /** - * @var DriverOptions - */ - private $driverOptions; - /** * Constructor * @@ -75,20 +69,17 @@ class ConfigGenerator * @param DeploymentConfig $deploymentConfig * @param ConfigDataFactory|null $configDataFactory * @param CryptKeyGeneratorInterface|null $cryptKeyGenerator - * @param DriverOptions|null $driverOptions */ public function __construct( Random $random, DeploymentConfig $deploymentConfig, ConfigDataFactory $configDataFactory = null, - CryptKeyGeneratorInterface $cryptKeyGenerator = null, - DriverOptions $driverOptions = null + CryptKeyGeneratorInterface $cryptKeyGenerator = null ) { $this->random = $random; $this->deploymentConfig = $deploymentConfig; $this->configDataFactory = $configDataFactory ?? ObjectManager::getInstance()->get(ConfigDataFactory::class); $this->cryptKeyGenerator = $cryptKeyGenerator ?? ObjectManager::getInstance()->get(CryptKeyGenerator::class); - $this->driverOptions = $driverOptions ?? ObjectManager::getInstance()->get(DriverOptions::class); } /** @@ -190,9 +181,6 @@ public function createDbConfig(array $data) $configData->set($dbConnectionPrefix . ConfigOptionsListConstants::KEY_ACTIVE, '1'); } - $driverOptions = $this->driverOptions->getDriverOptions($data); - $configData->set($dbConnectionPrefix . ConfigOptionsListConstants::KEY_DRIVER_OPTIONS, $driverOptions); - return $configData; } diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index 313e08d6c92ca..4568c70403772 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php @@ -11,9 +11,7 @@ use Magento\Framework\Encryption\KeyValidator; use Magento\Framework\Setup\ConfigOptionsListInterface; use Magento\Framework\Setup\Option\FlagConfigOption; -use Magento\Framework\Setup\Option\SelectConfigOption; use Magento\Framework\Setup\Option\TextConfigOption; -use Magento\Setup\Model\ConfigOptionsList\DriverOptions; use Magento\Setup\Validator\DbValidator; /** @@ -55,24 +53,17 @@ class ConfigOptionsList implements ConfigOptionsListInterface \Magento\Setup\Model\ConfigOptionsList\Lock::class, ]; - /** - * @var DriverOptions - */ - private $driverOptions; - /** * Constructor * * @param ConfigGenerator $configGenerator * @param DbValidator $dbValidator * @param KeyValidator|null $encryptionKeyValidator - * @param DriverOptions|null $driverOptions */ public function __construct( ConfigGenerator $configGenerator, DbValidator $dbValidator, - KeyValidator $encryptionKeyValidator = null, - DriverOptions $driverOptions = null + KeyValidator $encryptionKeyValidator = null ) { $this->configGenerator = $configGenerator; $this->dbValidator = $dbValidator; @@ -81,7 +72,6 @@ public function __construct( $this->configOptionsCollection[] = $objectManager->get($className); } $this->encryptionKeyValidator = $encryptionKeyValidator ?: $objectManager->get(KeyValidator::class); - $this->driverOptions = $driverOptions ?? $objectManager->get(DriverOptions::class); } /** @@ -172,36 +162,6 @@ public function getOptions() ConfigOptionsListConstants::CONFIG_PATH_CACHE_HOSTS, 'http Cache hosts' ), - new TextConfigOption( - ConfigOptionsListConstants::INPUT_KEY_DB_SSL_KEY, - TextConfigOption::FRONTEND_WIZARD_TEXT, - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . - '/' . ConfigOptionsListConstants::KEY_MYSQL_SSL_KEY, - 'Full path of client key file in order to establish db connection through SSL', - null - ), - new TextConfigOption( - ConfigOptionsListConstants::INPUT_KEY_DB_SSL_CERT, - TextConfigOption::FRONTEND_WIZARD_TEXT, - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . - '/' . ConfigOptionsListConstants::KEY_MYSQL_SSL_CERT, - 'Full path of client certificate file in order to establish db connection through SSL', - null - ), - new TextConfigOption( - ConfigOptionsListConstants::INPUT_KEY_DB_SSL_CA, - TextConfigOption::FRONTEND_WIZARD_TEXT, - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . - '/' . ConfigOptionsListConstants::KEY_MYSQL_SSL_CA, - 'Full path of server certificate file in order to establish db connection through SSL', - null - ), - new FlagConfigOption( - ConfigOptionsListConstants::INPUT_KEY_DB_SSL_VERIFY, - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . - '/' . ConfigOptionsListConstants::KEY_MYSQL_SSL_VERIFY, - 'Verify server certification' - ), ]; foreach ($this->configOptionsCollection as $configOptionsList) { @@ -387,14 +347,12 @@ private function validateDbSettings(array $options, DeploymentConfig $deployment ) { try { $options = $this->getDbSettings($options, $deploymentConfig); - $driverOptions = $this->driverOptions->getDriverOptions($options); - $this->dbValidator->checkDatabaseConnectionWithDriverOptions( + $this->dbValidator->checkDatabaseConnection( $options[ConfigOptionsListConstants::INPUT_KEY_DB_NAME], $options[ConfigOptionsListConstants::INPUT_KEY_DB_HOST], $options[ConfigOptionsListConstants::INPUT_KEY_DB_USER], - $options[ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD], - $driverOptions + $options[ConfigOptionsListConstants::INPUT_KEY_DB_PASSWORD] ); } catch (\Exception $exception) { $errors[] = $exception->getMessage(); diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php b/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php deleted file mode 100644 index 5910899354911..0000000000000 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList/DriverOptions.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Setup\Model\ConfigOptionsList; - -use Magento\Framework\Config\ConfigOptionsListConstants; - -/** - * MySql driver options - */ -class DriverOptions -{ - /** - * Get driver options. - * - * @param array $options - * @return array - */ - public function getDriverOptions(array $options): array - { - $driverOptionKeys = [ - ConfigOptionsListConstants::KEY_MYSQL_SSL_KEY => ConfigOptionsListConstants::INPUT_KEY_DB_SSL_KEY, - ConfigOptionsListConstants::KEY_MYSQL_SSL_CERT => ConfigOptionsListConstants::INPUT_KEY_DB_SSL_CERT, - ConfigOptionsListConstants::KEY_MYSQL_SSL_CA => ConfigOptionsListConstants::INPUT_KEY_DB_SSL_CA, - ]; - $booleanDriverOptionKeys = [ - ConfigOptionsListConstants::KEY_MYSQL_SSL_VERIFY => ConfigOptionsListConstants::INPUT_KEY_DB_SSL_VERIFY, - ]; - $driverOptions = []; - foreach ($driverOptionKeys as $configKey => $driverOptionKey) { - if ($this->optionExists($options, $driverOptionKey)) { - $driverOptions[$configKey] = $options[$driverOptionKey]; - } - } - foreach ($booleanDriverOptionKeys as $configKey => $driverOptionKey) { - $driverOptions[$configKey] = $this->booleanValue($options, $driverOptionKey); - } - - return $driverOptions; - } - - /** - * Check if provided option exists. - * - * @param array $options - * @param string $driverOptionKey - * @return bool - */ - private function optionExists(array $options, string $driverOptionKey): bool - { - return isset($options[$driverOptionKey]) - && ($options[$driverOptionKey] === false || !empty($options[$driverOptionKey])); - } - - /** - * Transforms checkbox flag value into boolean. - * - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#value - * - * @param array $options - * @param string $driverOptionKey - * @return bool - */ - private function booleanValue(array $options, string $driverOptionKey): bool - { - return isset($options[$driverOptionKey]) && (bool)$options[$driverOptionKey]; - } -} diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index f80a35937d5dc..2510817c2e010 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -1371,32 +1371,7 @@ private function deleteDeploymentConfig() */ private function assertDbAccessible() { - $driverOptionKeys = [ - ConfigOptionsListConstants::KEY_MYSQL_SSL_KEY => - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . '/' . - ConfigOptionsListConstants::KEY_MYSQL_SSL_KEY, - - ConfigOptionsListConstants::KEY_MYSQL_SSL_CERT => - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . '/' . - ConfigOptionsListConstants::KEY_MYSQL_SSL_CERT, - - ConfigOptionsListConstants::KEY_MYSQL_SSL_CA => - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . '/' . - ConfigOptionsListConstants::KEY_MYSQL_SSL_CA, - - ConfigOptionsListConstants::KEY_MYSQL_SSL_VERIFY => - ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT_DRIVER_OPTIONS . '/' . - ConfigOptionsListConstants::KEY_MYSQL_SSL_VERIFY - ]; - $driverOptions = []; - foreach ($driverOptionKeys as $driverOptionKey => $driverOptionConfig) { - $config = $this->deploymentConfig->get($driverOptionConfig); - if ($config !== null) { - $driverOptions[$driverOptionKey] = $config; - } - } - - $this->dbValidator->checkDatabaseConnectionWithDriverOptions( + $this->dbValidator->checkDatabaseConnection( $this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . '/' . ConfigOptionsListConstants::KEY_NAME @@ -1412,8 +1387,7 @@ private function assertDbAccessible() $this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . '/' . ConfigOptionsListConstants::KEY_PASSWORD - ), - $driverOptions + ) ); $prefix = $this->deploymentConfig->get( ConfigOptionsListConstants::CONFIG_PATH_DB_CONNECTION_DEFAULT . diff --git a/setup/src/Magento/Setup/Model/RequestDataConverter.php b/setup/src/Magento/Setup/Model/RequestDataConverter.php index 4964dbe596d85..0ed5dc669c95f 100644 --- a/setup/src/Magento/Setup/Model/RequestDataConverter.php +++ b/setup/src/Magento/Setup/Model/RequestDataConverter.php @@ -51,14 +51,6 @@ private function convertDeploymentConfigForm(array $source) isset($source['db']['tablePrefix']) ? $source['db']['tablePrefix'] : ''; $result[BackendConfigOptionsList::INPUT_KEY_BACKEND_FRONTNAME] = isset($source['config']['address']['admin']) ? $source['config']['address']['admin'] : ''; - $result[SetupConfigOptionsList::INPUT_KEY_DB_SSL_KEY] = isset($source['db']['driverOptionsSslKey']) - ? $source['db']['driverOptionsSslKey'] : ''; - $result[SetupConfigOptionsList::INPUT_KEY_DB_SSL_CERT] = isset($source['db']['driverOptionsSslCert']) - ? $source['db']['driverOptionsSslCert'] : ''; - $result[SetupConfigOptionsList::INPUT_KEY_DB_SSL_CA] = isset($source['db']['driverOptionsSslCa']) - ? $source['db']['driverOptionsSslCa'] : ''; - $result[SetupConfigOptionsList::INPUT_KEY_DB_SSL_VERIFY] = isset($source['db']['driverOptionsSslVerify']) - ? $source['db']['driverOptionsSslVerify'] : ''; $result[SetupConfigOptionsList::INPUT_KEY_ENCRYPTION_KEY] = isset($source['config']['encrypt']['key']) ? $source['config']['encrypt']['key'] : null; $result[SetupConfigOptionsList::INPUT_KEY_SESSION_SAVE] = isset($source['config']['sessionSave']['type']) diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php index 63a92bd4a1982..a388c72fc834b 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php @@ -11,7 +11,6 @@ use Magento\Framework\Config\Data\ConfigData; use Magento\Framework\Config\Data\ConfigDataFactory; use Magento\Setup\Model\ConfigGenerator; -use Magento\Setup\Model\ConfigOptionsList\DriverOptions; /** * Test for Magento\Setup\Model\ConfigGenerator class. @@ -33,11 +32,6 @@ class ConfigGeneratorTest extends \PHPUnit\Framework\TestCase */ private $configDataMock; - /** - * @var DriverOptions - */ - private $driverOptionsMock; - public function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -59,17 +53,11 @@ public function setUp() $configDataFactoryMock->method('create') ->willReturn($this->configDataMock); - $this->driverOptionsMock = $this->getMockBuilder(DriverOptions::class) - ->disableOriginalConstructor() - ->setMethods(['getDriverOptions']) - ->getMock(); - $this->model = $objectManager->getObject( ConfigGenerator::class, [ 'deploymentConfig' => $this->deploymentConfigMock, 'configDataFactory' => $configDataFactoryMock, - 'driverOptions' => $this->driverOptionsMock, ] ); } diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php index 435f03f9de60b..a3ade400fd363 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigOptionsListTest.php @@ -37,29 +37,12 @@ class ConfigOptionsListTest extends \PHPUnit\Framework\TestCase */ private $dbValidator; - /** - * @var \Magento\Framework\Encryption\KeyValidator|\PHPUnit_Framework_MockObject_MockObject - */ - private $encryptionKeyValidator; - - /** - * @var ConfigOptionsList\DriverOptions - */ - private $driverOptionsMock; - protected function setUp() { $this->generator = $this->createMock(\Magento\Setup\Model\ConfigGenerator::class); $this->deploymentConfig = $this->createMock(\Magento\Framework\App\DeploymentConfig::class); $this->dbValidator = $this->createMock(\Magento\Setup\Validator\DbValidator::class); - $this->encryptionKeyValidator = $this->createMock(\Magento\Framework\Encryption\KeyValidator::class); - $this->driverOptionsMock = $this->createMock(ConfigOptionsList\DriverOptions::class); - $this->object = new ConfigOptionsList( - $this->generator, - $this->dbValidator, - $this->encryptionKeyValidator, - $this->driverOptionsMock - ); + $this->object = new ConfigOptionsList($this->generator, $this->dbValidator); } public function testGetOptions() @@ -181,7 +164,7 @@ private function prepareValidationMocks() $this->dbValidator->expects($this->once())->method('checkDatabaseTablePrefix')->willReturn($configDataMock); $this->dbValidator ->expects($this->once()) - ->method('checkDatabaseConnectionWithDriverOptions') + ->method('checkDatabaseConnection') ->willReturn($configDataMock); } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/ConfigGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Module/ConfigGeneratorTest.php index ea4261b271582..d4cdee4b84bab 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/ConfigGeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/ConfigGeneratorTest.php @@ -15,7 +15,6 @@ use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Setup\Model\CryptKeyGenerator; use PHPUnit\Framework\TestCase; -use Magento\Setup\Model\ConfigOptionsList\DriverOptions; /** * Test for Magento\Setup\Model\ConfigGenerator class. @@ -48,17 +47,11 @@ protected function setUp() $configDataFactoryMock = (new ObjectManager($this)) ->getObject(ConfigDataFactory::class, ['objectManager' => $objectManagerMock]); - $driverOptions = $this->getMockBuilder(DriverOptions::class) - ->disableOriginalConstructor() - ->setMethods(['getDriverOptions']) - ->getMock(); - $this->configGeneratorObject = new ConfigGenerator( $randomMock, $deployConfig, $configDataFactoryMock, - $cryptKeyGenerator, - $driverOptions + $cryptKeyGenerator ); } diff --git a/setup/src/Magento/Setup/Validator/AdminCredentialsValidator.php b/setup/src/Magento/Setup/Validator/AdminCredentialsValidator.php index 23e41df900d4f..d8380bca54f7b 100644 --- a/setup/src/Magento/Setup/Validator/AdminCredentialsValidator.php +++ b/setup/src/Magento/Setup/Validator/AdminCredentialsValidator.php @@ -5,10 +5,8 @@ */ namespace Magento\Setup\Validator; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Config\ConfigOptionsListConstants as ConfigOption; use Magento\Setup\Model\AdminAccount; -use Magento\Setup\Model\Installer; use Magento\Setup\Model\ConfigOptionsList\DriverOptions; /** @@ -31,29 +29,21 @@ class AdminCredentialsValidator */ private $setupFactory; - /** - * @var DriverOptions - */ - private $driverOptions; - /** * Initialize dependencies. * * @param \Magento\Setup\Model\AdminAccountFactory $adminAccountFactory * @param \Magento\Setup\Module\ConnectionFactory $connectionFactory * @param \Magento\Setup\Module\SetupFactory $setupFactory - * @param DriverOptions|null $driverOptions */ public function __construct( \Magento\Setup\Model\AdminAccountFactory $adminAccountFactory, \Magento\Setup\Module\ConnectionFactory $connectionFactory, - \Magento\Setup\Module\SetupFactory $setupFactory, - DriverOptions $driverOptions = null + \Magento\Setup\Module\SetupFactory $setupFactory ) { $this->connectionFactory = $connectionFactory; $this->adminAccountFactory = $adminAccountFactory; $this->setupFactory = $setupFactory; - $this->driverOptions = $driverOptions ?? ObjectManager::getInstance()->get(DriverOptions::class); } /** @@ -65,8 +55,6 @@ public function __construct( */ public function validate(array $data) { - $driverOptions = $this->driverOptions->getDriverOptions($data); - $dbConnection = $this->connectionFactory->create( [ ConfigOption::KEY_NAME => $data[ConfigOption::INPUT_KEY_DB_NAME], @@ -74,7 +62,6 @@ public function validate(array $data) ConfigOption::KEY_USER => $data[ConfigOption::INPUT_KEY_DB_USER], ConfigOption::KEY_PASSWORD => $data[ConfigOption::INPUT_KEY_DB_PASSWORD], ConfigOption::KEY_PREFIX => $data[ConfigOption::INPUT_KEY_DB_PREFIX], - ConfigOption::KEY_DRIVER_OPTIONS => $driverOptions ] ); diff --git a/setup/src/Magento/Setup/Validator/DbValidator.php b/setup/src/Magento/Setup/Validator/DbValidator.php index 01ed537f887cd..075e48140e150 100644 --- a/setup/src/Magento/Setup/Validator/DbValidator.php +++ b/setup/src/Magento/Setup/Validator/DbValidator.php @@ -78,27 +78,6 @@ public function checkDatabaseTablePrefix($prefix) */ public function checkDatabaseConnection($dbName, $dbHost, $dbUser, $dbPass = '') { - return $this->checkDatabaseConnectionWithDriverOptions($dbName, $dbHost, $dbUser, $dbPass, []); - } - - /** - * Checks Database Connection with Driver Options - * - * @param string $dbName - * @param string $dbHost - * @param string $dbUser - * @param string $dbPass - * @param array $driverOptions - * @return bool - * @throws \Magento\Setup\Exception - */ - public function checkDatabaseConnectionWithDriverOptions( - $dbName, - $dbHost, - $dbUser, - $dbPass = '', - $driverOptions = [] - ) { // establish connection to information_schema view to retrieve information about user and table privileges $connection = $this->connectionFactory->create( [ @@ -107,7 +86,6 @@ public function checkDatabaseConnectionWithDriverOptions( ConfigOptionsListConstants::KEY_USER => $dbUser, ConfigOptionsListConstants::KEY_PASSWORD => $dbPass, ConfigOptionsListConstants::KEY_ACTIVE => true, - ConfigOptionsListConstants::KEY_DRIVER_OPTIONS => $driverOptions, ] ); diff --git a/setup/view/magento/setup/add-database.phtml b/setup/view/magento/setup/add-database.phtml index c858d92cfef55..f36024c112823 100644 --- a/setup/view/magento/setup/add-database.phtml +++ b/setup/view/magento/setup/add-database.phtml @@ -475,94 +475,6 @@ */ ?> - -<div class="row form-row"> - <div class="col-m-3"> - <label class="form-label" for="dbDriverOptionsSslKey"> - Driver Options - SSL Key - </label> - </div> - <div class="col-m-4"> - <input - id="dbDriverOptionsSslKey" - class="form-el-input" - tooltip-placement="right" - tooltip="File that contains X509 key" - tooltip-trigger="focus" - tooltip-append-to-body="true" - type="text" - name="dbDriverOptionsSslKey" - ng-model="db.driverOptionsSslKey" - placeholder="/path/to/client-key.pem" - > - </div> -</div> - -<div class="row form-row"> - <div class="col-m-3"> - <label class="form-label" for="dbDriverOptionsSslCert"> - Driver Options - SSL Certificate - </label> - </div> - <div class="col-m-4"> - <input - id="dbDriverOptionsSslCert" - class="form-el-input" - tooltip-placement="right" - tooltip="File that contains X509 certificate" - tooltip-trigger="focus" - tooltip-append-to-body="true" - type="text" - name="dbDriverOptionsSslCert" - ng-model="db.driverOptionsSslCert" - placeholder="/path/to/client-cert.pem" - > - </div> -</div> - -<div class="row form-row"> - <div class="col-m-3"> - <label class="form-label" for="dbDriverOptionsSslCa"> - Driver Options - SSL Certificate Authorities - </label> - </div> - <div class="col-m-4"> - <input - id="dbDriverOptionsSslCa" - class="form-el-input" - tooltip-placement="right" - tooltip="File that contains list of trusted SSL Certificate Authorities" - tooltip-trigger="focus" - tooltip-append-to-body="true" - type="text" - name="dbDriverOptionsSslCa" - ng-model="db.driverOptionsSslCa" - placeholder="/path/to/ca.pem" - > - </div> -</div> - -<div class="row form-row"> - <div class="col-m-3"> - <label class="form-label"> - Driver Options - SSL Verification - </label> - </div> - <div class="col-m-4"> - <div class="form-row"> - <input - id="dbDriverOptionsSslVerify" - class="form-el-checkbox" - type="checkbox" - ng-model="db.driverOptionsSslVerify" - ng-checked="db.driverOptionsSslVerify" - > - <label class="form-label" for="dbDriverOptionsSslVerify"> - Perform verification against the server CA certificate and against the server host name in its certificate - </label> - </div> - </div> -</div> </fieldset> </form> From ee52bbe016d6afe072761ef18d5110c7513c26ce Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 13 Aug 2019 09:50:11 +0300 Subject: [PATCH 0357/2437] MC-19084: Browser console error after applying gift card account during checkout --- .../Checkout/view/frontend/web/js/sidebar.js | 3 +- .../Ui/view/frontend/web/js/view/messages.js | 3 +- .../css/source/module/checkout/_checkout.less | 39 ++++++++++--------- lib/web/jquery/ui-modules/effect-fade.js | 3 -- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js index e67b04e6104cc..472f7588d5b6c 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js @@ -13,7 +13,8 @@ define([ 'jquery-ui-modules/widget', 'mage/decorate', 'mage/collapsible', - 'mage/cookies' + 'mage/cookies', + 'jquery-ui-modules/effect-fade' ], function ($, authenticationPopup, customerData, alert, confirm, _) { 'use strict'; diff --git a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js index 57a590c87179a..b2fb3f216199b 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js @@ -10,7 +10,8 @@ define([ 'ko', 'jquery', 'uiComponent', - '../model/messageList' + '../model/messageList', + 'jquery-ui-modules/effect-blind' ], function (ko, $, Component, globalMessages) { 'use strict'; diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less index a2daf0da247d1..ce3f45c990b96 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less @@ -78,27 +78,28 @@ } .abs-discount-code { - .actions-toolbar { - display: table-cell; - vertical-align: top; - width: 1%; - - .primary { - float: left; - .action { - &:extend(.abs-revert-to-action-secondary all); - border-bottom-left-radius: 0; - border-top-left-radius: 0; - margin: 0 0 0 -2px; - white-space: nowrap; - width: auto; - } - } - } - .form-discount { + .form-discount { display: table; width: 100%; - + + .actions-toolbar { + display: table-cell; + vertical-align: top; + width: 1%; + + .primary { + float: left; + .action { + &:extend(.abs-revert-to-action-secondary all); + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin: 0 0 0 -2px; + white-space: nowrap; + width: auto; + } + } + } + > .field { > .label { display: none; diff --git a/lib/web/jquery/ui-modules/effect-fade.js b/lib/web/jquery/ui-modules/effect-fade.js index ba289cbeb0180..46db40fc8a42e 100644 --- a/lib/web/jquery/ui-modules/effect-fade.js +++ b/lib/web/jquery/ui-modules/effect-fade.js @@ -7,9 +7,6 @@ * http://jquery.org/license * * http://api.jqueryui.com/fade-effect/ - * - * Depends: - * jquery.ui.effect.js */ define([ From c5574b8eb9c7f30f59a5e58d85462deb71279e2a Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 13 Aug 2019 10:20:42 +0300 Subject: [PATCH 0358/2437] MC-19075: Html text are visible on frontend --- .../Checkout/view/frontend/templates/onepage/review/item.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml index 8e83526b4ceaa..b3b94cfae18dc 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml @@ -28,7 +28,7 @@ $taxDataHelper = $this->helper(Magento\Tax\Helper\Data::class); <dt><?= $block->escapeHtml($_option['label']) ?></dt> <dd> <?php if (isset($_formatedOptionValue['full_view'])) :?> - <?= $block->escapeHtml($_formatedOptionValue['full_view']) ?> + <?= /* @noEscape */ $_formatedOptionValue['full_view'] ?> <?php else :?> <?= /* @noEscape */ $_formatedOptionValue['value'] ?> <?php endif; ?> From 76b668bc8a5b0a83bb3414660eaa793d969cf8ff Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 13 Aug 2019 12:04:13 +0300 Subject: [PATCH 0359/2437] MC-19023: PayPal Express Checkout Payflow Edition don't work (Internal Error) --- .../web/js/action/select-payment-method.js | 9 +- .../set-payment-information-extended.js | 25 +++++- .../set-payment-information-extended.test.js | 86 +++++++++++++++++++ 3 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js index 6bda10fceb7b9..34f1700749794 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/select-payment-method.js @@ -12,10 +12,11 @@ define([ 'use strict'; return function (paymentMethod) { - paymentMethod.__disableTmpl = { - title: true - }; - + if (paymentMethod) { + paymentMethod.__disableTmpl = { + title: true + }; + } quote.paymentMethod(paymentMethod); }; }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js b/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js index 263d5747f2842..9de8a93905c99 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/set-payment-information-extended.js @@ -13,16 +13,33 @@ define([ 'Magento_Checkout/js/model/error-processor', 'Magento_Customer/js/model/customer', 'Magento_Checkout/js/action/get-totals', - 'Magento_Checkout/js/model/full-screen-loader' -], function (quote, urlBuilder, storage, errorProcessor, customer, getTotalsAction, fullScreenLoader) { + 'Magento_Checkout/js/model/full-screen-loader', + 'underscore' +], function (quote, urlBuilder, storage, errorProcessor, customer, getTotalsAction, fullScreenLoader, _) { 'use strict'; + /** + * Filter template data. + * + * @param {Object|Array} data + */ + var filterTemplateData = function (data) { + return _.each(data, function (value, key, list) { + if (_.isArray(value) || _.isObject(value)) { + list[key] = filterTemplateData(value); + } + + if (key === '__disableTmpl') { + delete list[key]; + } + }); + }; + return function (messageContainer, paymentData, skipBilling) { var serviceUrl, payload; - delete paymentData.__disableTmpl; - + paymentData = filterTemplateData(paymentData); skipBilling = skipBilling || false; payload = { cartId: quote.getQuoteId(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js new file mode 100644 index 0000000000000..d928ee9f995d2 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js @@ -0,0 +1,86 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'squire', + 'jquery' +], function (Squire, $) { + 'use strict'; + + var injector = new Squire(), + setPaymentInformation, + serviceUrl = 'http://url', + mocks = { + 'Magento_Checkout/js/model/quote': { + getQuoteId: jasmine.createSpy().and.returnValue(1), + billingAddress: jasmine.createSpy().and.returnValue(null) + }, + 'Magento_Checkout/js/model/url-builder': { + createUrl: jasmine.createSpy().and.returnValue(serviceUrl) + }, + 'mage/storage': { + post: function () {} // jscs:ignore jsDoc + }, + 'Magento_Customer/js/model/customer': { + isLoggedIn: jasmine.createSpy().and.returnValue(false) + }, + 'Magento_Checkout/js/model/full-screen-loader': { + startLoader: jasmine.createSpy(), + stopLoader: jasmine.createSpy() + }, + 'Magento_Checkout/js/action/get-totals': jasmine.createSpy('getTotalsAction'), + 'Magento_Checkout/js/model/error-processor': jasmine.createSpy('errorProcessor') + }; + + beforeEach(function (done) { + injector.mock(mocks); + injector.require( + ['Magento_Checkout/js/action/set-payment-information-extended'], + function (action) { + setPaymentInformation = action; + done(); + }); + }); + + afterEach(function () { + try { + injector.clean(); + injector.remove(); + } catch (e) { + } + }); + + describe('Magento/Checkout/js/action/set-payment-information-extended', function () { + it('Checks that paymentData consist correct data value.', function () { + var messageContainer = jasmine.createSpy('messageContainer'), + deferral = new $.Deferred(), + paymentData = { + method: 'checkmo', + additionalData: null, + __disableTmpl: { + title: true + } + }, + formattedData = { + method: 'checkmo', + additionalData: null + }, + payload = { + cartId: 1, + paymentMethod: formattedData, + billingAddress: null + }; + + spyOn(mocks['mage/storage'], 'post').and.callFake(function () { + return deferral.resolve({}); + }); + + setPaymentInformation(messageContainer, paymentData, false); + expect(mocks['Magento_Checkout/js/model/full-screen-loader'].startLoader).toHaveBeenCalled(); + expect(mocks['mage/storage'].post).toHaveBeenCalledWith(serviceUrl, JSON.stringify(payload)); + expect(mocks['Magento_Checkout/js/model/full-screen-loader'].stopLoader).toHaveBeenCalled(); + }); + }); +}); From 30c3db8364c85f4e39680a1f63717d8b28709a04 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 13 Aug 2019 12:26:00 +0300 Subject: [PATCH 0360/2437] MC-19014: QueueManagementTest caused flakiness during nightly build Integration test runs --- .../Magento/MysqlMq/Model/ResourceModel/Queue.php | 2 +- .../Test/Unit/Model/ResourceModel/QueueTest.php | 2 +- .../Magento/MysqlMq/Model/QueueManagementTest.php | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php b/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php index d50ed851b64a9..2a45eafc63f24 100644 --- a/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php +++ b/app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php @@ -151,7 +151,7 @@ public function getMessages($queueName, $limit = null) 'queue_message_status.status IN (?)', [QueueManagement::MESSAGE_STATUS_NEW, QueueManagement::MESSAGE_STATUS_RETRY_REQUIRED] )->where('queue.name = ?', $queueName) - ->order('queue_message_status.updated_at ASC'); + ->order(['queue_message_status.updated_at ASC', 'queue_message_status.id ASC']); if ($limit) { $select->limit($limit); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php index d3fe09a712945..c8669809b3aa2 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php @@ -206,7 +206,7 @@ public function testGetMessages() ] )->willReturnSelf(); $select->expects($this->once()) - ->method('order')->with('queue_message_status.updated_at ASC')->willReturnSelf(); + ->method('order')->with(['queue_message_status.updated_at ASC', 'queue_message_status.id ASC'])->willReturnSelf(); $select->expects($this->once())->method('limit')->with($limit)->willReturnSelf(); $connection->expects($this->once())->method('fetchAll')->with($select)->willReturn($messages); $this->assertEquals($messages, $this->queue->getMessages($queueName, $limit)); diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php index 56dd77d3da17c..8fbbe800d6735 100644 --- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php @@ -91,13 +91,17 @@ public function testAllFlows() $messages = $this->queueManagement->readMessages('queue2', 1); $message = array_shift($messages); $messageRelationId = $message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]; + $this->queueManagement->pushToQueueForRetry($messageRelationId); - for ($i = 0; $i < 2; $i++) { - $this->assertEquals($i, $message[QueueManagement::MESSAGE_NUMBER_OF_TRIALS]); - $this->queueManagement->pushToQueueForRetry($message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]); + $retryMessage = null; + for ($i = 1; $i <= 3; $i++) { $messages = $this->queueManagement->readMessages('queue2', 1); $message = array_shift($messages); - $this->assertEquals($messageRelationId, $message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]); + if ($message[QueueManagement::MESSAGE_QUEUE_RELATION_ID] == $messageRelationId) { + $retryMessage = $message; + } } + $this->assertNotNull($retryMessage, 'Made retry message not found in queue'); + $this->assertEquals(1, $retryMessage[QueueManagement::MESSAGE_NUMBER_OF_TRIALS]); } } From 7c240d94881c76838141dcfb91fe2f6b2208928f Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 13 Aug 2019 13:58:07 +0300 Subject: [PATCH 0361/2437] MC-19023: PayPal Express Checkout Payflow Edition don't work (Internal Error) --- .../js/action/set-payment-information-extended.test.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js index d928ee9f995d2..66f7731415c05 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/action/set-payment-information-extended.test.js @@ -63,13 +63,12 @@ define([ title: true } }, - formattedData = { - method: 'checkmo', - additionalData: null - }, payload = { cartId: 1, - paymentMethod: formattedData, + paymentMethod: { + method: 'checkmo', + additionalData: null + }, billingAddress: null }; From 9092e5e22c736aca6dff5e0943c214e293ec1c66 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 13 Aug 2019 14:14:38 +0300 Subject: [PATCH 0362/2437] MC-19014: QueueManagementTest caused flakiness during nightly build Integration test runs --- .../MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php index c8669809b3aa2..e3c6e6d9aee2a 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Model/ResourceModel/QueueTest.php @@ -206,7 +206,9 @@ public function testGetMessages() ] )->willReturnSelf(); $select->expects($this->once()) - ->method('order')->with(['queue_message_status.updated_at ASC', 'queue_message_status.id ASC'])->willReturnSelf(); + ->method('order') + ->with(['queue_message_status.updated_at ASC', 'queue_message_status.id ASC']) + ->willReturnSelf(); $select->expects($this->once())->method('limit')->with($limit)->willReturnSelf(); $connection->expects($this->once())->method('fetchAll')->with($select)->willReturn($messages); $this->assertEquals($messages, $this->queue->getMessages($queueName, $limit)); From 8e3edf17c1e352bbf6320e68c06c9a0a402e2e9e Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 13 Aug 2019 15:31:19 +0300 Subject: [PATCH 0363/2437] MC-19132: Child product image of configurable product not displayed --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index d52e70ef56a11..5ef6b1802b0a5 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -297,7 +297,7 @@ private function _fillBackgroundColor(&$imageResourceTo) return $transparentColor; } } catch (\Exception $e) { - throw new \DomainException('Failed to fill image.'); + // fallback to default background color } } list($r, $g, $b) = $this->_backgroundColor; From fe432468b13d1f85c301b60ea890abb4bead5c34 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Tue, 13 Aug 2019 08:13:58 -0500 Subject: [PATCH 0364/2437] MC-18977: Revert change of ENGCOM-3260 - fix static failures --- app/code/Magento/Tax/Model/App/Action/ContextPlugin.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php index 992f206279b26..913fa4c46f0ae 100644 --- a/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php +++ b/app/code/Magento/Tax/Model/App/Action/ContextPlugin.php @@ -52,13 +52,11 @@ class ContextPlugin * @param \Magento\Tax\Helper\Data $taxHelper * @param \Magento\Framework\Module\Manager $moduleManager * @param \Magento\PageCache\Model\Config $cacheConfig - * - * phpcs:ignore Magento2.Classes.DiscouragedDependencies */ public function __construct( \Magento\Customer\Model\Session $customerSession, \Magento\Framework\App\Http\Context $httpContext, - \Magento\Tax\Model\Calculation\Proxy $calculation, + \Magento\Tax\Model\Calculation\Proxy $calculation, //phpcs:ignore Magento2.Classes.DiscouragedDependencies \Magento\Tax\Helper\Data $taxHelper, \Magento\Framework\Module\Manager $moduleManager, \Magento\PageCache\Model\Config $cacheConfig From 2c5fd23470c42e9c92e643bc9025cdc6de0e1a56 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 13 Aug 2019 17:00:43 +0300 Subject: [PATCH 0365/2437] MC-19132: Child product image of configurable product not displayed --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index 5ef6b1802b0a5..f8cad0e8821ef 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -296,6 +296,7 @@ private function _fillBackgroundColor(&$imageResourceTo) imagecolortransparent($imageResourceTo, $transparentColor); return $transparentColor; } + // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (\Exception $e) { // fallback to default background color } From bc97aedd25622f040ccba347b5dfc3efdacc3827 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Tue, 13 Aug 2019 17:51:54 +0300 Subject: [PATCH 0366/2437] MC-19080: Incorrect behavior after shipping methods disabled --- app/code/Magento/Shipping/Model/Shipping.php | 26 +++-- .../Shipping/Test/Unit/Model/ShippingTest.php | 33 ++++++- .../Magento/Shipping/Model/ShippingTest.php | 99 ++++++++++++++----- 3 files changed, 125 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Shipping/Model/Shipping.php b/app/code/Magento/Shipping/Model/Shipping.php index 5470f9a96775b..77e6f3c5219fb 100644 --- a/app/code/Magento/Shipping/Model/Shipping.php +++ b/app/code/Magento/Shipping/Model/Shipping.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Shipping\Model; use Magento\Framework\App\ObjectManager; @@ -272,7 +273,9 @@ public function collectRates(\Magento\Quote\Model\Quote\Address\RateRequest $req */ private function prepareCarrier(string $carrierCode, RateRequest $request): AbstractCarrier { - $carrier = $this->_carrierFactory->create($carrierCode, $request->getStoreId()); + $carrier = $this->isActive($carrierCode) + ? $this->_carrierFactory->create($carrierCode, $request->getStoreId()) + : null; if (!$carrier) { throw new \RuntimeException('Failed to initialize carrier'); } @@ -425,13 +428,10 @@ public function composePackagesForCarrier($carrier, $request) if (!empty($decimalItems)) { foreach ($decimalItems as $decimalItem) { - $fullItems = array_merge( - $fullItems, - array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']) - ); + $fullItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']); } } else { - $fullItems = array_merge($fullItems, array_fill(0, $qty, $itemWeight)); + $fullItems[] = array_fill(0, $qty, $itemWeight); } } sort($fullItems); @@ -532,4 +532,18 @@ public function setCarrierAvailabilityConfigField($code = 'active') $this->_availabilityConfigField = $code; return $this; } + + /** + * Checks availability of carrier. + * + * @param string $carrierCode + * @return bool|null + */ + private function isActive(string $carrierCode) + { + return $this->_scopeConfig->isSetFlag( + 'carriers/' . $carrierCode . '/' . $this->_availabilityConfigField, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ); + } } diff --git a/app/code/Magento/Shipping/Test/Unit/Model/ShippingTest.php b/app/code/Magento/Shipping/Test/Unit/Model/ShippingTest.php index 1df41aeba076b..e5723c38ac568 100644 --- a/app/code/Magento/Shipping/Test/Unit/Model/ShippingTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Model/ShippingTest.php @@ -3,12 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Shipping\Test\Unit\Model; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type as ProductType; use Magento\CatalogInventory\Model\Stock\Item as StockItem; use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Shipping\Model\Carrier\AbstractCarrierInterface; use Magento\Shipping\Model\CarrierFactory; @@ -19,12 +21,14 @@ use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * @see Shipping + * Unit tests for \Magento\Shipping\Model\Shipping class. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShippingTest extends \PHPUnit\Framework\TestCase { /** - * Test identification number of product + * Test identification number of product. * * @var int */ @@ -50,22 +54,34 @@ class ShippingTest extends \PHPUnit\Framework\TestCase */ private $carrier; + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; + + /** + * @inheritdoc + */ protected function setUp() { $this->stockRegistry = $this->createMock(StockRegistry::class); $this->stockItemData = $this->createMock(StockItem::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); $this->shipping = (new ObjectManagerHelper($this))->getObject( Shipping::class, [ 'stockRegistry' => $this->stockRegistry, 'carrierFactory' => $this->getCarrierFactory(), + 'scopeConfig' => $this->scopeConfig, ] ); } /** + * Compose Packages For Carrier. * + * @return void */ public function testComposePackages() { @@ -125,14 +141,25 @@ function ($key) { /** * Active flag should be set before collecting carrier rates. + * + * @return void */ public function testCollectCarrierRatesSetActiveFlag() { + $carrierCode = 'carrier'; + $scopeStore = 'store'; + $this->scopeConfig->expects($this->once()) + ->method('isSetFlag') + ->with( + 'carriers/' . $carrierCode . '/active', + $scopeStore + ) + ->willReturn(true); $this->carrier->expects($this->atLeastOnce()) ->method('setActiveFlag') ->with('active'); - $this->shipping->collectCarrierRates('carrier', new RateRequest()); + $this->shipping->collectCarrierRates($carrierCode, new RateRequest()); } /** diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php index 760b24db5ef30..8c5dfb0d0ff5e 100644 --- a/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php +++ b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php @@ -3,16 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Shipping\Model; use Magento\Framework\DataObject; use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Model\Quote\Address\RateResult\Error; use Magento\Quote\Model\Quote\Address\RateResult\Method; use Magento\Shipping\Model\Rate\Result; use Magento\TestFramework\Helper\Bootstrap; /** - * Contains list of tests for Shipping model + * Contains list of tests for Shipping model. + * @magentoAppIsolation enabled */ class ShippingTest extends \PHPUnit\Framework\TestCase { @@ -42,22 +45,8 @@ protected function setUp() */ public function testCollectRatesByAddress() { - $address = $this->objectManager->create(DataObject::class, [ - 'data' => [ - 'region_id' => 'CA', - 'postcode' => '11111', - 'lastname' => 'John', - 'firstname' => 'Doe', - 'street' => 'Some street', - 'city' => 'Los Angeles', - 'email' => 'john.doe@example.com', - 'telephone' => '11111111', - 'country_id' => 'US', - 'item_qty' => 1 - ] - ]); /** @var Shipping $result */ - $result = $this->model->collectRatesByAddress($address, 'flatrate'); + $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); static::assertInstanceOf(Shipping::class, $result); return $result->getResult(); @@ -68,19 +57,81 @@ public function testCollectRatesByAddress() * @covers \Magento\Shipping\Model\Shipping::collectRatesByAddress * @param Result $result * @depends testCollectRatesByAddress - * @magentoConfigFixture carriers/flatrate/active 1 - * @magentoConfigFixture carriers/flatrate/price 5.00 */ public function testCollectRates(Result $result) { - $rates = $result->getAllRates(); - static::assertNotEmpty($rates); - - /** @var Method $rate */ - $rate = array_pop($rates); - + $rate = $this->getRate($result); static::assertInstanceOf(Method::class, $rate); static::assertEquals('flatrate', $rate->getData('carrier')); static::assertEquals(5, $rate->getData('price')); } + + /** + * @magentoConfigFixture default_store carriers/flatrate/active 1 + * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 + * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK + * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 + */ + public function testShippingMethodIsActiveAndNotApplicable() + { + $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); + $rate = $this->getRate($result->getResult()); + + static::assertEquals('flatrate', $rate->getData('carrier')); + static::assertEquals( + 'This shipping method is not available. To use this shipping method, please contact us.', + $rate->getData('error_message') + ); + } + + /** + * @magentoConfigFixture default_store carriers/flatrate/active 0 + * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 + * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK + * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 + */ + public function testShippingMethodIsNotActiveAndNotApplicable() + { + $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); + $rate = $this->getRate($result->getResult()); + + static::assertNull($rate); + } + + /** + * @return DataObject + */ + private function getAddress(): DataObject + { + $address = $this->objectManager->create( + DataObject::class, + [ + 'data' => [ + 'region_id' => 'CA', + 'postcode' => '11111', + 'lastname' => 'John', + 'firstname' => 'Doe', + 'street' => 'Some street', + 'city' => 'Los Angeles', + 'email' => 'john.doe@example.com', + 'telephone' => '11111111', + 'country_id' => 'US', + 'item_qty' => 1, + ], + ] + ); + + return $address; + } + + /** + * @param Result $result + * @return Method|Error + */ + private function getRate(Result $result) + { + $rates = $result->getAllRates(); + + return array_pop($rates); + } } From eb6d625269699de731b2be64098f83cab6a045ab Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Tue, 13 Aug 2019 09:58:25 -0500 Subject: [PATCH 0367/2437] MC-19061: Revert ticket MC-17003 --- .../adminhtml/templates/order/creditmemo/create/items.phtml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml index 16ab1750efeb6..22e648e2b5bde 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml @@ -139,7 +139,8 @@ require(['jquery'], function(jQuery){ //<![CDATA[ var submitButtons = jQuery('.submit-button'); - +var updateButtons = jQuery('.update-button'); +var fields = jQuery('.qty-input'); function enableButtons(buttons) { buttons.removeClass('disabled').prop('disabled', false); } From a098f4fe398be310a46f4cc106efc951b64cc8e8 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Tue, 13 Aug 2019 11:13:06 -0500 Subject: [PATCH 0368/2437] MC-19115: Admin Analytics tracking should be enabled by default - static fix --- lib/internal/Magento/Framework/App/ProductMetadata.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php index 3683d7ecf61d9..583638826e766 100644 --- a/lib/internal/Magento/Framework/App/ProductMetadata.php +++ b/lib/internal/Magento/Framework/App/ProductMetadata.php @@ -14,6 +14,7 @@ /** * Class ProductMetadata + * * @package Magento\Framework\App */ class ProductMetadata implements ProductMetadataInterface @@ -29,9 +30,9 @@ class ProductMetadata implements ProductMetadataInterface const PRODUCT_NAME = 'Magento'; /** - * Magento version cache prefix + * Magento version cache key */ - const CACHE_PREFIX = 'mage-version'; + const VERSION_CACHE_KEY = 'mage-version'; /** * Product version @@ -76,7 +77,7 @@ public function __construct( */ public function getVersion() { - $versionFromCache = $this->cache->load(self::CACHE_PREFIX); + $versionFromCache = $this->cache->load(self::VERSION_CACHE_KEY); $this->version = $this->version ?: $versionFromCache; if (!$this->version) { if (!($this->version = $this->getSystemPackageVersion())) { @@ -85,7 +86,7 @@ public function getVersion() } else { $this->version = 'UNKNOWN'; } - $this->cache->save($this->version, self::CACHE_PREFIX, [Config::CACHE_TAG]); + $this->cache->save($this->version, self::VERSION_CACHE_KEY, [Config::CACHE_TAG]); } } return $this->version; From 6d7e1f714c455764255f654a1b88707a0243e1d8 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Tue, 13 Aug 2019 11:51:58 -0500 Subject: [PATCH 0369/2437] MC-19161: Revert ENGCOM-2983 --- setup/src/Magento/Setup/Model/ConfigOptionsList.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup/src/Magento/Setup/Model/ConfigOptionsList.php b/setup/src/Magento/Setup/Model/ConfigOptionsList.php index 4568c70403772..c080b3c7fc6a1 100644 --- a/setup/src/Magento/Setup/Model/ConfigOptionsList.php +++ b/setup/src/Magento/Setup/Model/ConfigOptionsList.php @@ -165,6 +165,7 @@ public function getOptions() ]; foreach ($this->configOptionsCollection as $configOptionsList) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $options = array_merge($options, $configOptionsList->getOptions()); } @@ -221,6 +222,7 @@ public function validate(array $options, DeploymentConfig $deploymentConfig) } foreach ($this->configOptionsCollection as $configOptionsList) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $errors = array_merge($errors, $configOptionsList->validate($options, $deploymentConfig)); } From d0659f5d5304a282074741fa9bf4cbd13bf2bac0 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 13 Aug 2019 14:00:16 -0500 Subject: [PATCH 0370/2437] MC-18685: Remove custom layout updates from admin --- .../Magento/Cms/Api/Data/PageInterface.php | 2 + .../Adminhtml/Page/PostDataProcessor.php | 1 + .../Cms/Controller/Adminhtml/Page/Save.php | 16 +- .../Cms/Controller/Page/SaveManager.php | 102 +++++++++ app/code/Magento/Cms/Helper/Page.php | 22 +- .../Page/CustomLayout/CustomLayoutManager.php | 195 ++++++++++++++++++ .../Data/CustomLayoutSelected.php | 51 +++++ .../Data/CustomLayoutSelectedInterface.php | 29 +++ .../Page/CustomLayoutManagerInterface.php | 58 ++++++ .../Magento/Cms/Model/Page/DataProvider.php | 41 +++- .../Magento/Cms/Model/Page/IdentityMap.php | 72 +++++++ app/code/Magento/Cms/Model/PageRepository.php | 45 ++-- .../Cms/Observer/PageValidatorObserver.php | 45 ++++ app/code/Magento/Cms/etc/db_schema.xml | 2 + .../Magento/Cms/etc/db_schema_whitelist.json | 3 +- app/code/Magento/Cms/etc/di.xml | 6 + app/code/Magento/Cms/etc/events.xml | 3 + app/code/Magento/Cms/etc/webapi.xml | 4 +- .../adminhtml/ui_component/cms_page_form.xml | 21 ++ .../Magento/Webapi/Model/Config/Converter.php | 17 +- .../Magento/Webapi/Model/ServiceMetadata.php | 5 +- app/code/Magento/Webapi/Model/Soap/Config.php | 2 +- app/code/Magento/Webapi/etc/webapi_base.xsd | 1 + .../Magento/Cms/Api/PageRepositoryTest.php | 126 +++++++++++ .../Cms/Controller/Adminhtml/PageSaveTest.php | 113 ++++++++++ .../Model/Page/CustomLayoutManagerTest.php | 91 ++++++++ .../Magento/Cms/Model/PageRepositoryTest.php | 98 --------- .../User/_files/user_with_custom_role.php | 40 ++++ .../_files/user_with_custom_role_rollback.php | 28 +++ 29 files changed, 1098 insertions(+), 141 deletions(-) create mode 100644 app/code/Magento/Cms/Controller/Page/SaveManager.php create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php create mode 100644 app/code/Magento/Cms/Model/Page/IdentityMap.php create mode 100644 app/code/Magento/Cms/Observer/PageValidatorObserver.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role.php create mode 100644 dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role_rollback.php diff --git a/app/code/Magento/Cms/Api/Data/PageInterface.php b/app/code/Magento/Cms/Api/Data/PageInterface.php index 032f4916a85e4..50fa8cf0db2d1 100644 --- a/app/code/Magento/Cms/Api/Data/PageInterface.php +++ b/app/code/Magento/Cms/Api/Data/PageInterface.php @@ -145,6 +145,8 @@ public function getCustomRootTemplate(); /** * Get custom layout update xml * + * @deprecated Existing updates are applied, new are not accepted. + * @see \Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface * @return string|null */ public function getCustomLayoutUpdateXml(); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index 9b8933c8dba2e..20c33a2927101 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -64,6 +64,7 @@ public function __construct( */ public function filter($data) { + unset($data['layout_update_selected']); $filterRules = []; foreach (['custom_theme_from', 'custom_theme_to'] as $dateField) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 37cb45753174f..1412a86cf0fd0 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -102,16 +102,16 @@ public function execute() $model->setData($data); - $this->_eventManager->dispatch( - 'cms_page_prepare_save', - ['page' => $model, 'request' => $this->getRequest()] - ); + try { + $this->_eventManager->dispatch( + 'cms_page_prepare_save', + ['page' => $model, 'request' => $this->getRequest()] + ); - if (!$this->dataProcessor->validate($data)) { - return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); - } + if (!$this->dataProcessor->validate($data)) { + return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); + } - try { $this->pageRepository->save($model); $this->messageManager->addSuccessMessage(__('You saved the page.')); return $this->processResultRedirect($model, $resultRedirect, $data); diff --git a/app/code/Magento/Cms/Controller/Page/SaveManager.php b/app/code/Magento/Cms/Controller/Page/SaveManager.php new file mode 100644 index 0000000000000..bbd80b72a049d --- /dev/null +++ b/app/code/Magento/Cms/Controller/Page/SaveManager.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Controller\Page; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\Exception\LocalizedException; + +/** + * Manages CMS pages modification initiated by users. + */ +class SaveManager +{ + /** + * @var PageRepositoryInterface + */ + private $pageRepository; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @param PageRepositoryInterface $pageRepository + * @param AuthorizationInterface $authorization + */ + public function __construct( + PageRepositoryInterface $pageRepository, + AuthorizationInterface $authorization + ) { + $this->pageRepository = $pageRepository; + $this->authorization = $authorization; + } + + /** + * User saves a page (bew or existing). + * + * @param \Magento\Cms\Api\Data\PageInterface $page + * @return \Magento\Cms\Api\Data\PageInterface + * @throws LocalizedException + */ + public function save(\Magento\Cms\Api\Data\PageInterface $page): \Magento\Cms\Api\Data\PageInterface + { + $this->validatePage($page); + + return $this->pageRepository->save($page); + } + + /** + * Validate page data received from a user. + * + * @param PageInterface $page + * @return void + * @throws LocalizedException When validation failed. + */ + public function validatePage(\Magento\Cms\Api\Data\PageInterface $page): void + { + //Validate design changes. + if (!$this->authorization->isAllowed('Magento_Cms::save_design')) { + $notAllowed = false; + if (!$page->getId()) { + if ($page->getLayoutUpdateXml() + || $page->getPageLayout() + || $page->getCustomTheme() + || $page->getCustomLayoutUpdateXml() + || $page->getCustomThemeFrom() + || $page->getCustomThemeTo() + ) { + //Not allowed to set design properties value for new pages. + $notAllowed = true; + } + } else { + $savedPage = $this->pageRepository->getById($page->getId()); + if ($page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml() + || $page->getPageLayout() !== $savedPage->getPageLayout() + || $page->getCustomTheme() !== $savedPage->getCustomTheme() + || $page->getCustomThemeTo() !== $savedPage->getCustomThemeTo() + || $page->getCustomThemeFrom() !== $savedPage->getCustomThemeFrom() + || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml() + ) { + //Not allowed to update design settings. + $notAllowed = true; + } + } + + if ($notAllowed) { + throw new AuthorizationException( + __('You are not allowed to change CMS pages design settings') + ); + } + } + } +} diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 70e9437235ac3..bf263d94aaca7 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -5,7 +5,9 @@ */ namespace Magento\Cms\Helper; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ObjectManager; /** * CMS Page Helper @@ -76,6 +78,11 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper */ protected $resultPageFactory; + /** + * @var CustomLayoutManagerInterface + */ + private $customLayoutManager; + /** * Constructor * @@ -88,6 +95,7 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Framework\Escaper $escaper * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param CustomLayoutManagerInterface|null $customLayoutManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,7 +107,8 @@ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Framework\Escaper $escaper, - \Magento\Framework\View\Result\PageFactory $resultPageFactory + \Magento\Framework\View\Result\PageFactory $resultPageFactory, + ?CustomLayoutManagerInterface $customLayoutManager = null ) { $this->messageManager = $messageManager; $this->_page = $page; @@ -109,6 +118,8 @@ public function __construct( $this->_localeDate = $localeDate; $this->_escaper = $escaper; $this->resultPageFactory = $resultPageFactory; + $this->customLayoutManager = $customLayoutManager + ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); parent::__construct($context); } @@ -152,7 +163,14 @@ public function prepareResultPage(Action $action, $pageId = null) $resultPage = $this->resultPageFactory->create(); $this->setLayoutType($inRange, $resultPage); $resultPage->addHandle('cms_page_view'); - $resultPage->addPageLayoutHandles(['id' => str_replace('/', '_', $this->_page->getIdentifier())]); + $pageHandles = ['id' => str_replace('/', '_', $this->_page->getIdentifier())]; + //Selected custom updates. + $customLayoutHandle = $this->customLayoutManager->fetchHandle($this->_page->getId()); + if ($customLayoutHandle) { + $pageHandles = array_merge($pageHandles, $customLayoutHandle); + } + + $resultPage->addPageLayoutHandles($pageHandles); $this->_eventManager->dispatch( 'cms_page_render', diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php new file mode 100644 index 0000000000000..8d8dbe06a6078 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout; + +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; +use Magento\Cms\Model\ResourceModel\Page; +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\Cms\Model\Page\IdentityMap; +use Magento\Framework\App\Area; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\File; +use Magento\Framework\View\File\CollectorInterface; + +/** + * @inheritDoc + */ +class CustomLayoutManager implements CustomLayoutManagerInterface +{ + /** + * @var Page + */ + private $pageRepository; + + /** + * @var PageModelFactory; + */ + private $pageFactory; + + /** + * @var IdentityMap + */ + private $identityMap; + + /** + * @var CollectorInterface + */ + private $fileCollector; + + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @param Page $pageRepository + * @param PageModelFactory $factory + * @param IdentityMap $identityMap + * @param CollectorInterface $fileCollector + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + */ + public function __construct( + Page $pageRepository, + PageModelFactory $factory, + IdentityMap $identityMap, + CollectorInterface $fileCollector, + FlyweightFactory $themeFactory, + DesignInterface $design + ) { + $this->pageRepository = $pageRepository; + $this->pageFactory = $factory; + $this->identityMap = $identityMap; + $this->fileCollector = $fileCollector; + $this->themeFactory = $themeFactory; + $this->design = $design; + } + + /** + * Find page model by ID. + * + * @param int $id + * @return PageModel + * @throws NoSuchEntityException + */ + private function findPage(int $id): PageModel + { + if (!$page = $this->identityMap->get($id)) { + /** @var PageModel $page */ + $this->pageRepository->load($page = $this->pageFactory->create(), $id); + if (!$page->getIdentifier()) { + throw NoSuchEntityException::singleField('id', $id); + } + } + + return $page; + } + + /** + * Adopt page's identifier to be used as layout handle. + * + * @param PageModel $page + * @return string + */ + private function sanitizeIdentifier(PageModel $page): string + { + return str_replace('/', '_', $page->getIdentifier()); + } + + /** + * Save new custom layout file value for a page. + * + * @param int $pageId + * @param string|null $layoutFile + * @throws LocalizedException + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException + */ + private function saveLayout(int $pageId, ?string $layoutFile): void + { + $page = $this->findPage($pageId); + if ($layoutFile !== null && !in_array($layoutFile, $this->fetchAvailableFiles($pageId), true)) { + throw new \InvalidArgumentException( + $layoutFile .' is not available for page #' .$pageId + ); + } + + $page->setData('layout_update_selected', $layoutFile); + $this->pageRepository->save($page); + } + + /** + * @inheritDoc + */ + public function save(CustomLayoutSelectedInterface $layout): void + { + $this->saveLayout($layout->getPageId(), $layout->getLayoutFileId()); + } + + /** + * @inheritDoc + */ + public function deleteFor(int $pageId): void + { + $this->saveLayout($pageId, null); + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(int $pageId): array + { + $page = $this->findPage($pageId); + $identifier = $this->sanitizeIdentifier($page); + $layoutFiles = $this->fileCollector->getFiles( + $this->themeFactory->create($this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)), + 'cms_page_view_selectable_' .$identifier .'_*.xml' + ); + + return array_filter( + array_map( + function (File $file) use ($identifier) : ?string + { + preg_match( + '/selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', + $file->getName(), + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $layoutFiles + ) + ); + } + + /** + * @inheritDoc + */ + public function fetchHandle(int $pageId): ?array + { + $page = $this->findPage($pageId); + + return $page['layout_update_selected'] + ? ['selectable' => $this->sanitizeIdentifier($page) .'_' .$page['layout_update_selected']] : null; + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php new file mode 100644 index 0000000000000..fc29ebd72e802 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout\Data; + +/** + * @inheritDoc + */ +class CustomLayoutSelected implements CustomLayoutSelectedInterface +{ + /** + * @var int + */ + private $pageId; + + /** + * @var string + */ + private $layoutFile; + + /** + * @param int $pageId + * @param string $layoutFile + */ + public function __construct(int $pageId, string $layoutFile) + { + $this->pageId = $pageId; + $this->layoutFile = $layoutFile; + } + + /** + * @inheritDoc + */ + public function getPageId(): int + { + return $this->pageId; + } + + /** + * @inheritDoc + */ + public function getLayoutFileId(): string + { + return $this->layoutFile; + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php new file mode 100644 index 0000000000000..68bac57e98d56 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout\Data; + +/** + * Custom layout update file to be used for the specific CMS page. + */ +interface CustomLayoutSelectedInterface +{ + /** + * CMS page ID. + * + * @return int + */ + public function getPageId(): int; + + /** + * Custom layout file ID (layout update handle value). + * + * @return string + */ + public function getLayoutFileId(): string; +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php new file mode 100644 index 0000000000000..5c1c8662f9d9c --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Manage custom layout files for CMS pages. + */ +interface CustomLayoutManagerInterface +{ + /** + * Save layout file to be used when rendering given page. + * + * @throws LocalizedException When failed to save new value. + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException When given page is not found. + * @param CustomLayoutSelectedInterface $layout + * @return void + */ + public function save(CustomLayoutSelectedInterface $layout): void; + + /** + * Do not use custom layout update when rendering the page. + * + * @throws NoSuchEntityException When given page is not found. + * @throws LocalizedException When failed to remove existing value. + * @param int $pageId + * @return void + */ + public function deleteFor(int $pageId): void; + + /** + * List of available custom files for the given page. + * + * @throws NoSuchEntityException When given page is not found. + * @param int $pageId + * @return string[] + */ + public function fetchAvailableFiles(int $pageId): array; + + /** + * Get handles according to the page's settings. + * + * @throws NoSuchEntityException When given page is not found. + * @param int $pageId + * @return array|null With keys as handle IDs and values as handles. + */ + public function fetchHandle(int $pageId): ?array; +} diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index 64abaffd04e66..bf961c0420e2f 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -8,6 +8,7 @@ use Magento\Cms\Model\ResourceModel\Page\CollectionFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\App\RequestInterface; use Magento\Ui\DataProvider\Modifier\PoolInterface; use Magento\Framework\AuthorizationInterface; @@ -36,6 +37,16 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ private $auth; + /** + * @var RequestInterface + */ + private $request; + + /** + * @var CustomLayoutManagerInterface + */ + private $customLayoutManager; + /** * @param string $name * @param string $primaryFieldName @@ -46,6 +57,8 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param array $data * @param PoolInterface|null $pool * @param AuthorizationInterface|null $auth + * @param RequestInterface|null $request + * @param CustomLayoutManagerInterface|null $customLayoutManager */ public function __construct( $name, @@ -56,13 +69,18 @@ public function __construct( array $meta = [], array $data = [], PoolInterface $pool = null, - ?AuthorizationInterface $auth = null + ?AuthorizationInterface $auth = null, + ?RequestInterface $request = null, + ?CustomLayoutManagerInterface $customLayoutManager = null ) { $this->collection = $pageCollectionFactory->create(); $this->dataPersistor = $dataPersistor; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); $this->meta = $this->prepareMeta($this->meta); + $this->request = $request ?? ObjectManager::getInstance()->get(RequestInterface::class); + $this->customLayoutManager = $customLayoutManager + ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); } /** @@ -134,6 +152,27 @@ public function getMeta() $meta = array_merge_recursive($meta, $designMeta); } + //List of custom layout files available for current page. + $options = [['label' => 'Use default', 'value' => '']]; + if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { + //We must have a specific page selected. + foreach ($this->customLayoutManager->fetchAvailableFiles($pageId) as $layoutFile) { + $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; + } + } + $customLayoutMeta = [ + 'design' => [ + 'children' => [ + 'custom_layout_update_select' => [ + 'arguments' => [ + 'data' => ['options' => $options] + ] + ] + ] + ] + ]; + $meta = array_merge_recursive($meta, $customLayoutMeta); + return $meta; } } diff --git a/app/code/Magento/Cms/Model/Page/IdentityMap.php b/app/code/Magento/Cms/Model/Page/IdentityMap.php new file mode 100644 index 0000000000000..249010fbf90ce --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/IdentityMap.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page; + +/** + * Identity map of loaded pages. + */ +class IdentityMap +{ + /** + * @var Page[] + */ + private $pages = []; + + /** + * Add a page to the list. + * + * @param Page $page + * @throws \InvalidArgumentException When page doesn't have an ID. + * @return void + */ + public function add(Page $page): void + { + if (!$page->getId()) { + throw new \InvalidArgumentException('Cannot add non-persisted page to identity map'); + } + $this->pages[$page->getId()] = $page; + } + + /** + * Find a loaded page by ID. + * + * @param int $id + * @return Page|null + */ + public function get(int $id): ?Page + { + if (array_key_exists($id, $this->pages)) { + return $this->pages[$id]; + } + + return null; + } + + /** + * Remove the page from the list. + * + * @param int $id + * @return void + */ + public function remove(int $id): void + { + unset($this->pages[$id]); + } + + /** + * Clear the list. + * + * @return void + */ + public function clear(): void + { + $this->pages = []; + } +} diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index e6777659d7d88..2fc16e871cce7 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -8,6 +8,7 @@ use Magento\Cms\Api\Data; use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\App\ObjectManager; @@ -82,6 +83,11 @@ class PageRepository implements PageRepositoryInterface */ private $authorization; + /** + * @var IdentityMap + */ + private $identityMap; + /** * @param ResourcePage $resource * @param PageFactory $pageFactory @@ -92,6 +98,7 @@ class PageRepository implements PageRepositoryInterface * @param DataObjectProcessor $dataObjectProcessor * @param StoreManagerInterface $storeManager * @param CollectionProcessorInterface $collectionProcessor + * @param IdentityMap|null $identityMap * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -103,7 +110,8 @@ public function __construct( DataObjectHelper $dataObjectHelper, DataObjectProcessor $dataObjectProcessor, StoreManagerInterface $storeManager, - CollectionProcessorInterface $collectionProcessor = null + CollectionProcessorInterface $collectionProcessor = null, + ?IdentityMap $identityMap = null ) { $this->resource = $resource; $this->pageFactory = $pageFactory; @@ -114,6 +122,7 @@ public function __construct( $this->dataObjectProcessor = $dataObjectProcessor; $this->storeManager = $storeManager; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); + $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); } /** @@ -158,33 +167,18 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $page->setStoreId($storeId); } try { - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Cms::save_design') + //Persisted data + $savedPage = $page->getId() ? $this->getById($page->getId()) : null; + + //Custom layout update must be selected from available files + if ($page->getCustomLayoutUpdateXml() + && (!$savedPage || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml()) ) { - if (!$page->getId()) { - $page->setLayoutUpdateXml(null); - $page->setPageLayout(null); - $page->setCustomTheme(null); - $page->setCustomLayoutUpdateXml(null); - $page->setCustomThemeTo(null); - $page->setCustomThemeFrom(null); - } else { - $savedPage = $this->getById($page->getId()); - $page->setLayoutUpdateXml($savedPage->getLayoutUpdateXml()); - $page->setPageLayout($savedPage->getPageLayout()); - $page->setCustomTheme($savedPage->getCustomTheme()); - $page->setCustomLayoutUpdateXml($savedPage->getCustomLayoutUpdateXml()); - $page->setCustomThemeTo($savedPage->getCustomThemeTo()); - $page->setCustomThemeFrom($savedPage->getCustomThemeFrom()); - } + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); } $this->resource->save($page); + $this->identityMap->add($page); } catch (\Exception $exception) { throw new CouldNotSaveException( __('Could not save the page: %1', $exception->getMessage()), @@ -208,6 +202,8 @@ public function getById($pageId) if (!$page->getId()) { throw new NoSuchEntityException(__('The CMS page with the "%1" ID doesn\'t exist.', $pageId)); } + $this->identityMap->add($page); + return $page; } @@ -245,6 +241,7 @@ public function delete(\Magento\Cms\Api\Data\PageInterface $page) { try { $this->resource->delete($page); + $this->identityMap->remove($page->getId()); } catch (\Exception $exception) { throw new CouldNotDeleteException( __('Could not delete the page: %1', $exception->getMessage()) diff --git a/app/code/Magento/Cms/Observer/PageValidatorObserver.php b/app/code/Magento/Cms/Observer/PageValidatorObserver.php new file mode 100644 index 0000000000000..af0d30f450432 --- /dev/null +++ b/app/code/Magento/Cms/Observer/PageValidatorObserver.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Observer; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Controller\Page\SaveManager; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; + +/** + * Performing additional validation each time a user saves a CMS page. + */ +class PageValidatorObserver implements ObserverInterface +{ + /** + * @var SaveManager + */ + private $saveManager; + + /** + * @param SaveManager $saveManager + */ + public function __construct(SaveManager $saveManager) + { + $this->saveManager = $saveManager; + } + + /** + * @inheritDoc + * @throws LocalizedException + */ + public function execute(Observer $observer) + { + /** @var PageInterface $page */ + $page = $observer->getEvent()->getData('page'); + $this->saveManager->validatePage($page); + } +} diff --git a/app/code/Magento/Cms/etc/db_schema.xml b/app/code/Magento/Cms/etc/db_schema.xml index 1e64c905badd8..e0cf534e8354c 100644 --- a/app/code/Magento/Cms/etc/db_schema.xml +++ b/app/code/Magento/Cms/etc/db_schema.xml @@ -68,6 +68,8 @@ comment="Page Custom Template"/> <column xsi:type="text" name="custom_layout_update_xml" nullable="true" comment="Page Custom Layout Update Content"/> + <column xsi:type="varchar" name="layout_update_selected" nullable="true" + length="128" comment="Custom Layout Update File"/> <column xsi:type="date" name="custom_theme_from" comment="Page Custom Theme Active From Date"/> <column xsi:type="date" name="custom_theme_to" comment="Page Custom Theme Active To Date"/> <column xsi:type="varchar" name="meta_title" nullable="true" length="255" comment="Page Meta Title"/> diff --git a/app/code/Magento/Cms/etc/db_schema_whitelist.json b/app/code/Magento/Cms/etc/db_schema_whitelist.json index 8da44baf71719..fe0e9c1f2e22e 100644 --- a/app/code/Magento/Cms/etc/db_schema_whitelist.json +++ b/app/code/Magento/Cms/etc/db_schema_whitelist.json @@ -50,7 +50,8 @@ "custom_layout_update_xml": true, "custom_theme_from": true, "custom_theme_to": true, - "meta_title": true + "meta_title": true, + "layout_update_selected": true }, "index": { "CMS_PAGE_IDENTIFIER": true, diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index e41f500915916..0af2fa29fb762 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -235,4 +235,10 @@ <type name="Magento\Catalog\Model\Product"> <plugin name="cms" type="Magento\Cms\Model\Plugin\Product" sortOrder="100"/> </type> + <preference for="Magento\Cms\Model\Page\CustomLayoutManagerInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager" /> + <type name="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager"> + <arguments> + <argument name="fileCollector" xsi:type="object">Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Cms/etc/events.xml b/app/code/Magento/Cms/etc/events.xml index d6b9ad4ee0248..1ad847e215ccc 100644 --- a/app/code/Magento/Cms/etc/events.xml +++ b/app/code/Magento/Cms/etc/events.xml @@ -36,4 +36,7 @@ <event name="magento_cms_api_data_pageinterface_load_after"> <observer name="legacy_model_cms_page_after_load" instance="Magento\Framework\EntityManager\Observer\AfterEntityLoad" /> </event> + <event name="cms_page_prepare_save"> + <observer name="validate_cms_page" instance="Magento\Cms\Observer\PageValidatorObserver" /> + </event> </config> diff --git a/app/code/Magento/Cms/etc/webapi.xml b/app/code/Magento/Cms/etc/webapi.xml index 5b66d0e3ed879..8cf0cb767d9be 100644 --- a/app/code/Magento/Cms/etc/webapi.xml +++ b/app/code/Magento/Cms/etc/webapi.xml @@ -20,8 +20,8 @@ <resource ref="Magento_Cms::page"/> </resources> </route> - <route url="/V1/cmsPage" method="POST"> - <service class="Magento\Cms\Api\PageRepositoryInterface" method="save"/> + <route url="/V1/cmsPage" method="POST" soapService="Magento\Cms\Api\PageRepositoryInterface"> + <service class="Magento\Cms\Controller\Page\SaveManager" method="save"/> <resources> <resource ref="Magento_Cms::page"/> </resources> diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml index dd58d17cbf577..396923a2b6f3b 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml @@ -249,6 +249,7 @@ </field> <field name="layout_update_xml" formElement="textarea"> <argument name="data" xsi:type="array"> + <item name="disabled" xsi:type="boolean">true</item> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">page</item> </item> @@ -259,6 +260,26 @@ <dataScope>layout_update_xml</dataScope> </settings> </field> + <field name="custom_layout_update_select" formElement="select"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="source" xsi:type="string">page</item> + </item> + </argument> + <settings> + <dataType>text</dataType> + <label translate="true">Custom Layout Update</label> + <tooltip> + <description translate="true"> + List of layout files with an update handle "selectable" + matching *PageIdentifier*_*UpdateID*. + <br/> + See Magento documentation for more information + </description> + </tooltip> + <dataScope>layout_update_selected</dataScope> + </settings> + </field> </fieldset> <fieldset name="custom_design_update" sortOrder="60"> <settings> diff --git a/app/code/Magento/Webapi/Model/Config/Converter.php b/app/code/Magento/Webapi/Model/Config/Converter.php index 837a0f84423ad..3ce79a83b176b 100644 --- a/app/code/Magento/Webapi/Model/Config/Converter.php +++ b/app/code/Magento/Webapi/Model/Config/Converter.php @@ -50,16 +50,22 @@ public function convert($source) $service = $route->getElementsByTagName('service')->item(0); $serviceClass = $service->attributes->getNamedItem('class')->nodeValue; $serviceMethod = $service->attributes->getNamedItem('method')->nodeValue; + //WSDL method name which may differ from an actual method used. $soapMethod = $serviceMethod; if ($soapOperationNode = $route->attributes->getNamedItem('soapOperation')) { $soapMethod = trim($soapOperationNode->nodeValue); } + //WSDL class name which may differ from an actual class used. + $soapService = $serviceClass; + if ($soapServiceNode = $route->attributes->getNamedItem('soapService')) { + $soapService = trim($soapServiceNode->nodeValue); + } $url = trim($route->attributes->getNamedItem('url')->nodeValue); $version = $this->convertVersion($url); $serviceClassData = []; - if (isset($result[self::KEY_SERVICES][$serviceClass][$version])) { - $serviceClassData = $result[self::KEY_SERVICES][$serviceClass][$version]; + if (isset($result[self::KEY_SERVICES][$soapService][$version])) { + $serviceClassData = $result[self::KEY_SERVICES][$soapService][$version]; } $resources = $route->getElementsByTagName('resource'); @@ -110,13 +116,18 @@ public function convert($source) if (isset($serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE])) { $serviceSecure = $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE]; } + //Real method to use when performing operations. if (!isset($serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_REAL_SERVICE_METHOD])) { $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_REAL_SERVICE_METHOD] = $serviceMethod; } + //Real class to initialize when performing operations. + if (!isset($serviceClassData[self::KEY_METHODS][$soapMethod]['real_class'])) { + $serviceClassData[self::KEY_METHODS][$soapMethod]['real_class'] = $serviceClass; + } $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE] = $serviceSecure || $secure; $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_DATA_PARAMETERS] = $serviceData; - $result[self::KEY_SERVICES][$serviceClass][$version] = $serviceClassData; + $result[self::KEY_SERVICES][$soapService][$version] = $serviceClassData; } return $result; } diff --git a/app/code/Magento/Webapi/Model/ServiceMetadata.php b/app/code/Magento/Webapi/Model/ServiceMetadata.php index 36f5819b03c98..d2bd08267128a 100644 --- a/app/code/Magento/Webapi/Model/ServiceMetadata.php +++ b/app/code/Magento/Webapi/Model/ServiceMetadata.php @@ -118,12 +118,15 @@ protected function initServicesMetadata() $methods = []; foreach ($serviceData[Converter::KEY_METHODS] as $methodName => $methodMetadata) { $services[$serviceName][self::KEY_SERVICE_METHODS][$methodName] = [ + //Method to use for the operation. May differ from the operation's name. self::KEY_METHOD => $methodMetadata[Converter::KEY_REAL_SERVICE_METHOD], self::KEY_IS_REQUIRED => (bool)$methodMetadata[Converter::KEY_SECURE], self::KEY_IS_SECURE => $methodMetadata[Converter::KEY_SECURE], self::KEY_ACL_RESOURCES => $methodMetadata[Converter::KEY_ACL_RESOURCES], self::KEY_METHOD_ALIAS => $methodName, - self::KEY_ROUTE_PARAMS => $methodMetadata[Converter::KEY_DATA_PARAMETERS] + self::KEY_ROUTE_PARAMS => $methodMetadata[Converter::KEY_DATA_PARAMETERS], + //Class to initialize for the operation. May differ from the operation's name. + 'real_class' => $methodMetadata['real_class'] ]; $services[$serviceName][self::KEY_CLASS] = $serviceClass; $methods[] = $methodMetadata[Converter::KEY_REAL_SERVICE_METHOD]; diff --git a/app/code/Magento/Webapi/Model/Soap/Config.php b/app/code/Magento/Webapi/Model/Soap/Config.php index 190280ff8f004..94b3dad48b1ce 100644 --- a/app/code/Magento/Webapi/Model/Soap/Config.php +++ b/app/code/Magento/Webapi/Model/Soap/Config.php @@ -74,7 +74,7 @@ protected function getSoapOperations($requestedServices) foreach ($this->getRequestedSoapServices($requestedServices) as $serviceName => $serviceData) { foreach ($serviceData[ServiceMetadata::KEY_SERVICE_METHODS] as $methodData) { $method = $methodData[ServiceMetadata::KEY_METHOD]; - $class = $serviceData[ServiceMetadata::KEY_CLASS]; + $class = $methodData['real_class']; $operation = $methodData[ServiceMetadata::KEY_METHOD_ALIAS]; $operationName = $serviceName . ucfirst($operation); $this->soapOperations[$operationName] = [ diff --git a/app/code/Magento/Webapi/etc/webapi_base.xsd b/app/code/Magento/Webapi/etc/webapi_base.xsd index 7d1a5a14ba78f..b38efae2a3435 100644 --- a/app/code/Magento/Webapi/etc/webapi_base.xsd +++ b/app/code/Magento/Webapi/etc/webapi_base.xsd @@ -30,6 +30,7 @@ <xs:attribute name="url" type="xs:string" use="required"/> <xs:attribute name="secure" type="xs:boolean"/> <xs:attribute name="soapOperation" type="xs:string"/> + <xs:attribute name="soapService" type="xs:string"/> </xs:complexType> <xs:complexType name="serviceType"> diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index c40d1918cca67..3470c507c4e5b 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -5,11 +5,16 @@ */ namespace Magento\Cms\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; use Magento\Cms\Api\Data\PageInterface; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -47,6 +52,21 @@ class PageRepositoryTest extends WebapiAbstract */ protected $currentPage; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + /** * Execute per test initialization. */ @@ -57,6 +77,9 @@ public function setUp() $this->dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); $this->dataObjectProcessor = Bootstrap::getObjectManager() ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); } /** @@ -379,4 +402,107 @@ private function deletePageByIdentifier($pageId) $this->_webApiCall($serviceInfo, [PageInterface::PAGE_ID => $pageId]); } + + /** + * Check that extra authorization is required for the design properties. + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving pages but not their design settings. + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load('test_custom_role', 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page']); + $rules->saveRel(); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken('customRoleUser', \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD); + + $id = 'test-cms-page'; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'token' => $token, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + 'token' => $token + ], + ]; + $requestData = [ + 'page' => [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_THEME => 1 + ], + ]; + + //Creating new page with design settings. + $exceptionMessage = null; + try { + $this->_webApiCall($serviceInfo, $requestData); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have the permissions. + $this->assertEquals('You are not allowed to change CMS pages design settings', $exceptionMessage); + + //Updating the user role to allow access to design properties. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page', 'Magento_Cms::save_design']); + $rules->saveRel(); + //Making the same request with design settings. + $result = $this->_webApiCall($serviceInfo, $requestData); + $this->assertArrayHasKey('id', $result); + //Page must be saved. + $this->currentPage = $this->pageRepository->getById($result['id']); + $this->assertEquals($id, $this->currentPage->getIdentifier()); + $this->assertEquals(1, $this->currentPage->getCustomTheme()); + $requestData['page']['id'] = $this->currentPage->getId(); + + //Updating our role to remove design properties access. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page']); + $rules->saveRel(); + //Updating the page but with the same design properties values. + $result = $this->_webApiCall($serviceInfo, $requestData); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + + //Changing a design property. + $requestData['page'][PageInterface::CUSTOM_THEME] = 2; + $exceptionMessage = null; + try { + $this->_webApiCall($serviceInfo, $requestData); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have permissions to do that. + $this->assertEquals('You are not allowed to change CMS pages design settings', $exceptionMessage); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php new file mode 100644 index 0000000000000..17c28a76d394d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Controller\Adminhtml; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\Page; +use Magento\Framework\Acl\Builder; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test the saving CMS pages via admin area interface. + * + * @magentoAppArea adminhtml + */ +class PageSaveTest extends AbstractBackendController +{ + /** + * @var string + */ + protected $resource = 'Magento_Cms::save'; + + /** + * @var string + */ + protected $uri = 'backend/cms/page/save'; + + /** + * @var string + */ + protected $httpMethod = HttpRequest::METHOD_POST; + + /** + * @var Builder + */ + private $aclBuilder; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + } + + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation enabled + * @return void + */ + public function testSaveDesign(): void + { + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['You are not allowed to change CMS pages design settings']; + //Test page data. + $id = 'test-page'; + $requestData = [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_THEME => '1' + ]; + + //Creating a new page with design properties without the required permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $this->aclBuilder->getAcl()->allow(null, ['Magento_Cms::save', 'Magento_Cms::save_design']); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + /** @var Page $page */ + $page = Bootstrap::getObjectManager()->create(PageInterface::class); + $page->load($id, PageInterface::IDENTIFIER); + $this->assertNotEmpty($page->getId()); + $this->assertEquals(1, $page->getCustomTheme()); + $requestData['page_id'] = $page->getId(); + $this->getRequest()->setPostValue($requestData); + + //Updating the page without the permissions but not touching design settings. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + $this->assertSessionMessages(self::equalTo($sessionMessages), MessageInterface::TYPE_ERROR); + + //Updating design settings without the permissions. + $requestData[PageInterface::CUSTOM_THEME] = '2'; + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + $sessionMessages[] = $sessionMessages[0]; + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php new file mode 100644 index 0000000000000..7b2f0b8cbd57c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page; +use Magento\Cms\Model\PageFactory; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Framework\View\File\CollectorInterface; +use Magento\Framework\View\File; + +/** + * Test the manager. + */ +class CustomLayoutManagerTest extends TestCase +{ + /** + * @var CustomLayoutManagerInterface + */ + private $manager; + + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + //Mocking available list of files for the page. + $files = [ + new File('cms_page_view_selectable_page100_select1.xml', 'test'), + new File('cms_page_view_selectable_page100_select2.xml', 'test') + ]; + $fileCollector = $this->getMockForAbstractClass(CollectorInterface::class); + $fileCollector->method('getFiles') + ->willReturn($files); + + $this->manager = $objectManager->create( + CustomLayoutManagerInterface::class, + ['fileCollector' => $fileCollector] + ); + $this->pageFactory = $objectManager->get(PageFactory::class); + } + + /** + * Test updating a page's custom layout. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @return void + */ + public function testCustomLayout(): void + { + /** @var Page $page */ + $page = $this->pageFactory->create(); + $page->load('page100', 'identifier'); + $pageId = (int)$page->getId(); + + //Invalid file ID + $exceptionRaised = null; + try { + $this->manager->save(new CustomLayoutSelected($pageId, 'some_file')); + } catch (\Throwable $exception) { + $exceptionRaised = $exception; + } + $this->assertNotEmpty($exceptionRaised); + $this->assertInstanceOf(\InvalidArgumentException::class, $exceptionRaised); + + //Set file ID + $this->manager->save(new CustomLayoutSelected($pageId, 'select2')); + + //Test handles + $this->assertEquals(['selectable' => 'page100_select2'], $this->manager->fetchHandle($pageId)); + + //Removing custom file + $this->manager->deleteFor($pageId); + + //Test handles + $this->assertNull($this->manager->fetchHandle($pageId)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php deleted file mode 100644 index cd4674f95d722..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Cms\Model; - -use Magento\Backend\Model\Auth; -use Magento\Cms\Api\PageRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\TestFramework\Helper\Bootstrap; -use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Bootstrap as TestBootstrap; -use Magento\Framework\Acl\Builder; - -/** - * Test class for page repository. - */ -class PageRepositoryTest extends TestCase -{ - /** - * Test subject. - * - * @var PageRepositoryInterface - */ - private $repo; - - /** - * @var Auth - */ - private $auth; - - /** - * @var SearchCriteriaBuilder - */ - private $criteriaBuilder; - - /** - * @var Builder - */ - private $aclBuilder; - - /** - * Sets up common objects. - * - * @inheritDoc - */ - protected function setUp() - { - $this->repo = Bootstrap::getObjectManager()->create(PageRepositoryInterface::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->criteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); - } - - /** - * @inheritDoc - */ - protected function tearDown() - { - parent::tearDown(); - - $this->auth->logout(); - } - - /** - * Test authorization when saving page's design settings. - * - * @magentoDataFixture Magento/Cms/_files/pages.php - * @magentoAppArea adminhtml - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - */ - public function testSaveDesign() - { - $pages = $this->repo->getList( - $this->criteriaBuilder->addFilter('identifier', 'page_design_blank')->create() - )->getItems(); - $page = array_pop($pages); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); - - //Admin doesn't have access to page's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); - - $page->setCustomTheme('test'); - $page = $this->repo->save($page); - $this->assertNotEquals('test', $page->getCustomTheme()); - - //Admin has access to page' design. - $this->aclBuilder->getAcl()->allow(null, ['Magento_Cms::save', 'Magento_Cms::save_design']); - - $page->setCustomTheme('test'); - $page = $this->repo->save($page); - $this->assertEquals('test', $page->getCustomTheme()); - } -} diff --git a/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role.php b/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role.php new file mode 100644 index 0000000000000..867832df20240 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Role; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\User\Model\User; +use Magento\Authorization\Model\RulesFactory; +use Magento\Authorization\Model\Rules; + +//Creating a new admin user with a custom role to safely change role settings without affecting the main user's role. +/** @var Role $role */ +$role = Bootstrap::getObjectManager()->get(RoleFactory::class)->create(); +$role->setName('test_custom_role'); +$role->setData('role_name', $role->getName()); +$role->setRoleType(\Magento\Authorization\Model\Acl\Role\Group::ROLE_TYPE); +$role->setUserType((string)\Magento\Authorization\Model\UserContextInterface::USER_TYPE_ADMIN); +$role->save(); +/** @var Rules $rules */ +$rules = Bootstrap::getObjectManager()->get(RulesFactory::class)->create(); +$rules->setRoleId($role->getId()); +//Granted all permissions. +$rules->setResources([Bootstrap::getObjectManager()->get(\Magento\Framework\Acl\RootResource::class)->getId()]); +$rules->saveRel(); + +/** @var User $user */ +$user = Bootstrap::getObjectManager()->create(User::class); +$user->setFirstname("John") + ->setLastname("Doe") + ->setUsername('customRoleUser') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUser@example.com') + ->setIsActive(1) + ->setRoleId($role->getId()); +$user->save(); diff --git a/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role_rollback.php b/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role_rollback.php new file mode 100644 index 0000000000000..f3c061236a1c3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/User/_files/user_with_custom_role_rollback.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\Role; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\User\Model\User; +use Magento\Authorization\Model\RulesFactory; +use Magento\Authorization\Model\Rules; + +//Deleting the user and the role. +/** @var User $user */ +$user = Bootstrap::getObjectManager()->create(User::class); +$user->load('customRoleUser', 'username'); +$user->delete(); +/** @var Role $role */ +$role = Bootstrap::getObjectManager()->get(RoleFactory::class)->create(); +$role->load('test_custom_role', 'role_name'); +/** @var Rules $rules */ +$rules = Bootstrap::getObjectManager()->get(RulesFactory::class)->create(); +$rules->load($role->getId(), 'role_id'); +$rules->delete(); +$role->delete(); From 7d3042fae6bfa3b8f7eb2afa6cd23407b0e44353 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@adobe.com> Date: Tue, 13 Aug 2019 14:23:40 -0500 Subject: [PATCH 0371/2437] MC-19061: Revert ticket MC-17003 Skip MFTF test --- .../Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml index a667f40ad327f..afe70243f602c 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminWatermarkUploadTest.xml @@ -16,6 +16,9 @@ <description value="Watermark images should be able to be uploaded in the admin"/> <severity value="MAJOR"/> <testCaseId value="MC-5796"/> + <skip> + <issueId value="MC-18496"/> + </skip> <group value="Watermark"/> </annotations> <before> From e913ce88010bbee69909e2d3e9cdf28816971eb8 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 14 Aug 2019 15:03:02 +0300 Subject: [PATCH 0372/2437] MC-19218: Catalog Event created with incorrect start and end dates - revert #22463 --- .../Magento/Framework/Stdlib/DateTime/Timezone.php | 3 +-- .../Stdlib/Test/Unit/DateTime/TimezoneTest.php | 11 ----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index 45c31d367b34a..5b028eda4c71f 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -196,8 +196,7 @@ public function date($date = null, $locale = null, $useTimezone = true, $include public function scopeDate($scope = null, $date = null, $includeTime = false) { $timezone = $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope); - $date = new \DateTime(is_numeric($date) ? '@' . $date : $date); - $date->setTimezone(new \DateTimeZone($timezone)); + $date = new \DateTime(is_numeric($date) ? '@' . $date : $date, new \DateTimeZone($timezone)); if (!$includeTime) { $date->setTime(0, 0, 0); } diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index 3d7d14a394629..d57525590b9e8 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -253,15 +253,4 @@ private function scopeConfigWillReturnConfiguredTimezone($configuredTimezone) { $this->scopeConfig->method('getValue')->with('', '', null)->willReturn($configuredTimezone); } - - public function testCheckIfScopeDateSetsTimeZone() - { - $scopeDate = new \DateTime('now', new \DateTimeZone('America/Vancouver')); - $this->scopeConfig->method('getValue')->willReturn('America/Vancouver'); - - $this->assertEquals( - $scopeDate->getTimezone(), - $this->getTimezone()->scopeDate(0, $scopeDate->getTimestamp())->getTimezone() - ); - } } From ccc5074fba3d60f132908d6bf7ad2fefad0280dc Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 14 Aug 2019 15:07:06 +0300 Subject: [PATCH 0373/2437] MC-19078: Product is missing from shopping cart after payment cancellation --- .../CheckExpirePersistentQuoteObserver.php | 34 +++++++++++++++++-- ...CheckExpirePersistentQuoteObserverTest.php | 18 +++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php index 1261a90b5843b..862ac561433ed 100644 --- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php +++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php @@ -6,6 +6,9 @@ namespace Magento\Persistent\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote; /** @@ -74,6 +77,11 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface */ private $quote; + /** + * @var CartRepositoryInterface + */ + private $quoteRepository; + /** * @param \Magento\Persistent\Helper\Session $persistentSession * @param \Magento\Persistent\Helper\Data $persistentData @@ -82,6 +90,7 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\RequestInterface $request + * @param CartRepositoryInterface $quoteRepository */ public function __construct( \Magento\Persistent\Helper\Session $persistentSession, @@ -90,7 +99,8 @@ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Customer\Model\Session $customerSession, \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\App\RequestInterface $request + \Magento\Framework\App\RequestInterface $request, + CartRepositoryInterface $quoteRepository ) { $this->_persistentSession = $persistentSession; $this->quoteManager = $quoteManager; @@ -99,6 +109,7 @@ public function __construct( $this->_eventManager = $eventManager; $this->_persistentData = $persistentData; $this->request = $request; + $this->quoteRepository = $quoteRepository; } /** @@ -146,9 +157,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) */ private function isPersistentQuoteOutdated(): bool { - if ((!$this->_persistentData->isEnabled() || !$this->_persistentData->isShoppingCartPersist()) + if (!($this->_persistentData->isEnabled() && $this->_persistentData->isShoppingCartPersist()) && !$this->_customerSession->isLoggedIn() - && $this->_checkoutSession->getQuoteId()) { + && $this->_checkoutSession->getQuoteId() + && $this->isActiveQuote() + ) { return (bool)$this->getQuote()->getIsPersistent(); } return false; @@ -181,6 +194,21 @@ private function getQuote(): Quote return $this->quote; } + /** + * Check if quote is active. + * + * @return bool + */ + private function isActiveQuote(): bool + { + try { + $this->quoteRepository->getActive($this->_checkoutSession->getQuoteId()); + return true; + } catch (NoSuchEntityException $e) { + return false; + } + } + /** * Check current request is coming from onepage checkout page. * diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php index 2c5b9dad48eb3..31a6ba9f10e7e 100644 --- a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php @@ -6,6 +6,8 @@ namespace Magento\Persistent\Test\Unit\Observer; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; /** @@ -63,6 +65,11 @@ class CheckExpirePersistentQuoteObserverTest extends \PHPUnit\Framework\TestCase */ private $quoteMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CartRepositoryInterface + */ + private $quoteRepositoryMock; + /** * @inheritdoc */ @@ -82,6 +89,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['getRequestUri', 'getServer']) ->getMockForAbstractClass(); + $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class); $this->model = new \Magento\Persistent\Observer\CheckExpirePersistentQuoteObserver( $this->sessionMock, @@ -90,7 +98,8 @@ protected function setUp() $this->eventManagerMock, $this->customerSessionMock, $this->checkoutSessionMock, - $this->requestMock + $this->requestMock, + $this->quoteRepositoryMock ); $this->quoteMock = $this->getMockBuilder(Quote::class) ->setMethods(['getCustomerIsGuest', 'getIsPersistent']) @@ -111,12 +120,19 @@ public function testExecuteWhenCanNotApplyPersistentData() public function testExecuteWhenPersistentIsNotEnabled() { + $quoteId = 'quote_id_1'; + $this->persistentHelperMock ->expects($this->once()) ->method('canProcess') ->with($this->observerMock) ->willReturn(true); $this->persistentHelperMock->expects($this->exactly(2))->method('isEnabled')->willReturn(false); + $this->checkoutSessionMock->expects($this->exactly(2))->method('getQuoteId')->willReturn($quoteId); + $this->quoteRepositoryMock->expects($this->once()) + ->method('getActive') + ->with($quoteId) + ->willThrowException(new NoSuchEntityException()); $this->eventManagerMock->expects($this->never())->method('dispatch'); $this->model->execute($this->observerMock); } From 8a4e7c4ca5f3e75fd2f158afa5338c39b5132418 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Wed, 14 Aug 2019 17:22:09 +0300 Subject: [PATCH 0374/2437] MC-19132: Child product image of configurable product not displayed --- lib/internal/Magento/Framework/Image/Adapter/Gd2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php index f8cad0e8821ef..7e92b336cfdc0 100644 --- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php +++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php @@ -296,7 +296,7 @@ private function _fillBackgroundColor(&$imageResourceTo) imagecolortransparent($imageResourceTo, $transparentColor); return $transparentColor; } - // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (\Exception $e) { // fallback to default background color } From eb3d1728566f992401bbfb648ab56cf39deecf9b Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Wed, 14 Aug 2019 18:02:38 +0300 Subject: [PATCH 0375/2437] MC-19080: Incorrect behavior after shipping methods disabled --- app/code/Magento/Shipping/Model/Shipping.php | 12 +- .../Model/CollectRatesTest.php | 118 ++++++++++++++++++ .../Magento/Shipping/Model/ShippingTest.php | 99 ++++----------- .../Magento/Ups/Model/CollectRatesTest.php | 50 ++++++++ 4 files changed, 199 insertions(+), 80 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php diff --git a/app/code/Magento/Shipping/Model/Shipping.php b/app/code/Magento/Shipping/Model/Shipping.php index 77e6f3c5219fb..48127469ea984 100644 --- a/app/code/Magento/Shipping/Model/Shipping.php +++ b/app/code/Magento/Shipping/Model/Shipping.php @@ -273,7 +273,7 @@ public function collectRates(\Magento\Quote\Model\Quote\Address\RateRequest $req */ private function prepareCarrier(string $carrierCode, RateRequest $request): AbstractCarrier { - $carrier = $this->isActive($carrierCode) + $carrier = $this->isShippingCarrierAvailable($carrierCode) ? $this->_carrierFactory->create($carrierCode, $request->getStoreId()) : null; if (!$carrier) { @@ -363,6 +363,7 @@ public function composePackagesForCarrier($carrier, $request) { $allItems = $request->getAllItems(); $fullItems = []; + $weightItems = []; $maxWeight = (double)$carrier->getConfigData('max_package_weight'); @@ -428,12 +429,13 @@ public function composePackagesForCarrier($carrier, $request) if (!empty($decimalItems)) { foreach ($decimalItems as $decimalItem) { - $fullItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']); + $weightItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']); } } else { - $fullItems[] = array_fill(0, $qty, $itemWeight); + $weightItems[] = array_fill(0, $qty, $itemWeight); } } + $fullItems = array_merge($fullItems, ...$weightItems); sort($fullItems); return $this->_makePieces($fullItems, $maxWeight); @@ -537,9 +539,9 @@ public function setCarrierAvailabilityConfigField($code = 'active') * Checks availability of carrier. * * @param string $carrierCode - * @return bool|null + * @return bool */ - private function isActive(string $carrierCode) + private function isShippingCarrierAvailable(string $carrierCode): bool { return $this->_scopeConfig->isSetFlag( 'carriers/' . $carrierCode . '/' . $this->_availabilityConfigField, diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php new file mode 100644 index 0000000000000..c5e3ab1935a2e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\OfflineShipping\Model; + +use Magento\Framework\DataObject; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Model\Quote\Address\RateResult\Error; +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\Shipping\Model\Shipping; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Integration tests for offline shipping carriers. + * @magentoAppIsolation enabled + */ +class CollectRatesTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Shipping + */ + protected $shipping; + + /** + * @var string + */ + protected $carrier = 'flatrate'; + + /** + * @var string + */ + protected $errorMessage = 'This shipping method is not available. To use this shipping method, please contact us.'; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->shipping = $this->objectManager->get(Shipping::class); + } + + /** + * @magentoConfigFixture default_store carriers/flatrate/active 1 + * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 + * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK + * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 + */ + public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() + { + $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); + $rate = $this->getRate($result->getResult()); + + static::assertEquals($this->carrier, $rate->getData('carrier')); + static::assertEquals($this->errorMessage, $rate->getData('error_message')); + } + + /** + * @magentoConfigFixture default_store carriers/flatrate/active 0 + * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 + * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK + * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 + */ + public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() + { + $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); + $rate = $this->getRate($result->getResult()); + + static::assertNull($rate); + } + + /** + * @return DataObject + */ + private function getAddress(): DataObject + { + $address = $this->objectManager->create( + DataObject::class, + [ + 'data' => [ + 'region_id' => 'CA', + 'postcode' => '11111', + 'lastname' => 'John', + 'firstname' => 'Doe', + 'street' => 'Some street', + 'city' => 'Los Angeles', + 'email' => 'john.doe@example.com', + 'telephone' => '11111111', + 'country_id' => 'US', + 'item_qty' => 1, + ], + ] + ); + + return $address; + } + + /** + * @param Result $result + * @return Method|Error + */ + private function getRate(Result $result) + { + $rates = $result->getAllRates(); + + return array_pop($rates); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php index 8c5dfb0d0ff5e..760b24db5ef30 100644 --- a/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php +++ b/dev/tests/integration/testsuite/Magento/Shipping/Model/ShippingTest.php @@ -3,19 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Shipping\Model; use Magento\Framework\DataObject; use Magento\Framework\ObjectManagerInterface; -use Magento\Quote\Model\Quote\Address\RateResult\Error; use Magento\Quote\Model\Quote\Address\RateResult\Method; use Magento\Shipping\Model\Rate\Result; use Magento\TestFramework\Helper\Bootstrap; /** - * Contains list of tests for Shipping model. - * @magentoAppIsolation enabled + * Contains list of tests for Shipping model */ class ShippingTest extends \PHPUnit\Framework\TestCase { @@ -45,8 +42,22 @@ protected function setUp() */ public function testCollectRatesByAddress() { + $address = $this->objectManager->create(DataObject::class, [ + 'data' => [ + 'region_id' => 'CA', + 'postcode' => '11111', + 'lastname' => 'John', + 'firstname' => 'Doe', + 'street' => 'Some street', + 'city' => 'Los Angeles', + 'email' => 'john.doe@example.com', + 'telephone' => '11111111', + 'country_id' => 'US', + 'item_qty' => 1 + ] + ]); /** @var Shipping $result */ - $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); + $result = $this->model->collectRatesByAddress($address, 'flatrate'); static::assertInstanceOf(Shipping::class, $result); return $result->getResult(); @@ -57,81 +68,19 @@ public function testCollectRatesByAddress() * @covers \Magento\Shipping\Model\Shipping::collectRatesByAddress * @param Result $result * @depends testCollectRatesByAddress + * @magentoConfigFixture carriers/flatrate/active 1 + * @magentoConfigFixture carriers/flatrate/price 5.00 */ public function testCollectRates(Result $result) { - $rate = $this->getRate($result); - static::assertInstanceOf(Method::class, $rate); - static::assertEquals('flatrate', $rate->getData('carrier')); - static::assertEquals(5, $rate->getData('price')); - } + $rates = $result->getAllRates(); + static::assertNotEmpty($rates); - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 - * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK - * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 - */ - public function testShippingMethodIsActiveAndNotApplicable() - { - $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); - $rate = $this->getRate($result->getResult()); + /** @var Method $rate */ + $rate = array_pop($rates); + static::assertInstanceOf(Method::class, $rate); static::assertEquals('flatrate', $rate->getData('carrier')); - static::assertEquals( - 'This shipping method is not available. To use this shipping method, please contact us.', - $rate->getData('error_message') - ); - } - - /** - * @magentoConfigFixture default_store carriers/flatrate/active 0 - * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 - * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK - * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 - */ - public function testShippingMethodIsNotActiveAndNotApplicable() - { - $result = $this->model->collectRatesByAddress($this->getAddress(), 'flatrate'); - $rate = $this->getRate($result->getResult()); - - static::assertNull($rate); - } - - /** - * @return DataObject - */ - private function getAddress(): DataObject - { - $address = $this->objectManager->create( - DataObject::class, - [ - 'data' => [ - 'region_id' => 'CA', - 'postcode' => '11111', - 'lastname' => 'John', - 'firstname' => 'Doe', - 'street' => 'Some street', - 'city' => 'Los Angeles', - 'email' => 'john.doe@example.com', - 'telephone' => '11111111', - 'country_id' => 'US', - 'item_qty' => 1, - ], - ] - ); - - return $address; - } - - /** - * @param Result $result - * @return Method|Error - */ - private function getRate(Result $result) - { - $rates = $result->getAllRates(); - - return array_pop($rates); + static::assertEquals(5, $rate->getData('price')); } } diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php new file mode 100644 index 0000000000000..27323bf22f01e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Ups\Model; + +/** + * Integration tests for online shipping carriers. + * @magentoAppIsolation enabled + */ +class CollectRatesTest extends \Magento\OfflineShipping\Model\CollectRatesTest +{ + /** + * @var string + */ + protected $carrier = 'ups'; + + /** + * @var string + */ + protected $errorMessage = 'This shipping method is currently unavailable. ' . + 'If you would like to ship using this shipping method, please contact us.'; + + /** + * @magentoConfigFixture default_store carriers/ups/active 1 + * @magentoConfigFixture default_store carriers/ups/type UPS + * @magentoConfigFixture default_store carriers/ups/sallowspecific 1 + * @magentoConfigFixture default_store carriers/ups/specificcountry UK + * @magentoConfigFixture default_store carriers/ups/showmethod 1 + */ + public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() + { + parent::testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable(); + } + + /** + * @magentoConfigFixture default_store carriers/ups/active 0 + * @magentoConfigFixture default_store carriers/ups/type UPS + * @magentoConfigFixture default_store carriers/ups/sallowspecific 1 + * @magentoConfigFixture default_store carriers/ups/specificcountry UK + * @magentoConfigFixture default_store carriers/ups/showmethod 1 + */ + public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() + { + parent::testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable(); + } +} From 9532e283542714364f2ce7b15508ea8c110e4165 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Wed, 14 Aug 2019 12:20:56 -0500 Subject: [PATCH 0376/2437] MC-19194: UpdateCartItems mutation does not update cart item quantity - Reverted back commit c7d9130289993310992cb3e4a0b5ec7c3dd196bd and modified functional test --- .../Model/Cart/UpdateCartItem.php | 1 + .../Quote/Guest/UpdateCartItemsTest.php | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php index 81a7779eb98b5..b18c6ad662335 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php @@ -117,6 +117,7 @@ private function updateItemQuantity(int $itemId, Quote $cart, float $quantity) } $cartItem->setQty($quantity); $this->validateCartItem($cartItem); + $this->cartItemRepository->save($cartItem); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php index 988ead7d86df3..13d9bb011d9b2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php @@ -70,6 +70,16 @@ public function testUpdateCartItemQuantity() $this->assertEquals($itemId, $item['id']); $this->assertEquals($quantity, $item['quantity']); + + $cartQuery = $this->getCartQuery($maskedQuoteId); + $response = $this->graphQlQuery($cartQuery); + + $this->assertArrayHasKey('cart', $response); + + $responseCart = $response['cart']; + $item = current($responseCart['items']); + + $this->assertEquals($quantity, $item['quantity']); } /** @@ -268,6 +278,26 @@ private function getQuery(string $maskedQuoteId, int $itemId, float $quantity): } } } +QUERY; + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery(string $maskedQuoteId) + { + return <<<QUERY +query { + cart(cart_id: "{$maskedQuoteId}"){ + items{ + product{ + name + } + quantity + } + } +} QUERY; } } From 13766411fd92f97cd2e0c4ebaaf3af16bda29985 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 14 Aug 2019 12:43:58 -0500 Subject: [PATCH 0377/2437] MC-18685: Remove custom layout updates from admin --- .../Adminhtml/Page/PostDataProcessor.php | 3 +- .../Page/CustomLayout/CustomLayoutManager.php | 3 +- app/code/Magento/Cms/Model/PageRepository.php | 2 +- .../Cms/Observer/PageValidatorObserver.php | 1 + .../Test/Unit/Model/PageRepositoryTest.php | 268 ------------------ app/code/Magento/Cms/etc/webapi.xml | 4 +- .../Magento/Webapi/Model/Config/Converter.php | 17 +- .../Magento/Webapi/Model/ServiceMetadata.php | 5 +- app/code/Magento/Webapi/Model/Soap/Config.php | 2 +- app/code/Magento/Webapi/etc/webapi_base.xsd | 1 - .../Magento/Cms/Api/PageRepositoryTest.php | 5 +- 11 files changed, 15 insertions(+), 296 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index 20c33a2927101..c46bcb8f247aa 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -12,8 +12,7 @@ use Magento\Framework\Config\Dom\ValidationSchemaException; /** - * Class PostDataProcessor - * @package Magento\Cms\Controller\Adminhtml\Page + * Controller helper for user input. */ class PostDataProcessor { diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php index 8d8dbe06a6078..ae9bda5d04f69 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -164,8 +164,7 @@ public function fetchAvailableFiles(int $pageId): array return array_filter( array_map( - function (File $file) use ($identifier) : ?string - { + function (File $file) use ($identifier) : ?string { preg_match( '/selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', $file->getName(), diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 2fc16e871cce7..09de8c7c086f6 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -273,7 +273,7 @@ private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( - 'Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor' + \Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor::class ); } return $this->collectionProcessor; diff --git a/app/code/Magento/Cms/Observer/PageValidatorObserver.php b/app/code/Magento/Cms/Observer/PageValidatorObserver.php index af0d30f450432..3c87c505cde60 100644 --- a/app/code/Magento/Cms/Observer/PageValidatorObserver.php +++ b/app/code/Magento/Cms/Observer/PageValidatorObserver.php @@ -34,6 +34,7 @@ public function __construct(SaveManager $saveManager) /** * @inheritDoc + * * @throws LocalizedException */ public function execute(Observer $observer) diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php deleted file mode 100644 index 61001794e2a0b..0000000000000 --- a/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php +++ /dev/null @@ -1,268 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Cms\Test\Unit\Model; - -use Magento\Cms\Model\PageRepository; -use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; - -/** - * Test for Magento\Cms\Model\PageRepository - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PageRepositoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var PageRepository - */ - protected $repository; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\ResourceModel\Page - */ - protected $pageResource; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\Page - */ - protected $page; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Api\Data\PageInterface - */ - protected $pageData; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Api\Data\PageSearchResultsInterface - */ - protected $pageSearchResult; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Api\DataObjectHelper - */ - protected $dataHelper; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Reflection\DataObjectProcessor - */ - protected $dataObjectProcessor; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\ResourceModel\Page\Collection - */ - protected $collection; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface - */ - private $storeManager; - - /** - * @var CollectionProcessorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $collectionProcessor; - - /** - * Initialize repository - */ - protected function setUp() - { - $this->pageResource = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dataObjectProcessor = $this->getMockBuilder(\Magento\Framework\Reflection\DataObjectProcessor::class) - ->disableOriginalConstructor() - ->getMock(); - $pageFactory = $this->getMockBuilder(\Magento\Cms\Model\PageFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $pageDataFactory = $this->getMockBuilder(\Magento\Cms\Api\Data\PageInterfaceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $pageSearchResultFactory = $this->getMockBuilder(\Magento\Cms\Api\Data\PageSearchResultsInterfaceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $collectionFactory = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page\CollectionFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $store->expects($this->any())->method('getId')->willReturn(0); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($store); - - $this->page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)->disableOriginalConstructor()->getMock(); - $this->pageData = $this->getMockBuilder(\Magento\Cms\Api\Data\PageInterface::class) - ->getMock(); - $this->pageSearchResult = $this->getMockBuilder(\Magento\Cms\Api\Data\PageSearchResultsInterface::class) - ->getMock(); - $this->collection = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page\Collection::class) - ->disableOriginalConstructor() - ->setMethods(['getSize', 'setCurPage', 'setPageSize', 'load', 'addOrder']) - ->getMock(); - - $pageFactory->expects($this->any()) - ->method('create') - ->willReturn($this->page); - $pageDataFactory->expects($this->any()) - ->method('create') - ->willReturn($this->pageData); - $pageSearchResultFactory->expects($this->any()) - ->method('create') - ->willReturn($this->pageSearchResult); - $collectionFactory->expects($this->any()) - ->method('create') - ->willReturn($this->collection); - /** - * @var \Magento\Cms\Model\PageFactory $pageFactory - * @var \Magento\Cms\Api\Data\PageInterfaceFactory $pageDataFactory - * @var \Magento\Cms\Api\Data\PageSearchResultsInterfaceFactory $pageSearchResultFactory - * @var \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $collectionFactory - */ - - $this->dataHelper = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->collectionProcessor = $this->getMockBuilder(CollectionProcessorInterface::class) - ->getMockForAbstractClass(); - - $this->repository = new PageRepository( - $this->pageResource, - $pageFactory, - $pageDataFactory, - $collectionFactory, - $pageSearchResultFactory, - $this->dataHelper, - $this->dataObjectProcessor, - $this->storeManager, - $this->collectionProcessor - ); - } - - /** - * @test - */ - public function testSave() - { - $this->pageResource->expects($this->once()) - ->method('save') - ->with($this->page) - ->willReturnSelf(); - $this->assertEquals($this->page, $this->repository->save($this->page)); - } - - /** - * @test - */ - public function testDeleteById() - { - $pageId = '123'; - - $this->page->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->page->expects($this->once()) - ->method('load') - ->with($pageId) - ->willReturnSelf(); - $this->pageResource->expects($this->once()) - ->method('delete') - ->with($this->page) - ->willReturnSelf(); - - $this->assertTrue($this->repository->deleteById($pageId)); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - */ - public function testSaveException() - { - $this->pageResource->expects($this->once()) - ->method('save') - ->with($this->page) - ->willThrowException(new \Exception()); - $this->repository->save($this->page); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\CouldNotDeleteException - */ - public function testDeleteException() - { - $this->pageResource->expects($this->once()) - ->method('delete') - ->with($this->page) - ->willThrowException(new \Exception()); - $this->repository->delete($this->page); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - */ - public function testGetByIdException() - { - $pageId = '123'; - - $this->page->expects($this->once()) - ->method('getId') - ->willReturn(false); - $this->page->expects($this->once()) - ->method('load') - ->with($pageId) - ->willReturnSelf(); - $this->repository->getById($pageId); - } - - /** - * @test - */ - public function testGetList() - { - $total = 10; - - /** @var \Magento\Framework\Api\SearchCriteriaInterface $criteria */ - $criteria = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaInterface::class)->getMock(); - - $this->collection->addItem($this->page); - $this->collection->expects($this->once()) - ->method('getSize') - ->willReturn($total); - - $this->collectionProcessor->expects($this->once()) - ->method('process') - ->with($criteria, $this->collection) - ->willReturnSelf(); - - $this->pageSearchResult->expects($this->once()) - ->method('setSearchCriteria') - ->with($criteria) - ->willReturnSelf(); - $this->pageSearchResult->expects($this->once()) - ->method('setTotalCount') - ->with($total) - ->willReturnSelf(); - $this->pageSearchResult->expects($this->once()) - ->method('setItems') - ->with([$this->page]) - ->willReturnSelf(); - $this->assertEquals($this->pageSearchResult, $this->repository->getList($criteria)); - } -} diff --git a/app/code/Magento/Cms/etc/webapi.xml b/app/code/Magento/Cms/etc/webapi.xml index 8cf0cb767d9be..5b66d0e3ed879 100644 --- a/app/code/Magento/Cms/etc/webapi.xml +++ b/app/code/Magento/Cms/etc/webapi.xml @@ -20,8 +20,8 @@ <resource ref="Magento_Cms::page"/> </resources> </route> - <route url="/V1/cmsPage" method="POST" soapService="Magento\Cms\Api\PageRepositoryInterface"> - <service class="Magento\Cms\Controller\Page\SaveManager" method="save"/> + <route url="/V1/cmsPage" method="POST"> + <service class="Magento\Cms\Api\PageRepositoryInterface" method="save"/> <resources> <resource ref="Magento_Cms::page"/> </resources> diff --git a/app/code/Magento/Webapi/Model/Config/Converter.php b/app/code/Magento/Webapi/Model/Config/Converter.php index 3ce79a83b176b..837a0f84423ad 100644 --- a/app/code/Magento/Webapi/Model/Config/Converter.php +++ b/app/code/Magento/Webapi/Model/Config/Converter.php @@ -50,22 +50,16 @@ public function convert($source) $service = $route->getElementsByTagName('service')->item(0); $serviceClass = $service->attributes->getNamedItem('class')->nodeValue; $serviceMethod = $service->attributes->getNamedItem('method')->nodeValue; - //WSDL method name which may differ from an actual method used. $soapMethod = $serviceMethod; if ($soapOperationNode = $route->attributes->getNamedItem('soapOperation')) { $soapMethod = trim($soapOperationNode->nodeValue); } - //WSDL class name which may differ from an actual class used. - $soapService = $serviceClass; - if ($soapServiceNode = $route->attributes->getNamedItem('soapService')) { - $soapService = trim($soapServiceNode->nodeValue); - } $url = trim($route->attributes->getNamedItem('url')->nodeValue); $version = $this->convertVersion($url); $serviceClassData = []; - if (isset($result[self::KEY_SERVICES][$soapService][$version])) { - $serviceClassData = $result[self::KEY_SERVICES][$soapService][$version]; + if (isset($result[self::KEY_SERVICES][$serviceClass][$version])) { + $serviceClassData = $result[self::KEY_SERVICES][$serviceClass][$version]; } $resources = $route->getElementsByTagName('resource'); @@ -116,18 +110,13 @@ public function convert($source) if (isset($serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE])) { $serviceSecure = $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE]; } - //Real method to use when performing operations. if (!isset($serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_REAL_SERVICE_METHOD])) { $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_REAL_SERVICE_METHOD] = $serviceMethod; } - //Real class to initialize when performing operations. - if (!isset($serviceClassData[self::KEY_METHODS][$soapMethod]['real_class'])) { - $serviceClassData[self::KEY_METHODS][$soapMethod]['real_class'] = $serviceClass; - } $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_SECURE] = $serviceSecure || $secure; $serviceClassData[self::KEY_METHODS][$soapMethod][self::KEY_DATA_PARAMETERS] = $serviceData; - $result[self::KEY_SERVICES][$soapService][$version] = $serviceClassData; + $result[self::KEY_SERVICES][$serviceClass][$version] = $serviceClassData; } return $result; } diff --git a/app/code/Magento/Webapi/Model/ServiceMetadata.php b/app/code/Magento/Webapi/Model/ServiceMetadata.php index d2bd08267128a..36f5819b03c98 100644 --- a/app/code/Magento/Webapi/Model/ServiceMetadata.php +++ b/app/code/Magento/Webapi/Model/ServiceMetadata.php @@ -118,15 +118,12 @@ protected function initServicesMetadata() $methods = []; foreach ($serviceData[Converter::KEY_METHODS] as $methodName => $methodMetadata) { $services[$serviceName][self::KEY_SERVICE_METHODS][$methodName] = [ - //Method to use for the operation. May differ from the operation's name. self::KEY_METHOD => $methodMetadata[Converter::KEY_REAL_SERVICE_METHOD], self::KEY_IS_REQUIRED => (bool)$methodMetadata[Converter::KEY_SECURE], self::KEY_IS_SECURE => $methodMetadata[Converter::KEY_SECURE], self::KEY_ACL_RESOURCES => $methodMetadata[Converter::KEY_ACL_RESOURCES], self::KEY_METHOD_ALIAS => $methodName, - self::KEY_ROUTE_PARAMS => $methodMetadata[Converter::KEY_DATA_PARAMETERS], - //Class to initialize for the operation. May differ from the operation's name. - 'real_class' => $methodMetadata['real_class'] + self::KEY_ROUTE_PARAMS => $methodMetadata[Converter::KEY_DATA_PARAMETERS] ]; $services[$serviceName][self::KEY_CLASS] = $serviceClass; $methods[] = $methodMetadata[Converter::KEY_REAL_SERVICE_METHOD]; diff --git a/app/code/Magento/Webapi/Model/Soap/Config.php b/app/code/Magento/Webapi/Model/Soap/Config.php index 94b3dad48b1ce..190280ff8f004 100644 --- a/app/code/Magento/Webapi/Model/Soap/Config.php +++ b/app/code/Magento/Webapi/Model/Soap/Config.php @@ -74,7 +74,7 @@ protected function getSoapOperations($requestedServices) foreach ($this->getRequestedSoapServices($requestedServices) as $serviceName => $serviceData) { foreach ($serviceData[ServiceMetadata::KEY_SERVICE_METHODS] as $methodData) { $method = $methodData[ServiceMetadata::KEY_METHOD]; - $class = $methodData['real_class']; + $class = $serviceData[ServiceMetadata::KEY_CLASS]; $operation = $methodData[ServiceMetadata::KEY_METHOD_ALIAS]; $operationName = $serviceName . ucfirst($operation); $this->soapOperations[$operationName] = [ diff --git a/app/code/Magento/Webapi/etc/webapi_base.xsd b/app/code/Magento/Webapi/etc/webapi_base.xsd index b38efae2a3435..7d1a5a14ba78f 100644 --- a/app/code/Magento/Webapi/etc/webapi_base.xsd +++ b/app/code/Magento/Webapi/etc/webapi_base.xsd @@ -30,7 +30,6 @@ <xs:attribute name="url" type="xs:string" use="required"/> <xs:attribute name="secure" type="xs:boolean"/> <xs:attribute name="soapOperation" type="xs:string"/> - <xs:attribute name="soapService" type="xs:string"/> </xs:complexType> <xs:complexType name="serviceType"> diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index 3470c507c4e5b..7456846cb2d2d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -422,7 +422,10 @@ public function testSaveDesign(): void $rules->setResources(['Magento_Cms::page']); $rules->saveRel(); //Using the admin user with custom role. - $token = $this->adminTokens->createAdminAccessToken('customRoleUser', \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD); + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); $id = 'test-cms-page'; $serviceInfo = [ From c235efb4e13e983ccacb36f95183595b8f09fcee Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 14 Aug 2019 13:17:30 -0500 Subject: [PATCH 0378/2437] MC-18685: Remove custom layout updates from admin --- .../{SaveManager.php => Authorization.php} | 25 +++------- app/code/Magento/Cms/Model/PageRepository.php | 40 ---------------- .../Magento/Cms/Observer/PageAclPlugin.php | 47 +++++++++++++++++++ .../Cms/Observer/PageValidatorObserver.php | 14 +++--- app/code/Magento/Cms/etc/webapi_rest/di.xml | 12 +++++ app/code/Magento/Cms/etc/webapi_soap/di.xml | 12 +++++ 6 files changed, 84 insertions(+), 66 deletions(-) rename app/code/Magento/Cms/Controller/Page/{SaveManager.php => Authorization.php} (79%) create mode 100644 app/code/Magento/Cms/Observer/PageAclPlugin.php create mode 100644 app/code/Magento/Cms/etc/webapi_rest/di.xml create mode 100644 app/code/Magento/Cms/etc/webapi_soap/di.xml diff --git a/app/code/Magento/Cms/Controller/Page/SaveManager.php b/app/code/Magento/Cms/Controller/Page/Authorization.php similarity index 79% rename from app/code/Magento/Cms/Controller/Page/SaveManager.php rename to app/code/Magento/Cms/Controller/Page/Authorization.php index bbd80b72a049d..f223a5c4d164c 100644 --- a/app/code/Magento/Cms/Controller/Page/SaveManager.php +++ b/app/code/Magento/Cms/Controller/Page/Authorization.php @@ -15,9 +15,9 @@ use Magento\Framework\Exception\LocalizedException; /** - * Manages CMS pages modification initiated by users. + * Authorization for saving a page. */ -class SaveManager +class Authorization { /** * @var PageRepositoryInterface @@ -42,27 +42,14 @@ public function __construct( } /** - * User saves a page (bew or existing). - * - * @param \Magento\Cms\Api\Data\PageInterface $page - * @return \Magento\Cms\Api\Data\PageInterface - * @throws LocalizedException - */ - public function save(\Magento\Cms\Api\Data\PageInterface $page): \Magento\Cms\Api\Data\PageInterface - { - $this->validatePage($page); - - return $this->pageRepository->save($page); - } - - /** - * Validate page data received from a user. + * Authorize user before updating a page. * * @param PageInterface $page * @return void - * @throws LocalizedException When validation failed. + * @throws AuthorizationException + * @throws LocalizedException When it is impossible to perform authorization for given page. */ - public function validatePage(\Magento\Cms\Api\Data\PageInterface $page): void + public function authorizeFor(PageInterface $page): void { //Validate design changes. if (!$this->authorization->isAllowed('Magento_Cms::save_design')) { diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 09de8c7c086f6..4492ea55da3a2 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -19,8 +19,6 @@ use Magento\Cms\Model\ResourceModel\Page as ResourcePage; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\AuthorizationInterface; -use Magento\Authorization\Model\UserContextInterface; /** * Class PageRepository @@ -73,16 +71,6 @@ class PageRepository implements PageRepositoryInterface */ private $collectionProcessor; - /** - * @var UserContextInterface - */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; - /** * @var IdentityMap */ @@ -125,34 +113,6 @@ public function __construct( $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); } - /** - * Get user context. - * - * @return UserContextInterface - */ - private function getUserContext(): UserContextInterface - { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); - } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); - } - - return $this->authorization; - } - /** * Save Page data * diff --git a/app/code/Magento/Cms/Observer/PageAclPlugin.php b/app/code/Magento/Cms/Observer/PageAclPlugin.php new file mode 100644 index 0000000000000..c8dc98f6f2807 --- /dev/null +++ b/app/code/Magento/Cms/Observer/PageAclPlugin.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Observer; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Controller\Page\Authorization; + +/** + * Perform additional authorization before saving a page. + */ +class PageAclPlugin +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * Authorize saving before it is executed. + * + * @param PageRepositoryInterface $subject + * @param PageInterface $page + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function beforeSave(PageRepositoryInterface $subject, PageInterface $page): array + { + $this->authorization->authorizeFor($page); + + return [$page]; + } +} diff --git a/app/code/Magento/Cms/Observer/PageValidatorObserver.php b/app/code/Magento/Cms/Observer/PageValidatorObserver.php index 3c87c505cde60..d245e9a519f1f 100644 --- a/app/code/Magento/Cms/Observer/PageValidatorObserver.php +++ b/app/code/Magento/Cms/Observer/PageValidatorObserver.php @@ -9,7 +9,7 @@ namespace Magento\Cms\Observer; use Magento\Cms\Api\Data\PageInterface; -use Magento\Cms\Controller\Page\SaveManager; +use Magento\Cms\Controller\Page\Authorization; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\LocalizedException; @@ -20,16 +20,16 @@ class PageValidatorObserver implements ObserverInterface { /** - * @var SaveManager + * @var Authorization */ - private $saveManager; + private $authorization; /** - * @param SaveManager $saveManager + * @param Authorization $authorization */ - public function __construct(SaveManager $saveManager) + public function __construct(Authorization $authorization) { - $this->saveManager = $saveManager; + $this->authorization = $authorization; } /** @@ -41,6 +41,6 @@ public function execute(Observer $observer) { /** @var PageInterface $page */ $page = $observer->getEvent()->getData('page'); - $this->saveManager->validatePage($page); + $this->authorization->authorizeFor($page); } } diff --git a/app/code/Magento/Cms/etc/webapi_rest/di.xml b/app/code/Magento/Cms/etc/webapi_rest/di.xml new file mode 100644 index 0000000000000..686305f2ed300 --- /dev/null +++ b/app/code/Magento/Cms/etc/webapi_rest/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Cms\Api\PageRepositoryInterface"> + <plugin name="aclCheck" type="Magento\Cms\Observer\PageAclPlugin" sortOrder="100" /> + </type> +</config> diff --git a/app/code/Magento/Cms/etc/webapi_soap/di.xml b/app/code/Magento/Cms/etc/webapi_soap/di.xml new file mode 100644 index 0000000000000..686305f2ed300 --- /dev/null +++ b/app/code/Magento/Cms/etc/webapi_soap/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Cms\Api\PageRepositoryInterface"> + <plugin name="aclCheck" type="Magento\Cms\Observer\PageAclPlugin" sortOrder="100" /> + </type> +</config> From 2fa2eb9d1314c7177a6a4bd8c9d19f90759fdf23 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 14 Aug 2019 14:59:56 -0500 Subject: [PATCH 0379/2437] MC-19161: Revert ENGCOM-2983 --- .../Magento/Mtf/TestSuite/InjectableTests/acceptance.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml index 6a8e2c615f847..d433f8c3fd06d 100644 --- a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml @@ -21,6 +21,7 @@ </allow> <deny> <tag group="stable" value="no" /> + <tag group="mftf_migrated" value="yes" /> </deny> </rule> </config> From 59e76d9a78e513730ac920cc785c52b9691796aa Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 15 Aug 2019 09:22:02 +0300 Subject: [PATCH 0380/2437] MC-19080: Incorrect behavior after shipping methods disabled --- .../testsuite/Magento/Ups/Model/CollectRatesTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php index 27323bf22f01e..3aab12df5f179 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php @@ -31,6 +31,7 @@ class CollectRatesTest extends \Magento\OfflineShipping\Model\CollectRatesTest * @magentoConfigFixture default_store carriers/ups/specificcountry UK * @magentoConfigFixture default_store carriers/ups/showmethod 1 */ + // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() { parent::testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable(); @@ -43,6 +44,7 @@ public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() * @magentoConfigFixture default_store carriers/ups/specificcountry UK * @magentoConfigFixture default_store carriers/ups/showmethod 1 */ + // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() { parent::testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable(); From 08bbdbf0246a09612f893c12587b3fae0d61efa7 Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Thu, 15 Aug 2019 14:24:11 +0300 Subject: [PATCH 0381/2437] MC-19080: Incorrect behavior after shipping methods disabled --- .../Model/CollectRatesTest.php | 79 +----------- .../Shipping/Model/CollectRatesTest.php | 114 ++++++++++++++++++ .../Magento/Ups/Model/CollectRatesTest.php | 2 +- 3 files changed, 120 insertions(+), 75 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php index c5e3ab1935a2e..910b4f570c360 100644 --- a/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php @@ -7,30 +7,12 @@ namespace Magento\OfflineShipping\Model; -use Magento\Framework\DataObject; -use Magento\Framework\ObjectManagerInterface; -use Magento\Quote\Model\Quote\Address\RateResult\Error; -use Magento\Quote\Model\Quote\Address\RateResult\Method; -use Magento\Shipping\Model\Rate\Result; -use Magento\Shipping\Model\Shipping; -use Magento\TestFramework\Helper\Bootstrap; - /** * Integration tests for offline shipping carriers. * @magentoAppIsolation enabled */ -class CollectRatesTest extends \PHPUnit\Framework\TestCase +class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesTest { - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Shipping - */ - protected $shipping; - /** * @var string */ @@ -41,28 +23,16 @@ class CollectRatesTest extends \PHPUnit\Framework\TestCase */ protected $errorMessage = 'This shipping method is not available. To use this shipping method, please contact us.'; - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->shipping = $this->objectManager->get(Shipping::class); - } - /** * @magentoConfigFixture default_store carriers/flatrate/active 1 * @magentoConfigFixture default_store carriers/flatrate/sallowspecific 1 * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 */ + // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() { - $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); - $rate = $this->getRate($result->getResult()); - - static::assertEquals($this->carrier, $rate->getData('carrier')); - static::assertEquals($this->errorMessage, $rate->getData('error_message')); + parent::testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable(); } /** @@ -71,48 +41,9 @@ public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() * @magentoConfigFixture default_store carriers/flatrate/specificcountry UK * @magentoConfigFixture default_store carriers/flatrate/showmethod 1 */ + // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() { - $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); - $rate = $this->getRate($result->getResult()); - - static::assertNull($rate); - } - - /** - * @return DataObject - */ - private function getAddress(): DataObject - { - $address = $this->objectManager->create( - DataObject::class, - [ - 'data' => [ - 'region_id' => 'CA', - 'postcode' => '11111', - 'lastname' => 'John', - 'firstname' => 'Doe', - 'street' => 'Some street', - 'city' => 'Los Angeles', - 'email' => 'john.doe@example.com', - 'telephone' => '11111111', - 'country_id' => 'US', - 'item_qty' => 1, - ], - ] - ); - - return $address; - } - - /** - * @param Result $result - * @return Method|Error - */ - private function getRate(Result $result) - { - $rates = $result->getAllRates(); - - return array_pop($rates); + parent::testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable(); } } diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php new file mode 100644 index 0000000000000..a5994cc604a65 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Model; + +use Magento\Framework\DataObject; +use Magento\Framework\ObjectManagerInterface; +use Magento\Quote\Model\Quote\Address\RateResult\Error; +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Integration tests for shipping carriers. + */ +class CollectRatesTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Shipping + */ + protected $shipping; + + /** + * @var string + */ + protected $carrier = ''; + + /** + * @var string + */ + protected $errorMessage = ''; + + /** + * @inheritdoc + */ + protected function setUp() + { + if (empty($this->carrier) || empty($this->errorMessage)) { + $this->markTestSkipped('Required fields are empty'); + } + $this->objectManager = Bootstrap::getObjectManager(); + $this->shipping = $this->objectManager->get(Shipping::class); + } + + /** + * @return void + */ + public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() + { + $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); + $rate = $this->getRate($result->getResult()); + + static::assertEquals($this->carrier, $rate->getData('carrier')); + static::assertEquals($this->errorMessage, $rate->getData('error_message')); + } + + /** + * @return void + */ + public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() + { + $result = $this->shipping->collectRatesByAddress($this->getAddress(), $this->carrier); + $rate = $this->getRate($result->getResult()); + + static::assertNull($rate); + } + + /** + * @return DataObject + */ + private function getAddress(): DataObject + { + $address = $this->objectManager->create( + DataObject::class, + [ + 'data' => [ + 'region_id' => 'CA', + 'postcode' => '11111', + 'lastname' => 'John', + 'firstname' => 'Doe', + 'street' => 'Some street', + 'city' => 'Los Angeles', + 'email' => 'john.doe@example.com', + 'telephone' => '11111111', + 'country_id' => 'US', + 'item_qty' => 1, + ], + ] + ); + + return $address; + } + + /** + * @param Result $result + * @return Method|Error + */ + private function getRate(Result $result) + { + $rates = $result->getAllRates(); + + return array_pop($rates); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php index 3aab12df5f179..58f31836a310a 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php @@ -11,7 +11,7 @@ * Integration tests for online shipping carriers. * @magentoAppIsolation enabled */ -class CollectRatesTest extends \Magento\OfflineShipping\Model\CollectRatesTest +class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesTest { /** * @var string From c6cc4b23b4b357adc925d6ae6ded2b43bf37d791 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 15 Aug 2019 17:58:49 +0300 Subject: [PATCH 0382/2437] MC-19044: [FT] Magento\Downloadable\Test\TestCase\CreateDownloadableProductEntityTest fails on Jenkins --- .../CreateDownloadableProductEntityTest.php | 24 ++++++++++++++++++- .../TestSuite/InjectableTests/acceptance.xml | 2 ++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.php b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.php index 496b7a280ca18..de71cdff7ae4c 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/CreateDownloadableProductEntityTest.php @@ -11,6 +11,7 @@ use Magento\Catalog\Test\Page\Adminhtml\CatalogProductNew; use Magento\Downloadable\Test\Fixture\DownloadableProduct; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\Util\Command\Cli\EnvWhitelist; /** * Steps: @@ -53,6 +54,13 @@ class CreateDownloadableProductEntityTest extends Injectable */ protected $catalogProductNew; + /** + * DomainWhitelist CLI + * + * @var EnvWhitelist + */ + private $envWhitelist; + /** * Persist category * @@ -73,16 +81,19 @@ public function __prepare(Category $category) * @param Category $category * @param CatalogProductIndex $catalogProductIndexNewPage * @param CatalogProductNew $catalogProductNewPage + * @param EnvWhitelist $envWhitelist * @return void */ public function __inject( Category $category, CatalogProductIndex $catalogProductIndexNewPage, - CatalogProductNew $catalogProductNewPage + CatalogProductNew $catalogProductNewPage, + EnvWhitelist $envWhitelist ) { $this->category = $category; $this->catalogProductIndex = $catalogProductIndexNewPage; $this->catalogProductNew = $catalogProductNewPage; + $this->envWhitelist = $envWhitelist; } /** @@ -95,10 +106,21 @@ public function __inject( public function test(DownloadableProduct $product, Category $category) { // Steps + $this->envWhitelist->addHost('example.com'); $this->catalogProductIndex->open(); $this->catalogProductIndex->getGridPageActionBlock()->addProduct('downloadable'); $productBlockForm = $this->catalogProductNew->getProductForm(); $productBlockForm->fill($product, null, $category); $this->catalogProductNew->getFormPageActions()->save(); } + + /** + * Clean data after running test. + * + * @return void + */ + protected function tearDown() + { + $this->envWhitelist->removeHost('example.com'); + } } diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml index 6a8e2c615f847..2eae769416c29 100644 --- a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/acceptance.xml @@ -13,6 +13,7 @@ </allow> <deny> <tag group="stable" value="no" /> + <tag group="mftf_migrated" value="yes" /> </deny> </rule> <rule scope="variation"> @@ -21,6 +22,7 @@ </allow> <deny> <tag group="stable" value="no" /> + <tag group="mftf_migrated" value="yes" /> </deny> </rule> </config> From f73e7a464461c0705423760c885728165eeafae6 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 15 Aug 2019 12:13:36 -0500 Subject: [PATCH 0383/2437] MC-18685: Remove custom layout updates from admin --- .../Adminhtml/Product/Authorization.php | 88 +++++++++++ .../Controller/Adminhtml/Product/Builder.php | 3 + .../Product/Initialization/Helper.php | 15 +- app/code/Magento/Catalog/Model/Product.php | 56 ------- .../Catalog/Plugin/ProductAuthorization.php | 49 ++++++ .../Magento/Catalog/etc/webapi_rest/di.xml | 3 + .../Magento/Catalog/etc/webapi_soap/di.xml | 3 + .../Api/ProductRepositoryInterfaceTest.php | 141 +++++++++++++++++- .../Magento/Cms/Api/PageRepositoryTest.php | 1 - .../Controller/Adminhtml/ProductTest.php | 99 +++++++++++- 10 files changed, 390 insertions(+), 68 deletions(-) create mode 100644 app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php create mode 100644 app/code/Magento/Catalog/Plugin/ProductAuthorization.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php new file mode 100644 index 0000000000000..d49c499930022 --- /dev/null +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductFactory; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Additional authorization for product operations. + */ +class Authorization +{ + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var ProductFactory + */ + private $productFactory; + + /** + * @param AuthorizationInterface $authorization + * @param ProductFactory $factory + */ + public function __construct(AuthorizationInterface $authorization, ProductFactory $factory) + { + $this->authorization = $authorization; + $this->productFactory = $factory; + } + + /** + * Authorize saving of a product. + * + * @throws AuthorizationException + * @throws NoSuchEntityException When product with invalid ID given. + * @param ProductInterface|Product $product + * @return void + */ + public function authorizeSavingOf(ProductInterface $product): void + { + if (!$this->authorization->isAllowed('Magento_Catalog::edit_product_design')) { + $notAllowed = false; + if (!$product->getId()) { + if ($product->getData('custom_design') + || $product->getData('page_layout') + || $product->getData('options_container') + || $product->getData('custom_layout_update') + || $product->getData('custom_design_from') + || $product->getData('custom_design_to') + ) { + $notAllowed = true; + } + } else { + /** @var Product $savedProduct */ + $savedProduct = $this->productFactory->create(); + $savedProduct->load($product->getId()); + if ($savedProduct->getSku()) { + throw NoSuchEntityException::singleField('id', $product->getId()); + } + if ($product->getData('custom_design') != $savedProduct->getData('custom_design') + || $product->getData('page_layout') != $savedProduct->getData('page_layout') + || $product->getData('options_container') != $savedProduct->getData('options_container') + || $product->getData('custom_layout_update') != $savedProduct->getData('custom_layout_update') + || $product->getData('custom_design_from') != $savedProduct->getData('custom_design_from') + || $product->getData('custom_design_to') != $savedProduct->getData('custom_design_to') + ) { + $notAllowed = true; + } + } + + if ($notAllowed) { + throw new AuthorizationException(__('Not allowed to edit the product\'s design attributes')); + } + } + } +} diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php index 78ad9f423871f..1f959a22b1ba1 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php @@ -115,6 +115,9 @@ public function build(RequestInterface $request): ProductInterface $store = $this->storeFactory->create(); $store->load($storeId); + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + $this->registry->unregister('current_store'); $this->registry->register('product', $product); $this->registry->register('current_product', $product); $this->registry->register('current_store', $store); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index f11d16755ef0d..2494412326075 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -17,6 +17,7 @@ use Magento\Catalog\Model\Product\LinkTypeProvider; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; +use Magento\Catalog\Controller\Adminhtml\Product\Authorization as ProductAuthorization; /** * Product helper @@ -96,6 +97,11 @@ class Helper */ private $attributeFilter; + /** + * @var ProductAuthorization + */ + private $productAuthorization; + /** * Constructor * @@ -123,7 +129,8 @@ public function __construct( ProductLinkFactory $productLinkFactory = null, ProductRepositoryInterface $productRepository = null, LinkTypeProvider $linkTypeProvider = null, - AttributeFilter $attributeFilter = null + AttributeFilter $attributeFilter = null, + ?ProductAuthorization $productAuthorization = null ) { $this->request = $request; $this->storeManager = $storeManager; @@ -138,6 +145,7 @@ public function __construct( $this->productRepository = $productRepository ?: $objectManager->get(ProductRepositoryInterface::class); $this->linkTypeProvider = $linkTypeProvider ?: $objectManager->get(LinkTypeProvider::class); $this->attributeFilter = $attributeFilter ?: $objectManager->get(AttributeFilter::class); + $this->productAuthorization = $productAuthorization ?? $objectManager->get(ProductAuthorization::class); } /** @@ -228,7 +236,10 @@ public function initializeFromData(\Magento\Catalog\Model\Product $product, arra public function initialize(\Magento\Catalog\Model\Product $product) { $productData = $this->request->getPost('product', []); - return $this->initializeFromData($product, $productData); + $product = $this->initializeFromData($product, $productData); + $this->productAuthorization->authorizeSavingOf($product); + + return $product; } /** diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index fc9fffb2a7e9a..93f5da70d40ea 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -5,7 +5,6 @@ */ namespace Magento\Catalog\Model; -use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface; use Magento\Catalog\Api\Data\ProductInterface; @@ -15,7 +14,6 @@ use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; -use Magento\Framework\AuthorizationInterface; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\SaleableInterface; @@ -355,16 +353,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements */ private $filterCustomAttribute; - /** - * @var UserContextInterface - */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; - /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -872,34 +860,6 @@ public function getAttributes($groupId = null, $skipSuper = false) return $attributes; } - /** - * Get user context. - * - * @return UserContextInterface - */ - private function getUserContext(): UserContextInterface - { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); - } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); - } - - return $this->authorization; - } - /** * Check product options and type options and save them, too * @@ -917,22 +877,6 @@ public function beforeSave() $this->getTypeInstance()->beforeSave($this); - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Catalog::edit_product_design') - ) { - $this->setData('custom_design', $this->getOrigData('custom_design')); - $this->setData('page_layout', $this->getOrigData('page_layout')); - $this->setData('options_container', $this->getOrigData('options_container')); - $this->setData('custom_layout_update', $this->getOrigData('custom_layout_update')); - $this->setData('custom_design_from', $this->getOrigData('custom_design_from')); - $this->setData('custom_design_to', $this->getOrigData('custom_design_to')); - } - $hasOptions = false; $hasRequiredOptions = false; diff --git a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php new file mode 100644 index 0000000000000..2de7c1d5bc681 --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Plugin; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Controller\Adminhtml\Product\Authorization; +use Magento\Framework\Exception\LocalizedException; + +/** + * Perform additional authorization for product operations. + */ +class ProductAuthorization +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * Authorize saving of a product. + * + * @param ProductRepositoryInterface $subject + * @param ProductInterface $product + * @param bool $saveOptions + * @throws LocalizedException + * @return array + */ + public function beforeSave(ProductRepositoryInterface $subject, ProductInterface $product, $saveOptions): array + { + $this->authorization->authorizeSavingOf($product); + + return [$product, $saveOptions]; + } +} diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index 44cdd473bf74e..4a7d2c7481576 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -22,4 +22,7 @@ <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="product_authorization" type="Magento\Catalog\Plugin\ProductAuthorization" /> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index 44cdd473bf74e..4a7d2c7481576 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -22,4 +22,7 @@ <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="product_authorization" type="Magento\Catalog\Plugin\ProductAuthorization" /> + </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 3e935e1d7ae9b..58ad18dcaf29c 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -7,9 +7,14 @@ namespace Magento\Catalog\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Downloadable\Model\Link; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\Store\Model\Store; use Magento\Store\Model\Website; use Magento\Store\Model\WebsiteRepository; @@ -55,6 +60,33 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ], ]; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_related.php */ @@ -726,9 +758,10 @@ public function testUpdateWithExtensionAttributes(): void /** * @param array $product + * @param string|null $token * @return array|bool|float|int|string */ - protected function updateProduct($product) + protected function updateProduct($product, ?string $token = null) { if (isset($product['custom_attributes'])) { for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { @@ -755,6 +788,9 @@ protected function updateProduct($product) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['product' => $product]; $response = $this->_webApiCall($serviceInfo, $requestData); return $response; @@ -1135,7 +1171,6 @@ protected function getSimpleProductData($productData = []) ProductInterface::TYPE_ID => 'simple', ProductInterface::PRICE => 3.62, ProductInterface::STATUS => 1, - ProductInterface::TYPE_ID => 'simple', ProductInterface::ATTRIBUTE_SET_ID => 4, 'custom_attributes' => [ ['attribute_code' => 'cost', 'value' => ''], @@ -1147,9 +1182,10 @@ protected function getSimpleProductData($productData = []) /** * @param $product * @param string|null $storeCode + * @param string|null $token * @return mixed */ - protected function saveProduct($product, $storeCode = null) + protected function saveProduct($product, $storeCode = null, ?string $token = null) { if (isset($product['custom_attributes'])) { for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { @@ -1171,6 +1207,9 @@ protected function saveProduct($product, $storeCode = null) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['product' => $product]; return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode); } @@ -1582,4 +1621,100 @@ private function assertMultiselectValue($productSku, $multiselectAttributeCode, } $this->assertEquals($expectedMultiselectValue, $multiselectValue); } + + /** + * Test design settings authorization + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving products but not their design settings. + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load('test_custom_role', 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products']); + $rules->saveRel(); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $productData = $this->getSimpleProductData(); + $productData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => '1']; + + //Creating new product with design settings. + $exceptionMessage = null; + try { + $this->saveProduct($productData, null, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have the permissions. + $this->assertEquals('Not allowed to edit the product\'s design attributes', $exceptionMessage); + + //Updating the user role to allow access to design properties. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products', 'Magento_Catalog::edit_product_design']); + $rules->saveRel(); + //Making the same request with design settings. + $result = $this->saveProduct($productData, null, $token); + $this->assertArrayHasKey('id', $result); + //Product must be saved. + $productSaved = $this->getProduct($productData[ProductInterface::SKU]); + $savedCustomDesign = null; + foreach ($productSaved['custom_attributes'] as $customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $savedCustomDesign = $customAttribute['value']; + } + } + $this->assertEquals('1', $savedCustomDesign); + $productData = $productSaved; + + //Updating our role to remove design properties access. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products']); + $rules->saveRel(); + //Updating the product but with the same design properties values. + $result = $this->updateProduct($productData, $token); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + + //Changing a design property. + foreach ($productData['custom_attributes'] as &$customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $customAttribute['value'] = '2'; + } + } + $exceptionMessage = null; + try { + $this->updateProduct($productData, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have permissions to do that. + $this->assertEquals('Not allowed to edit the product\'s design attributes', $exceptionMessage); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index 7456846cb2d2d..66cb91e528dff 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -490,7 +490,6 @@ public function testSaveDesign(): void $result = $this->_webApiCall($serviceInfo, $requestData); //We haven't changed the design so operation is successful. $this->assertArrayHasKey('id', $result); - //Changing a design property. $requestData['page'][PageInterface::CUSTOM_THEME] = 2; $exceptionMessage = null; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index acec996d0c406..547b282474fe6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -5,17 +5,41 @@ */ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ProductRepositoryFactory; use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Helper\Bootstrap; /** * @magentoAppArea adminhtml */ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendController { + /** + * @var Builder + */ + private $aclBuilder; + + /** + * @var ProductRepositoryFactory + */ + private $repositoryFactory; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->repositoryFactory = Bootstrap::getObjectManager()->get(ProductRepositoryFactory::class); + } + /** * Test calling save with invalid product's ID. */ @@ -39,7 +63,8 @@ public function testSaveActionWithDangerRequest() public function testSaveActionAndNew() { $this->getRequest()->setPostValue(['back' => 'new']); - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); @@ -59,7 +84,8 @@ public function testSaveActionAndNew() public function testSaveActionAndDuplicate() { $this->getRequest()->setPostValue(['back' => 'duplicate']); - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); @@ -130,7 +156,8 @@ public function testIndexAction() */ public function testEditAction() { - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->dispatch('backend/catalog/product/edit/id/' . $product->getEntityId()); $body = $this->getResponse()->getBody(); @@ -349,10 +376,70 @@ public function saveActionTierPriceDataProvider() */ private function getProductData(array $tierPrice) { - $productRepositoryInterface = $this->_objectManager->get(ProductRepositoryInterface::class); - $product = $productRepositoryInterface->get('tier_prices')->getData(); + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('tier_prices')->getData(); $product['tier_price'] = $tierPrice; unset($product['entity_id']); return $product; } + + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation enabled + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + $requestData = [ + 'product' => [ + 'type' => 'simple', + 'sku' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'product' => [], + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]' + ] + ]; + $uri = 'backend/catalog/product/save'; + + //Trying to update product's design settings without proper permissions. + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['Not allowed to edit the product\'s design attributes']; + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); + $requestData['product']['custom_design'] = '1'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $this->aclBuilder->getAcl()->allow(null, ['Magento_Catalog::products', 'Magento_Catalog::edit_product_design']); + $this->getRequest()->setDispatched(false); + $this->dispatch($uri); + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertNotEmpty($product->getCustomDesign()); + $this->assertEquals(1, $product->getCustomDesign()); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } } From 2dbf838540bf9bc0efbad683eb5b743632877bbe Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Thu, 15 Aug 2019 13:00:54 -0500 Subject: [PATCH 0384/2437] MC-19194: UpdateCartItems mutation does not update cart item quantity - fix performance issue --- .../QuoteGraphQl/Model/Cart/UpdateCartItem.php | 5 +---- .../QuoteGraphQl/Model/Resolver/UpdateCartItems.php | 12 +++++++++++- .../GraphQl/Quote/Guest/UpdateCartItemsTest.php | 10 ++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php index b18c6ad662335..7b5c9a57a7be9 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartItem.php @@ -93,8 +93,6 @@ public function execute(Quote $cart, int $cartItemId, float $quantity, array $cu ) ); } - - $this->quoteRepository->save($cart); } /** @@ -105,7 +103,7 @@ public function execute(Quote $cart, int $cartItemId, float $quantity, array $cu * @param float $quantity * @throws GraphQlNoSuchEntityException * @throws NoSuchEntityException - * @throws GraphQlNoSuchEntityException + * @throws GraphQlInputException */ private function updateItemQuantity(int $itemId, Quote $cart, float $quantity) { @@ -117,7 +115,6 @@ private function updateItemQuantity(int $itemId, Quote $cart, float $quantity) } $cartItem->setQty($quantity); $this->validateCartItem($cartItem); - $this->cartItemRepository->save($cartItem); } /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php index 8066c28e9e48a..241237613b94e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php @@ -15,6 +15,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\CartItemRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\QuoteGraphQl\Model\Cart\UpdateCartItem; @@ -39,19 +40,27 @@ class UpdateCartItems implements ResolverInterface */ private $cartItemRepository; + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + /** * @param GetCartForUser $getCartForUser * @param CartItemRepositoryInterface $cartItemRepository * @param UpdateCartItem $updateCartItem + * @param CartRepositoryInterface $cartRepository */ public function __construct( GetCartForUser $getCartForUser, CartItemRepositoryInterface $cartItemRepository, - UpdateCartItem $updateCartItem + UpdateCartItem $updateCartItem, + CartRepositoryInterface $cartRepository ) { $this->getCartForUser = $getCartForUser; $this->cartItemRepository = $cartItemRepository; $this->updateCartItem = $updateCartItem; + $this->cartRepository = $cartRepository; } /** @@ -76,6 +85,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value try { $this->processCartItems($cart, $cartItems); + $this->cartRepository->save($cart); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); } catch (LocalizedException $e) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php index 13d9bb011d9b2..6ac683ef77ade 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php @@ -71,6 +71,7 @@ public function testUpdateCartItemQuantity() $this->assertEquals($itemId, $item['id']); $this->assertEquals($quantity, $item['quantity']); + //Check that update is correctly reflected in cart $cartQuery = $this->getCartQuery($maskedQuoteId); $response = $this->graphQlQuery($cartQuery); @@ -101,6 +102,15 @@ public function testRemoveCartItemIfQuantityIsZero() $responseCart = $response['updateCartItems']['cart']; $this->assertCount(0, $responseCart['items']); + + //Check that update is correctly reflected in cart + $cartQuery = $this->getCartQuery($maskedQuoteId); + $response = $this->graphQlQuery($cartQuery); + + $this->assertArrayHasKey('cart', $response); + + $responseCart = $response['cart']; + $this->assertCount(0, $responseCart['items']); } /** From 98efddb24cc787d94113b9c5a83dae4a5771a9f2 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Thu, 15 Aug 2019 13:50:28 -0500 Subject: [PATCH 0385/2437] MQE-1702: Bump MFTF version in Magento - MFTF Version Bump --- composer.json | 2 +- composer.lock | 74 +++++++++++++++++++++++++-------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index 60f52e8ebc0bc..8b2cb0b53972a 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "~3.0.0", - "magento/magento2-functional-testing-framework": "2.4.3", + "magento/magento2-functional-testing-framework": "2.4.4", "pdepend/pdepend": "2.5.2", "phpmd/phpmd": "@stable", "phpunit/phpunit": "~6.5.0", diff --git a/composer.lock b/composer.lock index 675848c495915..8af9ead67d9c1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b5c5eeedfc8a724202af911b637f7b16", + "content-hash": "8819d140d0951fefd8e14b052ff0f61a", "packages": [ { "name": "braintree/braintree_php", @@ -1440,13 +1440,13 @@ }, { "name": "John Kelly", - "email": "johnmkelly86@gmail.com", - "role": "Maintainer" + "role": "Maintainer", + "email": "johnmkelly86@gmail.com" }, { "name": "Raúl Araya", - "email": "nubeiro@gmail.com", - "role": "Maintainer" + "role": "Maintainer", + "email": "nubeiro@gmail.com" } ], "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", @@ -1552,28 +1552,28 @@ "authors": [ { "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" + "role": "Lead Developer", + "email": "terrafrost@php.net" }, { "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" + "role": "Developer", + "email": "pm@datasphere.ch" }, { "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" + "role": "Developer", + "email": "bantu@phpbb.com" }, { "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" + "role": "Developer", + "email": "petrich@tronic-media.com" }, { "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" + "role": "Developer", + "email": "graham@alt-three.com" } ], "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", @@ -2402,7 +2402,7 @@ }, { "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "email": "BackEndTea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -4912,8 +4912,8 @@ "authors": [ { "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" + "role": "Developer", + "email": "vania-pooh@yandex-team.ru" } ], "description": "A Codeception adapter for Allure report.", @@ -4967,8 +4967,8 @@ "authors": [ { "name": "Ivan Krutov", - "email": "vania-pooh@yandex-team.ru", - "role": "Developer" + "role": "Developer", + "email": "vania-pooh@yandex-team.ru" } ], "description": "PHP API for Allure adapter", @@ -6697,9 +6697,9 @@ "authors": [ { "name": "Phil Bennett", + "role": "Developer", "email": "philipobenito@gmail.com", - "homepage": "http://www.philipobenito.com", - "role": "Developer" + "homepage": "http://www.philipobenito.com" } ], "description": "A fast and intuitive dependency injection container.", @@ -6814,16 +6814,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.4.3", + "version": "2.4.4", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "9e9a20fd4c77833ef41ac07eb076a7f2434ce61c" + "reference": "ab347cf23d01f6bb9d158ab37d2dc56594999beb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/9e9a20fd4c77833ef41ac07eb076a7f2434ce61c", - "reference": "9e9a20fd4c77833ef41ac07eb076a7f2434ce61c", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ab347cf23d01f6bb9d158ab37d2dc56594999beb", + "reference": "ab347cf23d01f6bb9d158ab37d2dc56594999beb", "shasum": "" }, "require": { @@ -6885,7 +6885,7 @@ "magento", "testing" ], - "time": "2019-08-02T14:26:18+00:00" + "time": "2019-08-15T14:46:36+00:00" }, { "name": "mikey179/vfsstream", @@ -6925,8 +6925,8 @@ "authors": [ { "name": "Frank Kleine", - "homepage": "http://frankkleine.de/", - "role": "Developer" + "role": "Developer", + "homepage": "http://frankkleine.de/" } ], "description": "Virtual file system to mock the real file system in unit tests.", @@ -7700,8 +7700,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", @@ -7970,8 +7970,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "The PHP Unit Testing framework.", @@ -8545,8 +8545,8 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "role": "lead", + "email": "sebastian@phpunit.de" } ], "description": "Copy/Paste Detector (CPD) for PHP code.", @@ -9542,8 +9542,8 @@ "authors": [ { "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "lead" + "role": "lead", + "email": "arne@blankerts.de" } ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", From b9d468ceb37a9ef32b6ca60e88feac6711e3bb32 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 15 Aug 2019 14:03:32 -0500 Subject: [PATCH 0386/2437] MC-19145: [CLOUD] Internal error after DHL was configured --- app/code/Magento/Dhl/Model/Carrier.php | 56 ++++++++++--------- app/code/Magento/Ups/Model/Carrier.php | 46 +++++++-------- app/code/Magento/Usps/Model/Carrier.php | 46 +++++++-------- .../Async/ProxyDeferredFactoryTest.php | 31 +++++----- 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 5959294fe6dc7..5d96d4bcbf43a 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -10,7 +10,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\Async\CallbackDeferred; -use Magento\Framework\Async\ProxyDeferredFactory; use Magento\Framework\HTTP\AsyncClient\HttpResponseDeferredInterface; use Magento\Framework\HTTP\AsyncClient\Request; use Magento\Framework\HTTP\AsyncClientInterface; @@ -21,6 +20,7 @@ use Magento\Quote\Model\Quote\Address\RateResult\Error; use Magento\Shipping\Model\Carrier\AbstractCarrier; use Magento\Shipping\Model\Rate\Result; +use Magento\Shipping\Model\Rate\Result\ProxyDeferredFactory; use Magento\Framework\Xml\Security; use Magento\Dhl\Model\Validator\XmlValidator; @@ -389,16 +389,17 @@ public function collectRates(RateRequest $request) //Saving $result to use proper result with the callback $this->_result = $result = $this->_getQuotes(); //After quotes are loaded parsing the response. - return $this->proxyDeferredFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($request, $result) { - $this->_result = $result; - $this->_updateFreeMethodQuote($request); - - return $this->_result; - } - ) + return $this->proxyDeferredFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($request, $result) { + $this->_result = $result; + $this->_updateFreeMethodQuote($request); + + return $this->_result; + } + ) + ] ); } @@ -1057,23 +1058,24 @@ protected function _getQuotes() } } - return $this->proxyDeferredFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($deferredResponses, $responseBodies) { - //Loading rates not found in cache - foreach ($deferredResponses as $deferredResponseData) { - $responseBodies[] = [ - 'body' => $deferredResponseData['deferred']->get()->getBody(), - 'date' => $deferredResponseData['date'], - 'request' => $deferredResponseData['request'], - 'from_cache' => false - ]; - } + return $this->proxyDeferredFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($deferredResponses, $responseBodies) { + //Loading rates not found in cache + foreach ($deferredResponses as $deferredResponseData) { + $responseBodies[] = [ + 'body' => $deferredResponseData['deferred']->get()->getBody(), + 'date' => $deferredResponseData['date'], + 'request' => $deferredResponseData['request'], + 'from_cache' => false + ]; + } - return $this->processQuotesResponses($responseBodies); - } - ) + return $this->processQuotesResponses($responseBodies); + } + ) + ] ); } diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index 5320aeb5bcc8a..72b68c476d88a 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -9,7 +9,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Async\CallbackDeferred; -use Magento\Framework\Async\ProxyDeferredFactory; use Magento\Framework\DataObject; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\HTTP\AsyncClient\HttpResponseDeferredInterface; @@ -22,6 +21,7 @@ use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; use Magento\Shipping\Model\Carrier\CarrierInterface; use Magento\Shipping\Model\Rate\Result; +use Magento\Shipping\Model\Rate\Result\ProxyDeferredFactory; use Magento\Shipping\Model\Simplexml\Element; use Magento\Ups\Helper\Config; use Magento\Shipping\Model\Shipment\Request as Shipment; @@ -239,15 +239,16 @@ public function collectRates(RateRequest $request) //To use the correct result in the callback. $this->_result = $result = $this->_getQuotes(); - return $this->deferredProxyFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($request, $result) { - $this->_result = $result; - $this->_updateFreeMethodQuote($request); - return $this->getResult(); - } - ) + return $this->deferredProxyFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($request, $result) { + $this->_result = $result; + $this->_updateFreeMethodQuote($request); + return $this->getResult(); + } + ) + ] ); } @@ -782,19 +783,20 @@ protected function _getXmlQuotes() new Request($url, Request::METHOD_POST, ['Content-Type' => 'application/xml'], $xmlRequest) ); - return $this->deferredProxyFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($httpResponse) { - if ($httpResponse->get()->getStatusCode() >= 400) { - $xmlResponse = ''; - } else { - $xmlResponse = $httpResponse->get()->getBody(); - } + return $this->deferredProxyFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($httpResponse) { + if ($httpResponse->get()->getStatusCode() >= 400) { + $xmlResponse = ''; + } else { + $xmlResponse = $httpResponse->get()->getBody(); + } - return $this->_parseXmlResponse($xmlResponse); - } - ) + return $this->_parseXmlResponse($xmlResponse); + } + ) + ] ); } diff --git a/app/code/Magento/Usps/Model/Carrier.php b/app/code/Magento/Usps/Model/Carrier.php index 7136a403003df..1c8ff0ce9efa9 100644 --- a/app/code/Magento/Usps/Model/Carrier.php +++ b/app/code/Magento/Usps/Model/Carrier.php @@ -8,7 +8,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Async\CallbackDeferred; -use Magento\Framework\Async\ProxyDeferredFactory; use Magento\Framework\HTTP\AsyncClient\Request; use Magento\Framework\HTTP\AsyncClientInterface; use Magento\Framework\Xml\Security; @@ -16,6 +15,7 @@ use Magento\Shipping\Helper\Carrier as CarrierHelper; use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; use Magento\Shipping\Model\Rate\Result; +use Magento\Shipping\Model\Rate\Result\ProxyDeferredFactory; use Magento\Usps\Helper\Data as DataHelper; /** @@ -239,16 +239,17 @@ public function collectRates(RateRequest $request) //Saving current result to use the right one in the callback. $this->_result = $result = $this->_getQuotes(); - return $this->proxyDeferredFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($request, $result) { - $this->_result = $result; - $this->_updateFreeMethodQuote($request); + return $this->proxyDeferredFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($request, $result) { + $this->_result = $result; + $this->_updateFreeMethodQuote($request); - return $this->getResult(); - } - ) + return $this->getResult(); + } + ) + ] ); } @@ -555,18 +556,19 @@ protected function _getXmlQuotes() ) ); - return $this->proxyDeferredFactory->createFor( - Result::class, - new CallbackDeferred( - function () use ($deferredResponse, $request, $debugData) { - $responseBody = $deferredResponse->get()->getBody(); - $debugData['result'] = $responseBody; - $this->_setCachedQuotes($request, $responseBody); - $this->_debug($debugData); - - return $this->_parseXmlResponse($responseBody); - } - ) + return $this->proxyDeferredFactory->create( + [ + 'deferred' => new CallbackDeferred( + function () use ($deferredResponse, $request, $debugData) { + $responseBody = $deferredResponse->get()->getBody(); + $debugData['result'] = $responseBody; + $this->_setCachedQuotes($request, $responseBody); + $this->_debug($debugData); + + return $this->_parseXmlResponse($responseBody); + } + ) + ] ); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Async/ProxyDeferredFactoryTest.php b/dev/tests/integration/testsuite/Magento/Framework/Async/ProxyDeferredFactoryTest.php index e4385b598c604..21392e5f7b127 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Async/ProxyDeferredFactoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Async/ProxyDeferredFactoryTest.php @@ -18,7 +18,7 @@ class ProxyDeferredFactoryTest extends TestCase { /** - * @var ProxyDeferredFactory + * @var \TestDeferred\TestClass\ProxyDeferredFactory */ private $factory; @@ -43,6 +43,7 @@ protected function setUp() //phpcs:ignore include_once __DIR__ .'/_files/test_class.php'; \TestDeferred\TestClass::$created = 0; + $this->factory = Bootstrap::getObjectManager()->get(\TestDeferred\TestClass\ProxyDeferredFactory::class); } /* @@ -57,9 +58,10 @@ public function testCreate(): void return new \TestDeferred\TestClass($value); }; /** @var \TestDeferred\TestClass $proxy */ - $proxy = $this->factory->createFor( - \TestDeferred\TestClass::class, - $this->callbackDeferredFactory->create(['callback' => $callback]) + $proxy = $this->factory->create( + [ + 'deferred' => $this->callbackDeferredFactory->create(['callback' => $callback]) + ] ); $this->assertInstanceOf(\TestDeferred\TestClass::class, $proxy); $this->assertEmpty(\TestDeferred\TestClass::$created); @@ -80,9 +82,10 @@ public function testSerialize(): void return new \TestDeferred\TestClass($value); }; /** @var \TestDeferred\TestClass $proxy */ - $proxy = $this->factory->createFor( - \TestDeferred\TestClass::class, - $this->callbackDeferredFactory->create(['callback' => $callback]) + $proxy = $this->factory->create( + [ + 'deferred' => $this->callbackDeferredFactory->create(['callback' => $callback]) + ] ); //phpcs:disable /** @var \TestDeferred\TestClass $unserialized */ @@ -106,9 +109,10 @@ public function testClone(): void return new \TestDeferred\TestClass($value); }; /** @var \TestDeferred\TestClass $proxy */ - $proxy = $this->factory->createFor( - \TestDeferred\TestClass::class, - $this->callbackDeferredFactory->create(['callback' => $callback]) + $proxy = $this->factory->create( + [ + 'deferred' => $this->callbackDeferredFactory->create(['callback' => $callback]) + ] ); $this->assertEquals(0, \TestDeferred\TestClass::$created); $this->assertEquals(0, $called); @@ -137,9 +141,10 @@ public function getValue() }; }; /** @var \TestDeferred\TestClass $proxy */ - $proxy = $this->factory->createFor( - \TestDeferred\TestClass::class, - $this->callbackDeferredFactory->create(['callback' => $callback]) + $proxy = $this->factory->create( + [ + 'deferred' => $this->callbackDeferredFactory->create(['callback' => $callback]) + ] ); $this->assertInstanceOf(\TestDeferred\TestClass::class, $proxy); $this->assertEmpty(\TestDeferred\TestClass::$created); From a0b1277bb78db8127d4ed091e8dc9c7461e2c660 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 15 Aug 2019 15:59:20 -0500 Subject: [PATCH 0387/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Source/LayoutUpdate.php | 37 ++++++ .../Product/Attribute/Source/LayoutUpdate.php | 58 ++++++++++ .../Data/UpdateCustomLayoutAttributes.php | 105 ++++++++++++++++++ .../Product/Form/Modifier/Eav.php | 10 +- .../Source/SpecificSourceInterface.php | 28 +++++ 5 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php create mode 100644 app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php create mode 100644 app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php create mode 100644 app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php new file mode 100644 index 0000000000000..2030e05b74925 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute\Source; + +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; +use Magento\Framework\Api\CustomAttributesDataInterface; + +/** + * List of layout updates available for a category. + */ +class LayoutUpdate extends AbstractSource implements SpecificSourceInterface +{ + /** + * @inheritDoc + */ + public function getAllOptions() + { + $options = [['label' => 'Use default', 'value' => '']]; + + return $options; + } + + /** + * @inheritDoc + */ + public function getOptionsFor(CustomAttributesDataInterface $entity): array + { + return $this->getAllOptions(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php new file mode 100644 index 0000000000000..214348890cf2a --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Source; + +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; +use Magento\Framework\Api\CustomAttributesDataInterface; + +/** + * List of layout updates available for a product. + */ +class LayoutUpdate extends AbstractSource implements SpecificSourceInterface +{ + /** + * @var string[] + */ + private $optionsText; + + /** + * @inheritDoc + */ + public function getAllOptions() + { + $default = ''; + $defaultText = 'Use default'; + $this->optionsText[$default] = $defaultText; + + return [['label' => $defaultText, 'value' => $default]]; + } + + /** + * @inheritDoc + */ + public function getOptionText($value) + { + if (is_scalar($value) && array_key_exists($value, $this->optionsText)) { + return $this->optionsText[$value]; + } + + return false; + } + + /** + * @inheritDoc + */ + public function getOptionsFor(CustomAttributesDataInterface $entity): array + { + $options = $this->getAllOptions(); + + return $options; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php new file mode 100644 index 0000000000000..0701d4e4b2b06 --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Setup\Patch\Data; + +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Catalog\Setup\CategorySetupFactory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Category; + +/** + * Add new custom layout related attributes. + */ +class UpdateCustomLayoutAttributes implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var CategorySetupFactory + */ + private $categorySetupFactory; + + /** + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param CategorySetupFactory $categorySetupFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + CategorySetupFactory $categorySetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritDoc + */ + public function apply() + { + /** @var CategorySetup $eavSetup */ + $eavSetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $eavSetup->addAttribute( + Product::ENTITY, + 'custom_layout_update_file', + [ + 'type' => 'varchar', + 'label' => 'Custom Layout Update', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Product\Attribute\Source\LayoutUpdate::class, + 'required' => false, + 'sort_order' => 51, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'Design', + 'is_used_in_grid' => false, + 'is_visible_in_grid' => false, + 'is_filterable_in_grid' => false + ] + ); + + $eavSetup->addAttribute( + Category::ENTITY, + 'custom_layout_update_file', + [ + 'type' => 'varchar', + 'label' => 'Custom Layout Update', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Category\Attribute\Source\LayoutUpdate::class, + 'required' => false, + 'sort_order' => 51, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'Custom Design', + 'is_used_in_grid' => false, + 'is_visible_in_grid' => false, + 'is_filterable_in_grid' => false + ] + ); + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 5d1e853cef3d1..2da985601761d 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -18,6 +18,7 @@ use Magento\Eav\Api\Data\AttributeGroupInterface; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory as GroupCollectionFactory; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; @@ -686,11 +687,17 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC 'sortOrder' => $sortOrder * self::SORT_ORDER_MULTIPLIER, ] ); + $product = $this->locator->getProduct(); // TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done $attributeModel = $this->getAttributeModel($attribute); if ($attributeModel->usesSource()) { - $options = $attributeModel->getSource()->getAllOptions(true, true); + $source = $attributeModel->getSource(); + if ($source instanceof SpecificSourceInterface) { + $options = $source->getOptionsFor($product); + } else { + $options = $attributeModel->getSource()->getAllOptions(true, true); + } foreach ($options as &$option) { $option['__disableTmpl'] = true; } @@ -717,7 +724,6 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC $meta = $this->arrayManager->merge($configPath, $meta, ['componentType' => Field::NAME]); } - $product = $this->locator->getProduct(); if (in_array($attributeCode, $this->attributesToDisable) || $product->isLockedAttribute($attributeCode)) { $meta = $this->arrayManager->merge($configPath, $meta, ['disabled' => true]); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php new file mode 100644 index 0000000000000..c6422962f6b1b --- /dev/null +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Eav\Model\Entity\Attribute\Source; + +use Magento\Framework\Api\CustomAttributesDataInterface; + +/** + * Can provide entity-specific options for an attribute. + */ +interface SpecificSourceInterface extends SourceInterface +{ + /** + * List of options specific to an entity. + * + * Same format as for "getAllOptions". + * Will be called instead of "getAllOptions". + * + * @param CustomAttributesDataInterface $entity + * @return array + */ + public function getOptionsFor(CustomAttributesDataInterface $entity): array; +} From 905804a24c934a166afe2e6fdb3434714dd35fd7 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Thu, 15 Aug 2019 22:40:41 -0500 Subject: [PATCH 0388/2437] Convert AdvancedSearchEntityTest to MFTF --- .../Test/Mftf/Data/CustomAttributeData.xml | 8 ++++ .../Catalog/Test/Mftf/Data/ProductData.xml | 27 +++++++++++ ...frontFillFormAdvancedSearchActionGroup.xml | 29 ++++++++++++ ...frontAdvancedSearchByAllParametersTest.xml | 30 ++++++++++++ ...refrontAdvancedSearchByDescriptionTest.xml | 28 +++++++++++ ...cedSearchByNameSkuDescriptionPriceTest.xml | 30 ++++++++++++ .../StorefrontAdvancedSearchByNameTest.xml | 28 +++++++++++ ...refrontAdvancedSearchByPartialNameTest.xml | 25 ++++++++++ ...cedSearchByPartialShortDescriptionTest.xml | 25 ++++++++++ ...edSearchByPartialSkuAndDescriptionTest.xml | 26 ++++++++++ ...orefrontAdvancedSearchByPartialSkuTest.xml | 25 ++++++++++ ...dvancedSearchByPriceFromAndPriceToTest.xml | 26 ++++++++++ .../StorefrontAdvancedSearchByPriceToTest.xml | 33 +++++++++++++ ...ntAdvancedSearchByShortDescriptionTest.xml | 29 ++++++++++++ .../StorefrontAdvancedSearchBySkuTest.xml | 25 ++++++++++ ...tAdvancedSearchEntitySimpleProductTest.xml | 47 +++++++++++++++++++ ...dvancedSearchNegativeProductSearchTest.xml | 29 ++++++++++++ ...tAdvancedSearchWithoutEnteringDataTest.xml | 33 +++++++++++++ .../TestCase/AdvancedSearchEntityTest.xml | 16 ++++++- 19 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchActionGroup.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuAndDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceFromAndPriceToTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByShortDescriptionTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchBySkuTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchNegativeProductSearchTest.xml create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchWithoutEnteringDataTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index 1684bd0c8a2c3..95e256c856fbe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -51,4 +51,12 @@ <data key="attribute_code">short_description</data> <data key="value">Short Fixedtest 555</data> </entity> + <entity name="ProductShortDescriptionAdvancedSearch" type="custom_attribute"> + <data key="attribute_code">short_description</data> + <data key="value"><p>abc_short</p></data> + </entity> + <entity name="ProductDescriptionAdvancedSearchADC123" type="custom_attribute"> + <data key="attribute_code">description</data> + <data key="value"><p>dfj_full</p></data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 517ab253b8238..70d58be4b0df8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1210,4 +1210,31 @@ <requiredEntity type="product_extension_attribute">EavStock1</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> + <entity name="ABC_dfj_SimpleProduct" type="product"> + <data key="name" unique="suffix">abc_dfj_</data> + <data key="sku" unique="suffix">abc_dfj</data> + <data key="price">50.00</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ProductDescriptionAdvancedSearchABC</requiredEntity> + <requiredEntity type="custom_attribute_array">ProductShortDescriptionAdvancedSearch</requiredEntity> + </entity> + <entity name="ABC_123_SimpleProduct" type="product"> + <data key="name" unique="suffix">adc_123_</data> + <data key="sku" unique="suffix">adc_123</data> + <data key="price">100.00</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">100</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ProductDescriptionAdvancedSearchADC123</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchActionGroup.xml new file mode 100644 index 0000000000000..1afdb6e5e46fa --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontFillFormAdvancedSearchActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillFormAdvancedSearchActionGroup"> + <arguments> + <argument name="productName" type="string" defaultValue=""/> + <argument name="sku" type="string" defaultValue=""/> + <argument name="description" type="string" defaultValue=""/> + <argument name="short_description" type="string" defaultValue=""/> + <argument name="price_from" type="string" defaultValue=""/> + <argument name="price_to" type="string" defaultValue=""/> + </arguments> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{productName}}" stepKey="fillName"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" userInput="{{sku}}" stepKey="fillSku"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.Description}}" userInput="{{description}}" stepKey="fillDescription"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ShortDescription}}" userInput="{{short_description}}" stepKey="fillShortDescription"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" userInput="{{price_from}}" stepKey="fillPriceFrom"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" userInput="{{price_to}}" stepKey="fillPriceTo"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml new file mode 100644 index 0000000000000..0ebcf59dae2b3 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByAllParametersTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by name, sku, description, short description, price from and price to"/> + <description value="Search product in advanced search by name, sku, description, short description, price from and price to"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="abc_dfj"/> + <argument name="sku" value="abc_dfj"/> + <argument name="description" value="adc_Full"/> + <argument name="short_description" value="abc_short"/> + <argument name="price_to" value="500"/> + <argument name="price_from" value="49"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByDescriptionTest.xml new file mode 100644 index 0000000000000..5693721e6ed65 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByDescriptionTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByDescriptionTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by description"/> + <description value="Search product in advanced search by description"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="ABC_123_SimpleProduct" stepKey="createProduct"/> + </before> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="description" value="dfj_full"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml new file mode 100644 index 0000000000000..71b6ca064e0c8 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByNameSkuDescriptionPriceTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by name, sku, description, short description, price from 49 and price to 50"/> + <description value="Search product in advanced search by name, sku, description, short description, price from 49 and price to 50"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="abc_dfj"/> + <argument name="sku" value="abc_dfj"/> + <argument name="description" value="adc_Full"/> + <argument name="short_description" value="abc_short"/> + <argument name="price_to" value="50"/> + <argument name="price_from" value="49"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml new file mode 100644 index 0000000000000..e6020a65cb62d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByNameTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by name"/> + <description value="Search product in advanced search by name"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="ABC_123_SimpleProduct" stepKey="createProduct"/> + </before> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="adc_123"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml new file mode 100644 index 0000000000000..bf2b2f7bbbee9 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPartialNameTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by partial name"/> + <description value="Search product in advanced search by partial name"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="abc"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml new file mode 100644 index 0000000000000..2afb4b5a341c1 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPartialShortDescriptionTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by partial short description"/> + <description value="Search product in advanced search by partial short description"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="short_description" value="dfj_short"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuAndDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuAndDescriptionTest.xml new file mode 100644 index 0000000000000..b2b4ef9cc4782 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuAndDescriptionTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPartialSkuAndDescriptionTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by partial sku and description"/> + <description value="Search product in advanced search by partial sku and description"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="sku" value="abc"/> + <argument name="description" value="adc_full"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuTest.xml new file mode 100644 index 0000000000000..45cec0a899361 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialSkuTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPartialSkuTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by partial sku"/> + <description value="Search product in advanced search by partial sku"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="sku" value="abc"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceFromAndPriceToTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceFromAndPriceToTest.xml new file mode 100644 index 0000000000000..6b85cdf61c84c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceFromAndPriceToTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPriceFromAndPriceToTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by price from and price to"/> + <description value="Search product in advanced search by price from and price to"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="price_to" value="50"/> + <argument name="price_from" value="50"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml new file mode 100644 index 0000000000000..755bb92c897ea --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByPriceToTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by price to"/> + <description value="Search product in advanced search by price to"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="ABC_123_SimpleProduct" stepKey="createProduct2" after="createProduct"/> + </before> + <after> + <deleteData createDataKey="createProduct2" stepKey="deleteProduct2" after="deleteProduct"/> + </after> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="price_to" value="100"/> + </actionGroup> + <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProduct2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProduct2Name" after="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByShortDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByShortDescriptionTest.xml new file mode 100644 index 0000000000000..c4622d02a5152 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByShortDescriptionTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchByShortDescriptionTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by short description"/> + <description value="Search product in advanced search by short description"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <remove keyForRemoval="createProduct"/> + <remove keyForRemoval="deleteProduct"/> + <remove keyForRemoval="seeProductName"/> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="short_description" value="dfj_short"/> + </actionGroup> + <see userInput="We can't find any items matching these search criteria. Modify your search." selector="{{StorefrontQuickSearchResultsSection.messageSection}}" stepKey="see"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchBySkuTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchBySkuTest.xml new file mode 100644 index 0000000000000..ca5e237099681 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchBySkuTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchBySkuTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Search product in advanced search by sku"/> + <description value="Search product in advanced search by sku"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="sku" value="abc_dfj"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml new file mode 100644 index 0000000000000..16bc36f907a61 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Use Advanced Search to Find the Product"/> + <description value="Use Advanced Search to Find the Product"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-12421"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="ABC_dfj_SimpleProduct" stepKey="createProduct"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + </after> + + <!-- 1. Navigate to Frontend --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/> + + <!-- 2. Click "Advanced Search" --> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="openAdvancedSearch"/> + + <!-- 3. Fill test data in to field(s) 4. Click "Search" button--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="abc_dfj"/> + <argument name="sku" value="abc_dfj"/> + </actionGroup> + + <!-- 5. Perform all asserts --> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResult"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$createProduct.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchNegativeProductSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchNegativeProductSearchTest.xml new file mode 100644 index 0000000000000..b4f2314295a00 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchNegativeProductSearchTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchNegativeProductSearchTest" extends="StorefrontAdvancedSearchEntitySimpleProductTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Negative product search"/> + <description value="Negative product search"/> + <testCaseId value="MAGETWO-24729"/> + <severity value="CRITICAL"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <remove keyForRemoval="createProduct"/> + <remove keyForRemoval="deleteProduct"/> + <remove keyForRemoval="seeProductName"/> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> + <argument name="productName" value="Negative_product_search"/> + </actionGroup> + <see userInput="We can't find any items matching these search criteria. Modify your search." selector="{{StorefrontQuickSearchResultsSection.messageSection}}" stepKey="see"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchWithoutEnteringDataTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchWithoutEnteringDataTest.xml new file mode 100644 index 0000000000000..8a29ab718bd25 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchWithoutEnteringDataTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvancedSearchWithoutEnteringDataTest"> + <annotations> + <stories value="Use Advanced Search"/> + <title value="Do Advanced Search without entering data"/> + <description value="'Enter a search term and try again.' error message is missed in Advanced Search"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-14859"/> + <group value="searchFrontend"/> + <group value="mtf_migrated"/> + </annotations> + <!-- 1. Navigate to Frontend --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/> + + <!-- 2. Click "Advanced Search" --> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="openAdvancedSearch"/> + + <!-- 3. Fill test data in to field(s) 4. Click "Search" button--> + <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"/> + + <!-- 5. Perform all asserts --> + <see userInput="Enter a search term and try again." selector="{{StorefrontQuickSearchResultsSection.messageSection}}" stepKey="see"/> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/AdvancedSearchEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/AdvancedSearchEntityTest.xml index 4744fa7756c4e..9a26386c82cb8 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/AdvancedSearchEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/AdvancedSearchEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\CatalogSearch\Test\TestCase\AdvancedSearchEntityTest" summary="Use Advanced Search" ticketId="MAGETWO-24729"> <variation name="AdvancedSearchEntityTestVariation1" summary="Use Advanced Search to Find the Product" ticketId="MAGETWO-12421"> - <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:acceptance_test, test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> <data name="productSearch/data/name" xsi:type="string">abc_dfj</data> @@ -16,6 +16,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by name</data> <data name="products/simple_1" xsi:type="string">-</data> <data name="products/simple_2" xsi:type="string">Yes</data> @@ -23,6 +24,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation3"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by partial name</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -30,6 +32,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation4"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by sku</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -37,6 +40,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by partial sku</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -44,6 +48,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by partial sku and description</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -52,6 +57,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation7"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by description</data> <data name="products/simple_1" xsi:type="string">-</data> <data name="products/simple_2" xsi:type="string">Yes</data> @@ -59,6 +65,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation8"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by short description</data> <data name="products/simple_1" xsi:type="string">-</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -66,6 +73,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation9"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by partial short description</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -73,6 +81,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation10"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by price to</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">Yes</data> @@ -80,6 +89,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation11"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by price from and price to</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -88,6 +98,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation12"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by name, sku, description, short description, price from and price to</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -100,6 +111,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation13"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Search product in advanced search by name, sku, description, short description, price from and price to</data> <data name="products/simple_1" xsi:type="string">Yes</data> <data name="products/simple_2" xsi:type="string">-</data> @@ -112,11 +124,13 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductsResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation14"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Negative product search</data> <data name="productSearch/data/name" xsi:type="string">Negative_product_search</data> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchNoResult" /> </variation> <variation name="AdvancedSearchEntityTestVariation15" summary="Do Advanced Search without entering data" ticketId="MAGETWO-14859"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="issue" xsi:type="string">MAGETWO-18537: "Enter a search term and try again." error message is missed in Advanced Search</data> <data name="productSearch/data/name" xsi:type="string" /> <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchEmptyTerm" /> From a2f86726b6c60bf059987ba8fbda7d59c1f1422b Mon Sep 17 00:00:00 2001 From: Viktor Petryk <victor.petryk@transoftgroup.com> Date: Fri, 16 Aug 2019 09:45:46 +0300 Subject: [PATCH 0389/2437] MC-19080: Incorrect behavior after shipping methods disabled --- .../OfflineShipping/Model/CollectRatesTest.php | 2 +- ...ectRatesTest.php => CollectRatesAbstract.php} | 16 ++++++++++------ .../Magento/Ups/Model/CollectRatesTest.php | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) rename dev/tests/integration/testsuite/Magento/Shipping/Model/{CollectRatesTest.php => CollectRatesAbstract.php} (85%) diff --git a/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php index 910b4f570c360..1b85f99752cf8 100644 --- a/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/OfflineShipping/Model/CollectRatesTest.php @@ -11,7 +11,7 @@ * Integration tests for offline shipping carriers. * @magentoAppIsolation enabled */ -class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesTest +class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesAbstract { /** * @var string diff --git a/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesAbstract.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php rename to dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesAbstract.php index a5994cc604a65..e120f2f64359b 100644 --- a/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/Shipping/Model/CollectRatesAbstract.php @@ -15,11 +15,10 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Integration tests for shipping carriers. + * Abstract class for testing shipping carriers. */ -class CollectRatesTest extends \PHPUnit\Framework\TestCase +abstract class CollectRatesAbstract extends \PHPUnit\Framework\TestCase { - /** * @var ObjectManagerInterface */ @@ -45,14 +44,13 @@ class CollectRatesTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - if (empty($this->carrier) || empty($this->errorMessage)) { - $this->markTestSkipped('Required fields are empty'); - } $this->objectManager = Bootstrap::getObjectManager(); $this->shipping = $this->objectManager->get(Shipping::class); } /** + * Tests that an error message is displayed when the shipping method is enabled and not applicable. + * * @return void */ public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() @@ -65,6 +63,8 @@ public function testCollectRatesWhenShippingCarrierIsAvailableAndNotApplicable() } /** + * Tests that shipping rates don't return when the shipping method is disabled and not applicable. + * * @return void */ public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicable() @@ -76,6 +76,8 @@ public function testCollectRatesWhenShippingCarrierIsNotAvailableAndNotApplicabl } /** + * Returns customer address. + * * @return DataObject */ private function getAddress(): DataObject @@ -102,6 +104,8 @@ private function getAddress(): DataObject } /** + * Returns shipping rate by the result. + * * @param Result $result * @return Method|Error */ diff --git a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php index 58f31836a310a..7cfaa8c7de733 100644 --- a/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php +++ b/dev/tests/integration/testsuite/Magento/Ups/Model/CollectRatesTest.php @@ -11,7 +11,7 @@ * Integration tests for online shipping carriers. * @magentoAppIsolation enabled */ -class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesTest +class CollectRatesTest extends \Magento\Shipping\Model\CollectRatesAbstract { /** * @var string From 88d7d1161c333e950c10e7bf9cdf04d19ae52123 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Fri, 16 Aug 2019 10:22:42 +0300 Subject: [PATCH 0390/2437] MC-17149: UI components configuration incorrect (overlapping text labels) --- .../Product/Form/Modifier/AdvancedPricing.php | 34 +++++++++++++++--- .../Product/Form/Modifier/Categories.php | 4 +-- .../Product/Form/Modifier/Eav.php | 4 ++- .../Product/Form/Modifier/General.php | 5 ++- .../Form/Modifier/ScheduleDesignUpdate.php | 9 +++-- .../Product/Form/Modifier/TierPrice.php | 2 +- .../adminhtml/ui_component/category_form.xml | 4 +-- .../Form/Modifier/AdvancedInventory.php | 13 +++---- .../adminhtml/ui_component/product_form.xml | 16 --------- .../Form/Modifier/ProductUrlRewrite.php | 25 +++++++------ .../view/base/ui_component/customer_form.xml | 3 +- .../Product/Modifier/GiftMessage.php | 36 ++++++++++--------- .../backend/web/css/source/forms/_fields.less | 1 - 13 files changed, 87 insertions(+), 69 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 9ad75b5fda923..3ea6a3cdfe656 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -149,6 +149,7 @@ public function modifyMeta(array $meta) $this->specialPriceDataToInline(); $this->customizeTierPrice(); + $this->customizePrice(); if (isset($this->meta['advanced-pricing'])) { $this->addAdvancedPriceLink(); @@ -197,6 +198,29 @@ protected function preparePriceFields($fieldCode) return $this; } + /** + * Customize price field. + * + * @return $this + */ + private function customizePrice() + { + $pathFrom = $this->arrayManager->findPath('price', $this->meta, null, 'children'); + + if ($pathFrom) { + $this->meta = $this->arrayManager->merge( + $this->arrayManager->slicePath($pathFrom, 0, -2) . '/arguments/data/config', + $this->meta, + [ + 'label' => false, + 'required' => false, + ] + ); + } + + return $this; + } + /** * Customize tier price field * @@ -573,12 +597,11 @@ private function specialPriceDataToInline() $this->arrayManager->slicePath($pathFrom, 0, -2) . '/arguments/data/config', $this->meta, [ - 'label' => __('Special Price From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', - 'scopeLabel' => - $this->arrayManager->get($pathFrom . '/arguments/data/config/scopeLabel', $this->meta), ] ); $this->meta = $this->arrayManager->merge( @@ -586,8 +609,9 @@ private function specialPriceDataToInline() $this->meta, [ 'label' => __('Special Price From'), - 'scopeLabel' => null, - 'additionalClasses' => 'admin__field-date' + 'scopeLabel' => + $this->arrayManager->get($pathFrom . '/arguments/data/config/scopeLabel', $this->meta), + 'additionalClasses' => 'admin__field-date', ] ); $this->meta = $this->arrayManager->merge( diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 5f1907344ce83..8c040b42c5029 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -243,13 +243,13 @@ protected function customizeCategoriesField(array $meta) 'arguments' => [ 'data' => [ 'config' => [ - 'label' => __('Categories'), + 'label' => false, + 'required' => false, 'dataScope' => '', 'breakLine' => false, 'formElement' => 'container', 'componentType' => 'container', 'component' => 'Magento_Ui/js/form/components/group', - 'scopeLabel' => __('[GLOBAL]'), 'disabled' => $this->locator->getProduct()->isLockedAttribute($fieldCode), ], ], diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 5d1e853cef3d1..f944b6ebde75c 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -858,7 +858,9 @@ public function setupAttributeContainerMeta(ProductAttributeInterface $attribute 'arguments/data/config', $containerMeta, [ - 'component' => 'Magento_Ui/js/form/components/group' + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, ] ); } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 91c74a2da5048..325eb5433f4ea 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -239,6 +239,8 @@ protected function customizeWeightField(array $meta) $containerPath . static::META_CONFIG_PATH, $meta, [ + 'label' => false, + 'required' => false, 'component' => 'Magento_Ui/js/form/components/group', ] ); @@ -314,7 +316,8 @@ protected function customizeNewDateRangeField(array $meta) $fromContainerPath . self::META_CONFIG_PATH, $meta, [ - 'label' => __('Set Product as New From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php index b2f453e8d8ccb..3b01106619640 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php @@ -37,7 +37,8 @@ public function __construct(ArrayManager $arrayManager) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -47,7 +48,8 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyData(array $data) @@ -96,7 +98,8 @@ protected function customizeDateRangeField(array $meta) $fromContainerPath . self::META_CONFIG_PATH, $meta, [ - 'label' => __('Schedule Update From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index a529580e29239..9c5fffc5db9b9 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -115,7 +115,7 @@ private function getUpdatedTierPriceStructure(array $priceMeta) 'dataType' => Price::NAME, 'component' => 'Magento_Ui/js/form/components/group', 'label' => __('Price'), - 'enableLabel' => true, + 'showLabel' => false, 'dataScope' => '', 'additionalClasses' => 'control-grouped', 'sortOrder' => isset($priceMeta['arguments']['data']['config']['sortOrder']) diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 6ce43132ef48a..5e761a665030a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -527,10 +527,7 @@ <item name="type" xsi:type="string">group</item> <item name="config" xsi:type="array"> <item name="additionalClasses" xsi:type="string">admin__control-grouped-date</item> - <item name="label" xsi:type="string" translate="true">Schedule Update From</item> - <item name="required" xsi:type="boolean">false</item> <item name="breakLine" xsi:type="boolean">false</item> - <item name="scopeLabel" xsi:type="string">[STORE VIEW]</item> </item> </argument> <field name="custom_design_from" sortOrder="230" formElement="date"> @@ -540,6 +537,7 @@ </additionalClasses> <dataType>string</dataType> <label translate="true">Schedule Update From</label> + <scopeLabel>[STORE VIEW]</scopeLabel> </settings> </field> <field name="custom_design_to" sortOrder="240" formElement="date"> diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php index 7386f133b569a..22896c5e47567 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php @@ -85,7 +85,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -163,7 +163,7 @@ private function getData(StockItemInterface $stockItem) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -175,6 +175,8 @@ public function modifyMeta(array $meta) } /** + * Modify UI Quantity and Stock status attribute meta. + * * @return void */ private function prepareMeta() @@ -183,10 +185,6 @@ private function prepareMeta() $pathField = $this->arrayManager->findPath($fieldCode, $this->meta, null, 'children'); if ($pathField) { - $labelField = $this->arrayManager->get( - $this->arrayManager->slicePath($pathField, 0, -2) . '/arguments/data/config/label', - $this->meta - ); $fieldsetPath = $this->arrayManager->slicePath($pathField, 0, -4); $this->meta = $this->arrayManager->merge( @@ -214,10 +212,9 @@ private function prepareMeta() 'formElement' => 'container', 'componentType' => 'container', 'component' => "Magento_Ui/js/form/components/group", - 'label' => $labelField, + 'label' => false, 'breakLine' => false, 'dataScope' => $fieldCode, - 'scopeLabel' => '[GLOBAL]', 'source' => 'product_details', 'sortOrder' => (int) $this->arrayManager->get( $this->arrayManager->slicePath($pathField, 0, -2) . '/arguments/data/config/sortOrder', diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index 0a7f0fdc32d40..7e5863bd2f616 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -35,9 +35,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Manage Stock</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="manage_stock" formElement="select"> @@ -112,9 +110,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Out-of-Stock Threshold</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -274,9 +270,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Maximum Qty Allowed in Shopping Cart</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="max_sale_qty" formElement="input"> @@ -373,9 +367,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Backorders</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -438,9 +430,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Notify for Quantity Below</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -495,9 +485,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Enable Qty Increments</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="enable_qty_increments" formElement="select"> @@ -554,9 +542,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Qty Increments</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.enable_qty_increments</item> </item> @@ -615,9 +601,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Stock Status</item> <item name="dataScope" xsi:type="string">quantity_and_stock_status</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> diff --git a/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php b/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php index bcb5154e35501..10791eae5405f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php +++ b/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php @@ -53,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -65,7 +65,7 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -95,16 +95,21 @@ protected function addUrlRewriteCheckbox(array $meta) ScopeInterface::SCOPE_STORE, $this->locator->getProduct()->getStoreId() ); - - $meta = $this->arrayManager->merge($containerPath, $meta, [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'component' => 'Magento_Ui/js/form/components/group', + $meta = $this->arrayManager->merge( + $containerPath, + $meta, + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, + ], ], ], - ], - ]); + ] + ); $checkbox['arguments']['data']['config'] = [ 'componentType' => Field::NAME, diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 5fb8b17dbb8c5..663e28bf987cc 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -152,8 +152,6 @@ <argument name="data" xsi:type="array"> <item name="type" xsi:type="string">group</item> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Group</item> - <item name="required" xsi:type="boolean">true</item> <item name="dataScope" xsi:type="boolean">false</item> <item name="validateWholeGroup" xsi:type="boolean">true</item> </item> @@ -166,6 +164,7 @@ </item> </argument> <settings> + <required>true</required> <dataType>number</dataType> </settings> </field> diff --git a/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php b/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php index e3d617eac1cd2..fe2479d778992 100644 --- a/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php +++ b/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php @@ -53,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -73,7 +73,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -101,24 +101,28 @@ protected function customizeAllowGiftMessageField(array $meta) 'children' ); $fieldPath = $this->arrayManager->findPath(static::FIELD_MESSAGE_AVAILABLE, $meta, null, 'children'); - $groupConfig = $this->arrayManager->get($containerPath, $meta); $fieldConfig = $this->arrayManager->get($fieldPath, $meta); - $meta = $this->arrayManager->merge($containerPath, $meta, [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'formElement' => 'container', - 'componentType' => 'container', - 'component' => 'Magento_Ui/js/form/components/group', - 'label' => $groupConfig['arguments']['data']['config']['label'], - 'breakLine' => false, - 'sortOrder' => $fieldConfig['arguments']['data']['config']['sortOrder'], - 'dataScope' => '', + $meta = $this->arrayManager->merge( + $containerPath, + $meta, + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'formElement' => 'container', + 'componentType' => 'container', + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, + 'breakLine' => false, + 'sortOrder' => $fieldConfig['arguments']['data']['config']['sortOrder'], + 'dataScope' => '', + ], ], ], - ], - ]); + ] + ); $meta = $this->arrayManager->merge( $containerPath, $meta, diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 08aeb35d7adb2..66c9086c15aa7 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -545,7 +545,6 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; - background: @color-white; left: 0; position: absolute; top: 0; From 789ed270d885afd20425da30b71b94cbedb49ce1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Fri, 16 Aug 2019 14:42:08 +0300 Subject: [PATCH 0391/2437] MC-19089: OnePageCheckoutDeclinedTest fails on Bamboo. Order is placed with incorrect credit card number --- .../frontend/web/js/view/payment/method-renderer/cc-form.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index ac97e4fa5eb58..5f06d26e2acfc 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -246,8 +246,10 @@ define( return; } - self.setPaymentPayload(payload); - self.placeOrder(); + if (self.validateCardType()) { + self.setPaymentPayload(payload); + self.placeOrder(); + } }); } }, From 60fc2086c62398f325426c307d8158ff988eb09a Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Fri, 16 Aug 2019 15:43:58 +0300 Subject: [PATCH 0392/2437] MC-19334: Reindex error when website have store without store view --- .../Magento/Store/Model/ScopeTreeProvider.php | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Store/Model/ScopeTreeProvider.php b/app/code/Magento/Store/Model/ScopeTreeProvider.php index da772ec0410e0..a22d5abb8c486 100644 --- a/app/code/Magento/Store/Model/ScopeTreeProvider.php +++ b/app/code/Magento/Store/Model/ScopeTreeProvider.php @@ -78,25 +78,30 @@ public function get() 'scopes' => [], ]; - /** @var Group $group */ - foreach ($groups[$website->getId()] as $group) { - $groupScope = [ - 'scope' => ScopeInterface::SCOPE_GROUP, - 'scope_id' => $group->getId(), - 'scopes' => [], - ]; - - /** @var Store $store */ - foreach ($stores[$group->getId()] as $store) { - $storeScope = [ - 'scope' => ScopeInterface::SCOPE_STORES, - 'scope_id' => $store->getId(), + if (!empty($groups[$website->getId()])) { + /** @var Group $group */ + foreach ($groups[$website->getId()] as $group) { + $groupScope = [ + 'scope' => ScopeInterface::SCOPE_GROUP, + 'scope_id' => $group->getId(), 'scopes' => [], ]; - $groupScope['scopes'][] = $storeScope; + + if (!empty($stores[$group->getId()])) { + /** @var Store $store */ + foreach ($stores[$group->getId()] as $store) { + $storeScope = [ + 'scope' => ScopeInterface::SCOPE_STORES, + 'scope_id' => $store->getId(), + 'scopes' => [], + ]; + $groupScope['scopes'][] = $storeScope; + } + } + $websiteScope['scopes'][] = $groupScope; } - $websiteScope['scopes'][] = $groupScope; } + $defaultScope['scopes'][] = $websiteScope; } From 8d588c0090850f54932997ea9dddb53469773ec8 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 16 Aug 2019 12:05:52 -0500 Subject: [PATCH 0393/2437] MC-18685: Remove custom layout updates from admin --- .../Cms/Controller/Adminhtml/Page/Save.php | 25 +++- .../Page/CustomLayout/CustomLayoutManager.php | 117 +++------------ .../CustomLayout/CustomLayoutRepository.php | 139 ++++++++++++++++++ .../Page/CustomLayoutManagerInterface.php | 41 ++---- .../Page/CustomLayoutRepositoryInterface.php | 50 +++++++ app/code/Magento/Cms/etc/di.xml | 1 + .../Model/Page/CustomLayoutManagerTest.php | 42 +++--- .../Model/Page/CustomLayoutRepositoryTest.php | 100 +++++++++++++ 8 files changed, 362 insertions(+), 153 deletions(-) create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php create mode 100644 app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 1412a86cf0fd0..ff64fc8ec7ee9 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -8,8 +8,11 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; use Magento\Cms\Model\Page; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Exception\LocalizedException; +use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; /** * Save CMS page action. @@ -43,6 +46,11 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac */ private $pageRepository; + /** + * @var CustomLayoutRepositoryInterface + */ + private $customLayoutRepository; + /** * @param Action\Context $context * @param PostDataProcessor $dataProcessor @@ -55,15 +63,16 @@ public function __construct( PostDataProcessor $dataProcessor, DataPersistorInterface $dataPersistor, \Magento\Cms\Model\PageFactory $pageFactory = null, - \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null + \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null, + ?CustomLayoutRepositoryInterface $customLayoutRepository = null ) { $this->dataProcessor = $dataProcessor; $this->dataPersistor = $dataPersistor; - $this->pageFactory = $pageFactory - ?: \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); + $this->pageFactory = $pageFactory ?: ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); $this->pageRepository = $pageRepository - ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Cms\Api\PageRepositoryInterface::class); + ?: ObjectManager::getInstance()->get(\Magento\Cms\Api\PageRepositoryInterface::class); + $this->customLayoutRepository = $customLayoutRepository + ?? ObjectManager::getInstance()->get(CustomLayoutRepositoryInterface::class); parent::__construct($context); } @@ -112,7 +121,13 @@ public function execute() return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); } + $customLayoutFile = (string)$this->getRequest()->getParam('layout_update_selected'); $this->pageRepository->save($model); + if ($customLayoutFile) { + $this->customLayoutRepository->save(new CustomLayoutSelected($model->getId(), $customLayoutFile)); + } else { + $this->customLayoutRepository->deleteFor($model->getId()); + } $this->messageManager->addSuccessMessage(__('You saved the page.')); return $this->processResultRedirect($model, $resultRedirect, $data); } catch (LocalizedException $e) { diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php index ae9bda5d04f69..8bf902f009bc8 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -8,40 +8,22 @@ namespace Magento\Cms\Model\Page\CustomLayout; +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; use Magento\Cms\Model\Page\CustomLayoutManagerInterface; -use Magento\Cms\Model\ResourceModel\Page; -use Magento\Cms\Model\Page as PageModel; -use Magento\Cms\Model\PageFactory as PageModelFactory; -use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\App\Area; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; use Magento\Framework\View\File; use Magento\Framework\View\File\CollectorInterface; +use Magento\Framework\View\Result\Page as PageLayout; /** * @inheritDoc */ class CustomLayoutManager implements CustomLayoutManagerInterface { - /** - * @var Page - */ - private $pageRepository; - - /** - * @var PageModelFactory; - */ - private $pageFactory; - - /** - * @var IdentityMap - */ - private $identityMap; - /** * @var CollectorInterface */ @@ -58,104 +40,44 @@ class CustomLayoutManager implements CustomLayoutManagerInterface private $design; /** - * @param Page $pageRepository - * @param PageModelFactory $factory - * @param IdentityMap $identityMap + * @var PageRepositoryInterface + */ + private $pageRepository; + + /** * @param CollectorInterface $fileCollector * @param FlyweightFactory $themeFactory * @param DesignInterface $design + * @param PageRepositoryInterface $pageRepository */ public function __construct( - Page $pageRepository, - PageModelFactory $factory, - IdentityMap $identityMap, CollectorInterface $fileCollector, FlyweightFactory $themeFactory, - DesignInterface $design + DesignInterface $design, + PageRepositoryInterface $pageRepository ) { - $this->pageRepository = $pageRepository; - $this->pageFactory = $factory; - $this->identityMap = $identityMap; $this->fileCollector = $fileCollector; $this->themeFactory = $themeFactory; $this->design = $design; - } - - /** - * Find page model by ID. - * - * @param int $id - * @return PageModel - * @throws NoSuchEntityException - */ - private function findPage(int $id): PageModel - { - if (!$page = $this->identityMap->get($id)) { - /** @var PageModel $page */ - $this->pageRepository->load($page = $this->pageFactory->create(), $id); - if (!$page->getIdentifier()) { - throw NoSuchEntityException::singleField('id', $id); - } - } - - return $page; + $this->pageRepository = $pageRepository; } /** * Adopt page's identifier to be used as layout handle. * - * @param PageModel $page + * @param PageInterface $page * @return string */ - private function sanitizeIdentifier(PageModel $page): string + private function sanitizeIdentifier(PageInterface $page): string { return str_replace('/', '_', $page->getIdentifier()); } - /** - * Save new custom layout file value for a page. - * - * @param int $pageId - * @param string|null $layoutFile - * @throws LocalizedException - * @throws \InvalidArgumentException When invalid file was selected. - * @throws NoSuchEntityException - */ - private function saveLayout(int $pageId, ?string $layoutFile): void - { - $page = $this->findPage($pageId); - if ($layoutFile !== null && !in_array($layoutFile, $this->fetchAvailableFiles($pageId), true)) { - throw new \InvalidArgumentException( - $layoutFile .' is not available for page #' .$pageId - ); - } - - $page->setData('layout_update_selected', $layoutFile); - $this->pageRepository->save($page); - } - /** * @inheritDoc */ - public function save(CustomLayoutSelectedInterface $layout): void + public function fetchAvailableFiles(PageInterface $page): array { - $this->saveLayout($layout->getPageId(), $layout->getLayoutFileId()); - } - - /** - * @inheritDoc - */ - public function deleteFor(int $pageId): void - { - $this->saveLayout($pageId, null); - } - - /** - * @inheritDoc - */ - public function fetchAvailableFiles(int $pageId): array - { - $page = $this->findPage($pageId); $identifier = $this->sanitizeIdentifier($page); $layoutFiles = $this->fileCollector->getFiles( $this->themeFactory->create($this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)), @@ -184,11 +106,12 @@ function (File $file) use ($identifier) : ?string { /** * @inheritDoc */ - public function fetchHandle(int $pageId): ?array + public function applyUpdate(PageLayout $layout, CustomLayoutSelectedInterface $layoutSelected): void { - $page = $this->findPage($pageId); + $page = $this->pageRepository->getById($layoutSelected->getPageId()); - return $page['layout_update_selected'] - ? ['selectable' => $this->sanitizeIdentifier($page) .'_' .$page['layout_update_selected']] : null; + $layout->addPageLayoutHandles( + ['selectable' => $this->sanitizeIdentifier($page) .'_' .$layoutSelected->getLayoutFileId()] + ); } } diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php new file mode 100644 index 0000000000000..9365bb31e970a --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout; + +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; +use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; +use Magento\Cms\Model\ResourceModel\Page; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; + +/** + * @inheritDoc + */ +class CustomLayoutRepository implements CustomLayoutRepositoryInterface +{ + /** + * @var Page + */ + private $pageRepository; + + /** + * @var PageModelFactory; + */ + private $pageFactory; + + /** + * @var IdentityMap + */ + private $identityMap; + + /** + * @var CustomLayoutManagerInterface + */ + private $manager; + + /** + * @param Page $pageRepository + * @param PageModelFactory $factory + * @param IdentityMap $identityMap + * @param CustomLayoutManagerInterface $manager + */ + public function __construct( + Page $pageRepository, + PageModelFactory $factory, + IdentityMap $identityMap, + CustomLayoutManagerInterface $manager + ) { + $this->pageRepository = $pageRepository; + $this->pageFactory = $factory; + $this->identityMap = $identityMap; + $this->manager = $manager; + } + + /** + * Find page model by ID. + * + * @param int $id + * @return PageModel + * @throws NoSuchEntityException + */ + private function findPage(int $id): PageModel + { + if (!$page = $this->identityMap->get($id)) { + /** @var PageModel $page */ + $this->pageRepository->load($page = $this->pageFactory->create(), $id); + if (!$page->getIdentifier()) { + throw NoSuchEntityException::singleField('id', $id); + } + } + + return $page; + } + + /** + * Save new custom layout file value for a page. + * + * @param int $pageId + * @param string|null $layoutFile + * @throws LocalizedException + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException + */ + private function saveLayout(int $pageId, ?string $layoutFile): void + { + $page = $this->findPage($pageId); + if ($layoutFile !== null && !in_array($layoutFile, $this->manager->fetchAvailableFiles($page), true)) { + throw new \InvalidArgumentException( + $layoutFile .' is not available for page #' .$pageId + ); + } + + if ($page->getData('layout_update_selected') != $layoutFile) { + $page->setData('layout_update_selected', $layoutFile); + $this->pageRepository->save($page); + } + } + + /** + * @inheritDoc + */ + public function save(CustomLayoutSelectedInterface $layout): void + { + $this->saveLayout($layout->getPageId(), $layout->getLayoutFileId()); + } + + /** + * @inheritDoc + */ + public function deleteFor(int $pageId): void + { + $this->saveLayout($pageId, null); + } + + /** + * @inheritDoc + */ + public function getFor(int $pageId): CustomLayoutSelectedInterface + { + $page = $this->findPage($pageId); + if (!$page['layout_update_selected']) { + throw new NoSuchEntityException( + __('Page "%id" doesn\'t have custom layout assigned', ['id' => $page->getIdentifier()]) + ); + } + + return new CustomLayoutSelected($pageId, $page['layout_update_selected']); + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php index 5c1c8662f9d9c..8d66ff36c846e 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php @@ -8,51 +8,30 @@ namespace Magento\Cms\Model\Page; +use Magento\Cms\Api\Data\PageInterface; use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Result\Page as View; /** * Manage custom layout files for CMS pages. */ interface CustomLayoutManagerInterface { - /** - * Save layout file to be used when rendering given page. - * - * @throws LocalizedException When failed to save new value. - * @throws \InvalidArgumentException When invalid file was selected. - * @throws NoSuchEntityException When given page is not found. - * @param CustomLayoutSelectedInterface $layout - * @return void - */ - public function save(CustomLayoutSelectedInterface $layout): void; - - /** - * Do not use custom layout update when rendering the page. - * - * @throws NoSuchEntityException When given page is not found. - * @throws LocalizedException When failed to remove existing value. - * @param int $pageId - * @return void - */ - public function deleteFor(int $pageId): void; - /** * List of available custom files for the given page. * - * @throws NoSuchEntityException When given page is not found. - * @param int $pageId + * @param PageInterface $page * @return string[] */ - public function fetchAvailableFiles(int $pageId): array; + public function fetchAvailableFiles(PageInterface $page): array; + /** - * Get handles according to the page's settings. + * Apply the page's layout settings. * - * @throws NoSuchEntityException When given page is not found. - * @param int $pageId - * @return array|null With keys as handle IDs and values as handles. + * @param View $layout + * @param CustomLayoutSelectedInterface $layoutSelected + * @return void */ - public function fetchHandle(int $pageId): ?array; + public function applyUpdate(View $layout, CustomLayoutSelectedInterface $layoutSelected): void; } diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php new file mode 100644 index 0000000000000..80eb39b7ab20f --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Access to "custom layout" page property. + */ +interface CustomLayoutRepositoryInterface +{ + + /** + * Save layout file to be used when rendering given page. + * + * @throws LocalizedException When failed to save new value. + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException When given page is not found. + * @param CustomLayoutSelectedInterface $layout + * @return void + */ + public function save(CustomLayoutSelectedInterface $layout): void; + + /** + * Do not use custom layout update when rendering the page. + * + * @throws NoSuchEntityException When given page is not found. + * @throws LocalizedException When failed to remove existing value. + * @param int $pageId + * @return void + */ + public function deleteFor(int $pageId): void; + + /** + * Find custom layout settings for a page. + * + * @param int $pageId + * @return CustomLayoutSelectedInterface + * @throws NoSuchEntityException When either the page or any settings are found. + */ + public function getFor(int $pageId): CustomLayoutSelectedInterface; +} diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 0af2fa29fb762..ef95a004102ac 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -241,4 +241,5 @@ <argument name="fileCollector" xsi:type="object">Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy</argument> </arguments> </type> + <preference for="Magento\Cms\Model\Page\CustomLayoutRepositoryInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository" /> </config> diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php index 7b2f0b8cbd57c..7e405725a2d8b 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -15,12 +15,18 @@ use PHPUnit\Framework\TestCase; use Magento\Framework\View\File\CollectorInterface; use Magento\Framework\View\File; +use Magento\Framework\View\Result\PageFactory as PageResultFactory; /** * Test the manager. */ class CustomLayoutManagerTest extends TestCase { + /** + * @var CustomLayoutRepositoryInterface + */ + private $repo; + /** * @var CustomLayoutManagerInterface */ @@ -31,12 +37,18 @@ class CustomLayoutManagerTest extends TestCase */ private $pageFactory; + /** + * @var PageResultFactory + */ + private $resultFactory; + /** * @inheritDoc */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->resultFactory = $objectManager->get(PageResultFactory::class); //Mocking available list of files for the page. $files = [ new File('cms_page_view_selectable_page100_select1.xml', 'test'), @@ -50,6 +62,10 @@ protected function setUp() CustomLayoutManagerInterface::class, ['fileCollector' => $fileCollector] ); + $this->repo = $objectManager->create( + CustomLayoutRepositoryInterface::class, + ['manager' => $this->manager] + ); $this->pageFactory = $objectManager->get(PageFactory::class); } @@ -57,35 +73,21 @@ protected function setUp() * Test updating a page's custom layout. * * @magentoDataFixture Magento/Cms/_files/pages.php + * @throws \Throwable * @return void */ - public function testCustomLayout(): void + public function testCustomLayoutUpdate(): void { /** @var Page $page */ $page = $this->pageFactory->create(); $page->load('page100', 'identifier'); $pageId = (int)$page->getId(); - - //Invalid file ID - $exceptionRaised = null; - try { - $this->manager->save(new CustomLayoutSelected($pageId, 'some_file')); - } catch (\Throwable $exception) { - $exceptionRaised = $exception; - } - $this->assertNotEmpty($exceptionRaised); - $this->assertInstanceOf(\InvalidArgumentException::class, $exceptionRaised); - //Set file ID - $this->manager->save(new CustomLayoutSelected($pageId, 'select2')); - - //Test handles - $this->assertEquals(['selectable' => 'page100_select2'], $this->manager->fetchHandle($pageId)); - - //Removing custom file - $this->manager->deleteFor($pageId); + $this->repo->save(new CustomLayoutSelected($pageId, 'select2')); //Test handles - $this->assertNull($this->manager->fetchHandle($pageId)); + $result = $this->resultFactory->create(); + $this->manager->applyUpdate($result, $this->repo->getFor($pageId)); + $this->assertContains('___selectable_page100_select2', $result->getLayout()->getUpdate()->getHandles()); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php new file mode 100644 index 0000000000000..4736774ea0f02 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page; +use Magento\Cms\Model\PageFactory; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Framework\View\File\CollectorInterface; +use Magento\Framework\View\File; + +/** + * Test the repository. + */ +class CustomLayoutRepositoryTest extends TestCase +{ + /** + * @var CustomLayoutRepositoryInterface + */ + private $repo; + + /** + * @var PageFactory + */ + private $pageFactory; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + //Mocking available list of files for the page. + $files = [ + new File('cms_page_view_selectable_page100_select1.xml', 'test'), + new File('cms_page_view_selectable_page100_select2.xml', 'test') + ]; + $fileCollector = $this->getMockForAbstractClass(CollectorInterface::class); + $fileCollector->method('getFiles') + ->willReturn($files); + + $manager = $objectManager->create( + CustomLayoutManagerInterface::class, + ['fileCollector' => $fileCollector] + ); + $this->repo = $objectManager->create(CustomLayoutRepositoryInterface::class, ['manager' => $manager]); + $this->pageFactory = $objectManager->get(PageFactory::class); + } + + /** + * Test updating a page's custom layout. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @return void + */ + public function testCustomLayout(): void + { + /** @var Page $page */ + $page = $this->pageFactory->create(); + $page->load('page100', 'identifier'); + $pageId = (int)$page->getId(); + + //Invalid file ID + $exceptionRaised = null; + try { + $this->repo->save(new CustomLayoutSelected($pageId, 'some_file')); + } catch (\Throwable $exception) { + $exceptionRaised = $exception; + } + $this->assertNotEmpty($exceptionRaised); + $this->assertInstanceOf(\InvalidArgumentException::class, $exceptionRaised); + + //Set file ID + $this->repo->save(new CustomLayoutSelected($pageId, 'select2')); + + //Test saved + $saved = $this->repo->getFor($pageId); + $this->assertEquals('select2', $saved->getLayoutFileId()); + + //Removing custom file + $this->repo->deleteFor($pageId); + + //Test saved + $notFound = false; + try { + $this->repo->getFor($pageId); + } catch (NoSuchEntityException $exception) { + $notFound = true; + } + $this->assertTrue($notFound); + } +} From 059b5cc7d1d069d870f80e5ecdead1ea54effcf8 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 16 Aug 2019 12:08:39 -0500 Subject: [PATCH 0394/2437] MC-19145: [CLOUD] Internal error after DHL was configured --- app/code/Magento/Dhl/Model/Carrier.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 5d96d4bcbf43a..0a1632a45cb0c 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -819,15 +819,13 @@ protected function _getAllItems() if (!empty($decimalItems)) { foreach ($decimalItems as $decimalItem) { - $fullItems = array_merge( - $fullItems, - array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']) - ); + $fullItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']); } } else { - $fullItems = array_merge($fullItems, array_fill(0, $qty, $this->_getWeight($itemWeight))); + $fullItems[] = array_fill(0, $qty, $this->_getWeight($itemWeight)); } } + $fullItems = array_merge(...$fullItems); sort($fullItems); return $fullItems; From 9c4cf858267136c719b960009dcd5319d54add21 Mon Sep 17 00:00:00 2001 From: Mark Berube <berube@adobe.com> Date: Fri, 16 Aug 2019 12:14:47 -0500 Subject: [PATCH 0395/2437] MC-5696: Fixing flaky QueueManagementTest --- .../MysqlMq/Model/QueueManagementTest.php | 84 ++++++++++++++----- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php index 56dd77d3da17c..abb8205ba8867 100644 --- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php @@ -23,27 +23,26 @@ class QueueManagementTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->queueManagement = $this->objectManager->create(\Magento\MysqlMq\Model\QueueManagement::class); + $this->queueManagement = $this->objectManager->create(QueueManagement::class); } /** * @magentoDataFixture Magento/MysqlMq/_files/queues.php */ - public function testAllFlows() + public function testMessageReading() { - $this->queueManagement->addMessageToQueues('topic1', 'messageBody1', ['queue1', 'queue2']); - $this->queueManagement->addMessageToQueues('topic2', 'messageBody2', ['queue2', 'queue3']); - $this->queueManagement->addMessageToQueues('topic3', 'messageBody3', ['queue1', 'queue3']); - $this->queueManagement->addMessageToQueues('topic4', 'messageBody4', ['queue1', 'queue2', 'queue3']); + $this->queueManagement->addMessageToQueues('topic1', 'messageBody1', ['queue1']); + $this->queueManagement->addMessageToQueues('topic2', 'messageBody2', ['queue1']); + $this->queueManagement->addMessageToQueues('topic3', 'messageBody3', ['queue1']); $maxMessagesNumber = 2; - $messages = $this->queueManagement->readMessages('queue3', $maxMessagesNumber); + $messages = $this->queueManagement->readMessages('queue1', $maxMessagesNumber); $this->assertCount($maxMessagesNumber, $messages); $firstMessage = array_shift($messages); - $this->assertEquals('topic2', $firstMessage[QueueManagement::MESSAGE_TOPIC]); - $this->assertEquals('messageBody2', $firstMessage[QueueManagement::MESSAGE_BODY]); - $this->assertEquals('queue3', $firstMessage[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals('topic1', $firstMessage[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody1', $firstMessage[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue1', $firstMessage[QueueManagement::MESSAGE_QUEUE_NAME]); $this->assertEquals( QueueManagement::MESSAGE_STATUS_IN_PROGRESS, $firstMessage[QueueManagement::MESSAGE_STATUS] @@ -55,9 +54,9 @@ public function testAllFlows() $this->assertCount(12, date_parse($firstMessage[QueueManagement::MESSAGE_UPDATED_AT])); $secondMessage = array_shift($messages); - $this->assertEquals('topic3', $secondMessage[QueueManagement::MESSAGE_TOPIC]); - $this->assertEquals('messageBody3', $secondMessage[QueueManagement::MESSAGE_BODY]); - $this->assertEquals('queue3', $secondMessage[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals('topic2', $secondMessage[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody2', $secondMessage[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue1', $secondMessage[QueueManagement::MESSAGE_QUEUE_NAME]); $this->assertEquals( QueueManagement::MESSAGE_STATUS_IN_PROGRESS, $secondMessage[QueueManagement::MESSAGE_STATUS] @@ -67,35 +66,74 @@ public function testAllFlows() $this->assertTrue(is_numeric($secondMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID])); $this->assertEquals(0, $secondMessage[QueueManagement::MESSAGE_NUMBER_OF_TRIALS]); $this->assertCount(12, date_parse($secondMessage[QueueManagement::MESSAGE_UPDATED_AT])); + } + + /** + * @magentoDataFixture Magento/MysqlMq/_files/queues.php + */ + public function testChangingMessageStatus() + { + $this->queueManagement->addMessageToQueues('topic1', 'messageBody1', ['queue1']); + $this->queueManagement->addMessageToQueues('topic2', 'messageBody2', ['queue1']); + $this->queueManagement->addMessageToQueues('topic3', 'messageBody3', ['queue1']); + $this->queueManagement->addMessageToQueues('topic4', 'messageBody4', ['queue1']); + + $maxMessagesNumber = 4; + $messages = $this->queueManagement->readMessages('queue1', $maxMessagesNumber); + $this->assertCount($maxMessagesNumber, $messages); + + $firstMessage = array_shift($messages); + $secondMessage = array_shift($messages); + $thirdMessage = array_shift($messages); + $fourthMessage = array_shift($messages); + + $this->queueManagement->changeStatus( + [ + $firstMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID] + ], + QueueManagement::MESSAGE_STATUS_ERROR + ); - /** Mark one message as complete or failed and make sure it is not displayed in the list of read messages */ $this->queueManagement->changeStatus( [ $secondMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID] ], QueueManagement::MESSAGE_STATUS_COMPLETE ); - $messages = $this->queueManagement->readMessages('queue3', $maxMessagesNumber); - $this->assertCount(1, $messages); $this->queueManagement->changeStatus( [ - $firstMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID] + $thirdMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID] ], - QueueManagement::MESSAGE_STATUS_ERROR + QueueManagement::MESSAGE_STATUS_NEW + ); + + $this->queueManagement->changeStatus( + [ + $fourthMessage[QueueManagement::MESSAGE_QUEUE_RELATION_ID] + ], + QueueManagement::MESSAGE_STATUS_RETRY_REQUIRED ); - $messages = $this->queueManagement->readMessages('queue3', $maxMessagesNumber); - $this->assertCount(0, $messages); - /** Ensure that message for retry is still accessible when reading messages from the queue */ - $messages = $this->queueManagement->readMessages('queue2', 1); + $messages = $this->queueManagement->readMessages('queue1'); + $this->assertCount(2, $messages); + } + + /** + * @magentoDataFixture Magento/MysqlMq/_files/queues.php + */ + public function testMessageRetry() + { + $this->queueManagement->addMessageToQueues('topic1', 'messageBody1', ['queue1']); + + $messages = $this->queueManagement->readMessages('queue1', 1); $message = array_shift($messages); $messageRelationId = $message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]; for ($i = 0; $i < 2; $i++) { $this->assertEquals($i, $message[QueueManagement::MESSAGE_NUMBER_OF_TRIALS]); $this->queueManagement->pushToQueueForRetry($message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]); - $messages = $this->queueManagement->readMessages('queue2', 1); + $messages = $this->queueManagement->readMessages('queue1', 1); $message = array_shift($messages); $this->assertEquals($messageRelationId, $message[QueueManagement::MESSAGE_QUEUE_RELATION_ID]); } From a2397de3e0e26c8c92143069a104403aaa113b91 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Fri, 16 Aug 2019 14:14:20 -0500 Subject: [PATCH 0396/2437] MC-19338: Fix MTF UpgradeSystemTest --- .../tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php b/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php index c9b3ce35e84ed..7a82b43dba76d 100644 --- a/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php +++ b/dev/tests/functional/tests/app/Magento/Setup/Test/TestCase/UpgradeSystemTest.php @@ -132,6 +132,8 @@ public function test( // Check application version $this->adminDashboard->open(); + $this->adminDashboard->getModalMessage()->dismissIfModalAppears(); + $this->adminDashboard->getModalMessage()->waitModalWindowToDisappear(); $assertApplicationVersion->processAssert($this->adminDashboard, $version); } } From a3c2ed1f0eb05eb223327e5881cab6a5af8a120c Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 16 Aug 2019 14:23:54 -0500 Subject: [PATCH 0397/2437] MC-18685: Remove custom layout updates from admin --- .../Cms/Controller/Adminhtml/Page/Save.php | 9 ++- .../Magento/Cms/Model/Page/DataProvider.php | 24 ++++++- .../{PageSaveTest.php => PageDesignTest.php} | 54 +++++++++++++- .../Cms/Model/Page/DataProviderTest.php | 72 +++++++++++++++++++ .../Cms/_files/pages_with_layout_xml.php | 25 +++++++ .../_files/pages_with_layout_xml_rollback.php | 26 +++++++ 6 files changed, 205 insertions(+), 5 deletions(-) rename dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/{PageSaveTest.php => PageDesignTest.php} (61%) create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php create mode 100644 dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index ff64fc8ec7ee9..974bc044e3ab0 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -95,6 +95,14 @@ public function execute() if (empty($data['page_id'])) { $data['page_id'] = null; } + //Either use existing custom layout XML or use a file. + $customLayoutFile = (string)$this->getRequest()->getParam('layout_update_selected'); + if ($customLayoutFile !== '_existing_') { + $data['custom_layout_update_xml'] = null; + } else { + $customLayoutFile = null; + } + /** @var \Magento\Cms\Model\Page $model */ $model = $this->pageFactory->create(); @@ -121,7 +129,6 @@ public function execute() return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); } - $customLayoutFile = (string)$this->getRequest()->getParam('layout_update_selected'); $this->pageRepository->save($model); if ($customLayoutFile) { $this->customLayoutRepository->save(new CustomLayoutSelected($model->getId(), $customLayoutFile)); diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index bf961c0420e2f..801440eaf0bcc 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -108,6 +108,10 @@ public function getData() /** @var $page \Magento\Cms\Model\Page */ foreach ($items as $page) { $this->loadedData[$page->getId()] = $page->getData(); + if ($page->getCustomLayoutUpdateXml()) { + //Deprecated layout update exists. + $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; + } } $data = $this->dataPersistor->get('cms_page'); @@ -115,6 +119,9 @@ public function getData() $page = $this->collection->getNewEmptyItem(); $page->setData($data); $this->loadedData[$page->getId()] = $page->getData(); + if ($page->getCustomLayoutUpdateXml()) { + $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; + } $this->dataPersistor->clear('cms_page'); } @@ -153,10 +160,23 @@ public function getMeta() } //List of custom layout files available for current page. - $options = [['label' => 'Use default', 'value' => '']]; + $options = [['label' => 'No update', 'value' => '']]; if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { //We must have a specific page selected. - foreach ($this->customLayoutManager->fetchAvailableFiles($pageId) as $layoutFile) { + //Finding our page. + $found = null; + /** @var \Magento\Cms\Model\Page $page */ + foreach ($this->collection->getItems() as $page) { + if ($page->getId() == $pageId) { + $found = $page; + break; + } + } + //If custom layout XML is set then displaying this special option. + if ($page->getCustomLayoutUpdateXml()) { + $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; + } + foreach ($this->customLayoutManager->fetchAvailableFiles($found) as $layoutFile) { $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php similarity index 61% rename from dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php rename to dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php index 17c28a76d394d..1d47ebc8aeca6 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageSaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php @@ -9,7 +9,9 @@ namespace Magento\Cms\Controller\Adminhtml; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\GetPageByIdentifierInterface; use Magento\Cms\Model\Page; +use Magento\Cms\Model\PageFactory; use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; @@ -17,11 +19,11 @@ use Magento\TestFramework\TestCase\AbstractBackendController; /** - * Test the saving CMS pages via admin area interface. + * Test the saving CMS pages design via admin area interface. * * @magentoAppArea adminhtml */ -class PageSaveTest extends AbstractBackendController +class PageDesignTest extends AbstractBackendController { /** * @var string @@ -43,6 +45,11 @@ class PageSaveTest extends AbstractBackendController */ private $aclBuilder; + /** + * @var GetPageByIdentifierInterface + */ + private $pageRetriever; + /** * @inheritDoc */ @@ -51,6 +58,7 @@ protected function setUp() parent::setUp(); $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->pageRetriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); } /** @@ -110,4 +118,46 @@ public function testSaveDesign(): void MessageInterface::TYPE_ERROR ); } + + /** + * Test that custom layout update fields are dealt with properly. + * + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @throws \Throwable + * @return void + */ + public function testSaveLayoutXml(): void + { + $page = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $requestData = [ + Page::PAGE_ID => $page->getId(), + PageInterface::IDENTIFIER => 'test_custom_layout_page_1', + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + 'layout_update_selected' => '_existing_' + ]; + + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->getRequest()->setDispatched(false); + + $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $this->assertEquals($updated->getCustomLayoutUpdateXml(), $page->getCustomLayoutUpdateXml()); + + $requestData = [ + Page::PAGE_ID => $page->getId(), + PageInterface::IDENTIFIER => 'test_custom_layout_page_1', + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + 'layout_update_selected' => '' + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->getRequest()->setDispatched(false); + + $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $this->assertEmpty($updated->getCustomLayoutUpdateXml()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php new file mode 100644 index 0000000000000..115e39269edba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; + +/** + * Test pages data provider. + */ +class DataProviderTest extends TestCase +{ + /** + * @var DataProvider + */ + private $provider; + + /** + * @var PageModelFactory + */ + private $pageFactory; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->pageFactory = $objectManager->get(PageModelFactory::class); + $this->provider = $objectManager->create( + DataProvider::class, + [ + 'name' => 'test', + 'primaryFieldName' => 'page_id', + 'requestFieldName' => 'page_id' + ] + ); + } + + /** + * Check that custom layout date is handled properly. + * + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @throws \Throwable + * @return void + */ + public function testCustomLayoutData(): void + { + $data = $this->provider->getData(); + $page1Data = null; + $page2Data = null; + foreach ($data as $pageData) { + if ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_1') { + $page1Data = $pageData; + } elseif ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_2') { + $page2Data = $pageData; + } + } + $this->assertNotEmpty($page1Data); + $this->assertNotEmpty($page2Data); + $this->assertEquals('_existing_', $page1Data['layout_update_selected']); + $this->assertEquals(null, $page2Data['layout_update_selected']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php new file mode 100644 index 0000000000000..c5e6986277ed0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$pageFactory = $objectManager->get(PageModelFactory::class); +/** @var PageModel $page */ +$page = $pageFactory->create(); +$page->setIdentifier('test_custom_layout_page_1'); +$page->setTitle('Test Page'); +$page->setCustomLayoutUpdateXml('tst'); +$page->save(); +/** @var PageModel $page2 */ +$page2 = $pageFactory->create(); +$page2->setIdentifier('test_custom_layout_page_2'); +$page2->setTitle('Test Page 2'); +$page2->save(); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php new file mode 100644 index 0000000000000..ca9195256af8c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$pageFactory = $objectManager->get(PageModelFactory::class); +/** @var PageModel $page */ +$page = $pageFactory->create(); +$page->load('test_custom_layout_page_1', PageModel::IDENTIFIER); +if ($page->getId()) { + $page->delete(); +} +/** @var PageModel $page2 */ +$page2 = $pageFactory->create(); +$page2->load('test_custom_layout_page_2', PageModel::IDENTIFIER); +if ($page2->getId()) { + $page2->delete(); +} From e12ee95aa33c5a27dbbad616d0b2890e8955f338 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 16 Aug 2019 16:03:38 -0500 Subject: [PATCH 0398/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Backend/LayoutUpdate.php | 41 +++++++++++++++++++ .../Attribute/Backend/LayoutUpdate.php | 41 +++++++++++++++++++ .../Data/UpdateCustomLayoutAttributes.php | 2 + 3 files changed, 84 insertions(+) create mode 100644 app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php create mode 100644 app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php new file mode 100644 index 0000000000000..c34bddbe11d33 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute\Backend; + +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Catalog\Model\Category; + +/** + * Allows to select a layout file to merge when rendering a category's page. + */ +class LayoutUpdate extends AbstractBackend +{ + /** + * @inheritDoc + * @param Category $object + */ + public function validate($object) + { + $valid = parent::validate($object); + + + return $valid; + } + + /** + * @inheritDoc + * @param Category $object + */ + public function beforeSave($object) + { + parent::beforeSave($object); + + return $this; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php new file mode 100644 index 0000000000000..d8c9b7e2ae59f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Backend; + +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Catalog\Model\Product; + +/** + * Allows to select a layout file to merge when rendering the product's page. + */ +class LayoutUpdate extends AbstractBackend +{ + /** + * @inheritDoc + * @param Product $object + */ + public function validate($object) + { + $valid = parent::validate($object); + + + return $valid; + } + + /** + * @inheritDoc + * @param Product $object + */ + public function beforeSave($object) + { + parent::beforeSave($object); + + return $this; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php index 0701d4e4b2b06..4809316d8ff0e 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php @@ -76,6 +76,7 @@ public function apply() 'source' => \Magento\Catalog\Model\Product\Attribute\Source\LayoutUpdate::class, 'required' => false, 'sort_order' => 51, + 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::class, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Design', 'is_used_in_grid' => false, @@ -94,6 +95,7 @@ public function apply() 'source' => \Magento\Catalog\Model\Category\Attribute\Source\LayoutUpdate::class, 'required' => false, 'sort_order' => 51, + 'backend' => \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::class, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, 'group' => 'Custom Design', 'is_used_in_grid' => false, From 572f40f211edc9ba47241d75d912d251a618e793 Mon Sep 17 00:00:00 2001 From: Mark Berube <berube@adobe.com> Date: Fri, 16 Aug 2019 16:22:16 -0500 Subject: [PATCH 0399/2437] MC-5696: Fixing flaky QueueManagementTest - Adding a test scenario for adding to multiple queues --- .../MysqlMq/Model/QueueManagementTest.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php index abb8205ba8867..790e68ee3a720 100644 --- a/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/MysqlMq/Model/QueueManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\MysqlMq\Model; /** @@ -68,6 +69,60 @@ public function testMessageReading() $this->assertCount(12, date_parse($secondMessage[QueueManagement::MESSAGE_UPDATED_AT])); } + /** + * @magentoDataFixture Magento/MysqlMq/_files/queues.php + */ + public function testMessageReadingMultipleQueues() + { + $this->queueManagement->addMessageToQueues('topic1', 'messageBody1', ['queue1']); + $this->queueManagement->addMessageToQueues('topic2', 'messageBody2', ['queue1', 'queue2']); + $this->queueManagement->addMessageToQueues('topic3', 'messageBody3', ['queue2']); + + $maxMessagesNumber = 2; + $messages = $this->queueManagement->readMessages('queue1', $maxMessagesNumber); + $this->assertCount($maxMessagesNumber, $messages); + + $message = array_shift($messages); + $this->assertEquals('topic1', $message[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody1', $message[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue1', $message[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals( + QueueManagement::MESSAGE_STATUS_IN_PROGRESS, + $message[QueueManagement::MESSAGE_STATUS] + ); + + $message= array_shift($messages); + $this->assertEquals('topic2', $message[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody2', $message[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue1', $message[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals( + QueueManagement::MESSAGE_STATUS_IN_PROGRESS, + $message[QueueManagement::MESSAGE_STATUS] + ); + + $maxMessagesNumber = 2; + $messages = $this->queueManagement->readMessages('queue2', $maxMessagesNumber); + $this->assertCount($maxMessagesNumber, $messages); + + $message= array_shift($messages); + $this->assertEquals('topic2', $message[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody2', $message[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue2', $message[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals( + QueueManagement::MESSAGE_STATUS_IN_PROGRESS, + $message[QueueManagement::MESSAGE_STATUS] + ); + + $message = array_shift($messages); + $this->assertEquals('topic3', $message[QueueManagement::MESSAGE_TOPIC]); + $this->assertEquals('messageBody3', $message[QueueManagement::MESSAGE_BODY]); + $this->assertEquals('queue2', $message[QueueManagement::MESSAGE_QUEUE_NAME]); + $this->assertEquals( + QueueManagement::MESSAGE_STATUS_IN_PROGRESS, + $message[QueueManagement::MESSAGE_STATUS] + ); + } + /** * @magentoDataFixture Magento/MysqlMq/_files/queues.php */ From 270fb51d4ccb157c6f65dc2db384dc9642977c8e Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 16 Aug 2019 16:25:47 -0500 Subject: [PATCH 0400/2437] MC-18685: Remove custom layout updates from admin --- .../Magento/Cms/Api/Data/PageInterface.php | 2 + .../Magento/Cms/Model/PageRepositoryTest.php | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php diff --git a/app/code/Magento/Cms/Api/Data/PageInterface.php b/app/code/Magento/Cms/Api/Data/PageInterface.php index 50fa8cf0db2d1..38d8feb953319 100644 --- a/app/code/Magento/Cms/Api/Data/PageInterface.php +++ b/app/code/Magento/Cms/Api/Data/PageInterface.php @@ -298,6 +298,8 @@ public function setCustomRootTemplate($customRootTemplate); * * @param string $customLayoutUpdateXml * @return \Magento\Cms\Api\Data\PageInterface + * @deprecated Existing updates are applied, new are not accepted. + * @see \Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface */ public function setCustomLayoutUpdateXml($customLayoutUpdateXml); diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php new file mode 100644 index 0000000000000..5bbb8b870aad5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model; + +use Magento\Cms\Api\GetPageByIdentifierInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test page repo. + */ +class PageRepositoryTest extends TestCase +{ + /** + * @var PageRepositoryInterface + */ + private $repo; + + /** + * @var GetPageByIdentifierInterface + */ + private $retriever; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->repo = Bootstrap::getObjectManager()->get(PageRepositoryInterface::class); + $this->retriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); + } + + /** + * Test that the field is deprecated. + * + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @return void + */ + public function testSaveUpdateXml(): void + { + $page = $this->retriever->execute('test_custom_layout_page_1', 0); + $page->setTitle($page->getTitle() .'TEST'); + + //Is successfully saved without changes to the custom layout xml. + $page = $this->repo->save($page); + + //New value is not accepted. + $page->setCustomLayoutUpdateXml($page->getCustomLayoutUpdateXml() .'TEST'); + $forbidden = false; + try { + $page = $this->repo->save($page); + } catch (CouldNotSaveException $exception) { + $forbidden = true; + } + $this->assertTrue($forbidden); + + //Can be removed + $page->setCustomLayoutUpdateXml(null); + $page = $this->repo->save($page); + $this->assertEmpty($page->getCustomLayoutUpdateXml()); + } +} From fcbe98e798706e54b785098b8c84c3b5b3c24dd6 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 19 Aug 2019 09:57:15 -0500 Subject: [PATCH 0401/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Cms/Helper/Page.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index bf263d94aaca7..634833ff45a23 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -6,8 +6,10 @@ namespace Magento\Cms\Helper; use Magento\Cms\Model\Page\CustomLayoutManagerInterface; +use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; use Magento\Framework\App\Action\Action; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; /** * CMS Page Helper @@ -83,6 +85,11 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper */ private $customLayoutManager; + /** + * @var CustomLayoutRepositoryInterface + */ + private $customLayoutRepo; + /** * Constructor * @@ -108,7 +115,8 @@ public function __construct( \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Framework\Escaper $escaper, \Magento\Framework\View\Result\PageFactory $resultPageFactory, - ?CustomLayoutManagerInterface $customLayoutManager = null + ?CustomLayoutManagerInterface $customLayoutManager = null, + ?CustomLayoutRepositoryInterface $customLayoutRepo = null ) { $this->messageManager = $messageManager; $this->_page = $page; @@ -120,6 +128,8 @@ public function __construct( $this->resultPageFactory = $resultPageFactory; $this->customLayoutManager = $customLayoutManager ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); + $this->customLayoutRepo = $customLayoutRepo + ?? ObjectManager::getInstance()->get(CustomLayoutRepositoryInterface::class); parent::__construct($context); } @@ -165,9 +175,10 @@ public function prepareResultPage(Action $action, $pageId = null) $resultPage->addHandle('cms_page_view'); $pageHandles = ['id' => str_replace('/', '_', $this->_page->getIdentifier())]; //Selected custom updates. - $customLayoutHandle = $this->customLayoutManager->fetchHandle($this->_page->getId()); - if ($customLayoutHandle) { - $pageHandles = array_merge($pageHandles, $customLayoutHandle); + try { + $this->customLayoutManager->applyUpdate($resultPage, $this->customLayoutRepo->getFor($this->_page->getId())); + } catch (NoSuchEntityException $exception) { + //No custom layout selected } $resultPage->addPageLayoutHandles($pageHandles); From 986bc309fdb9f9eee85af46439e619946dcc324b Mon Sep 17 00:00:00 2001 From: Mila Lesechko <26227105+MilaLesechko@users.noreply.github.com> Date: Mon, 19 Aug 2019 11:30:35 -0500 Subject: [PATCH 0402/2437] Convert AdvancedSearchEntityTest to MFTF --- .../Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index 95e256c856fbe..e87ee83c3c18a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -51,6 +51,10 @@ <data key="attribute_code">short_description</data> <data key="value">Short Fixedtest 555</data> </entity> + <entity name="ProductDescriptionAdvancedSearchABC" type="custom_attribute"> + <data key="attribute_code">description</data> + <data key="value"><p>adc_Full</p></data> + </entity> <entity name="ProductShortDescriptionAdvancedSearch" type="custom_attribute"> <data key="attribute_code">short_description</data> <data key="value"><p>abc_short</p></data> From 6b42eb2bff1e96c53a602f70f90785607b72c3a3 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Tue, 20 Aug 2019 09:10:12 +0300 Subject: [PATCH 0403/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer module addError -> addErrorMessage addSuccess -> addSuccessMessage addException -> addExceptionMessage --- .../Customer/Controller/Account/Confirm.php | 6 +++--- .../Customer/Controller/Account/Confirmation.php | 6 +++--- .../Customer/Controller/Account/CreatePost.php | 14 +++++++------- .../Customer/Controller/Account/EditPost.php | 8 ++++---- .../Customer/Controller/Account/LoginPost.php | 6 +++--- .../Controller/Account/ResetPasswordPost.php | 12 ++++++------ .../Magento/Customer/Controller/Address/Delete.php | 6 +++--- .../Adminhtml/Customer/InvalidateToken.php | 6 +++--- .../Customer/Controller/Adminhtml/Group/Delete.php | 6 +++--- .../Customer/Controller/Adminhtml/Group/Save.php | 4 ++-- .../Customer/Controller/Adminhtml/Index.php | 4 ++-- .../Adminhtml/Index/AbstractMassAction.php | 2 +- .../Customer/Controller/Adminhtml/Index/Delete.php | 6 +++--- .../Customer/Controller/Adminhtml/Index/Edit.php | 2 +- .../Controller/Adminhtml/Index/InlineEdit.php | 6 +++--- .../Controller/Adminhtml/Index/MassAssignGroup.php | 2 +- .../Controller/Adminhtml/Index/MassDelete.php | 2 +- .../Controller/Adminhtml/Index/MassSubscribe.php | 2 +- .../Controller/Adminhtml/Index/MassUnsubscribe.php | 2 +- .../Controller/Adminhtml/Index/ResetPassword.php | 6 ++++-- .../Customer/Controller/Adminhtml/Index/Save.php | 7 +++++-- .../Customer/Controller/Adminhtml/Locks/Unlock.php | 4 ++-- .../Customer/Observer/AfterAddressSaveObserver.php | 6 +++--- 23 files changed, 65 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 2b3cb9aa61ab5..4f0fb3ff08550 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -163,13 +163,13 @@ public function execute() $metadata->setPath('/'); $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata); } - $this->messageManager->addSuccess($this->getSuccessMessage()); + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { - $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.')); + $this->messageManager->addExceptionMessage($e, __('This confirmation key is invalid or has expired.')); } catch (\Exception $e) { - $this->messageManager->addException($e, __('There was an error confirming the account')); + $this->messageManager->addExceptionMessage($e, __('There was an error confirming the account')); } $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); diff --git a/app/code/Magento/Customer/Controller/Account/Confirmation.php b/app/code/Magento/Customer/Controller/Account/Confirmation.php index a3e2db0207630..bd9066dcd464a 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirmation.php +++ b/app/code/Magento/Customer/Controller/Account/Confirmation.php @@ -91,11 +91,11 @@ public function execute() $email, $this->storeManager->getStore()->getWebsiteId() ); - $this->messageManager->addSuccess(__('Please check your email for confirmation key.')); + $this->messageManager->addSuccessMessage(__('Please check your email for confirmation key.')); } catch (InvalidTransitionException $e) { - $this->messageManager->addSuccess(__('This email does not require confirmation.')); + $this->messageManager->addSuccessMessage(__('This email does not require confirmation.')); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Wrong email.')); + $this->messageManager->addExceptionMessage($e, __('Wrong email.')); $resultRedirect->setPath('*/*/*', ['email' => $email, '_secure' => true]); return $resultRedirect; } diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 4c9c25b5f33d9..510d82879637f 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -366,7 +366,7 @@ public function execute() if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); // @codingStandardsIgnoreStart - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __( 'You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', $email @@ -377,7 +377,7 @@ public function execute() $resultRedirect->setUrl($this->_redirect->success($url)); } else { $this->session->setCustomerDataAsLoggedIn($customer); - $this->messageManager->addSuccess($this->getSuccessMessage()); + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); $requestedRedirect = $this->accountRedirect->getRedirectCookie(); if (!$this->scopeConfig->getValue('customer/startup/redirect_dashboard') && $requestedRedirect) { $resultRedirect->setUrl($this->_redirect->success($requestedRedirect)); @@ -401,16 +401,16 @@ public function execute() $url ); // @codingStandardsIgnoreEnd - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } catch (InputException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($this->escaper->escapeHtml($error->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (LocalizedException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t save the customer.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the customer.')); } $this->session->setCustomerFormData($this->getRequest()->getPostValue()); diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index 4eb41cedea29a..d2300b7b490c5 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -216,7 +216,7 @@ public function execute() $isPasswordChanged ); $this->dispatchSuccessEvent($customerCandidateDataObject); - $this->messageManager->addSuccess(__('You saved the account information.')); + $this->messageManager->addSuccessMessage(__('You saved the account information.')); return $resultRedirect->setPath('customer/account'); } catch (InvalidEmailOrPasswordException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -227,7 +227,7 @@ public function execute() ); $this->session->logout(); $this->session->start(); - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); return $resultRedirect->setPath('customer/account/login'); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -235,9 +235,9 @@ public function execute() $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t save the customer.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the customer.')); } $this->session->setCustomerFormData($this->getRequest()->getPostValue()); diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 04051fbbf366b..a7632401933e6 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -217,17 +217,17 @@ public function execute() $message = $e->getMessage(); } catch (\Exception $e) { // PA DSS violation: throwing or logging an exception here can disclose customer password - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('An unspecified error occurred. Please contact us for assistance.') ); } finally { if (isset($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); $this->session->setUsername($login['username']); } } } else { - $this->messageManager->addError(__('A login and a password are required.')); + $this->messageManager->addErrorMessage(__('A login and a password are required.')); } } diff --git a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php index 27a00f86dd95d..a127f2acf538f 100644 --- a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php +++ b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php @@ -73,13 +73,13 @@ public function execute() $passwordConfirmation = (string)$this->getRequest()->getPost('password_confirmation'); if ($password !== $passwordConfirmation) { - $this->messageManager->addError(__("New Password and Confirm New Password values didn't match.")); + $this->messageManager->addErrorMessage(__("New Password and Confirm New Password values didn't match.")); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; } if (iconv_strlen($password) <= 0) { - $this->messageManager->addError(__('Please enter a new password.')); + $this->messageManager->addErrorMessage(__('Please enter a new password.')); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; @@ -92,17 +92,17 @@ public function execute() $password ); $this->session->unsRpToken(); - $this->messageManager->addSuccess(__('You updated your password.')); + $this->messageManager->addSuccessMessage(__('You updated your password.')); $resultRedirect->setPath('*/*/login'); return $resultRedirect; } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($error->getMessage()); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (\Exception $exception) { - $this->messageManager->addError(__('Something went wrong while saving the new password.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving the new password.')); } $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index a30e15db4b3f8..c90489bfc978e 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -27,12 +27,12 @@ public function execute() $address = $this->_addressRepository->getById($addressId); if ($address->getCustomerId() === $this->_getSession()->getCustomerId()) { $this->_addressRepository->deleteById($addressId); - $this->messageManager->addSuccess(__('You deleted the address.')); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); } else { - $this->messageManager->addError(__('We can\'t delete the address right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the address right now.')); } } catch (\Exception $other) { - $this->messageManager->addException($other, __('We can\'t delete the address right now.')); + $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); } } return $this->resultRedirectFactory->create()->setPath('*/*/index'); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php index b69410ecbfce7..7747d80595cdc 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php @@ -139,14 +139,14 @@ public function execute() if ($customerId = $this->getRequest()->getParam('customer_id')) { try { $this->tokenService->revokeCustomerAccessToken($customerId); - $this->messageManager->addSuccess(__('You have revoked the customer\'s tokens.')); + $this->messageManager->addSuccessMessage(__('You have revoked the customer\'s tokens.')); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } } else { - $this->messageManager->addError(__('We can\'t find a customer to revoke.')); + $this->messageManager->addErrorMessage(__('We can\'t find a customer to revoke.')); $resultRedirect->setPath('customer/index/index'); } return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index ab32ea08a44aa..819a49178a24d 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -24,12 +24,12 @@ public function execute() if ($id) { try { $this->groupRepository->deleteById($id); - $this->messageManager->addSuccess(__('You deleted the customer group.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer group.')); } catch (NoSuchEntityException $e) { - $this->messageManager->addError(__('The customer group no longer exists.')); + $this->messageManager->addErrorMessage(__('The customer group no longer exists.')); return $resultRedirect->setPath('customer/*/'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('customer/group/edit', ['id' => $id]); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 5ffce4cbcd989..64c94fa230fb1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -93,10 +93,10 @@ public function execute() $this->groupRepository->save($customerGroup); - $this->messageManager->addSuccess(__('You saved the customer group.')); + $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index.php b/app/code/Magento/Customer/Controller/Adminhtml/Index.php index a0317a51260da..ffae1e9f8bf1e 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index.php @@ -311,7 +311,7 @@ protected function _addSessionErrorMessages($messages) protected function actUponMultipleCustomers(callable $singleAction, $customerIds) { if (!is_array($customerIds)) { - $this->messageManager->addError(__('Please select customer(s).')); + $this->messageManager->addErrorMessage(__('Please select customer(s).')); return 0; } $customersUpdated = 0; @@ -320,7 +320,7 @@ protected function actUponMultipleCustomers(callable $singleAction, $customerIds $singleAction($customerId); $customersUpdated++; } catch (\Exception $exception) { - $this->messageManager->addError($exception->getMessage()); + $this->messageManager->addErrorMessage($exception->getMessage()); } } return $customersUpdated; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index e26b49aaebe7a..08c6e5148ade5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -64,7 +64,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php index ab39ca098162f..4b2f2614948cf 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php @@ -31,7 +31,7 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addError(__('Customer could not be deleted.')); + $this->messageManager->addErrorMessage(__('Customer could not be deleted.')); return $resultRedirect->setPath('customer/index'); } @@ -39,9 +39,9 @@ public function execute() if (!empty($customerId)) { try { $this->_customerRepository->deleteById($customerId); - $this->messageManager->addSuccess(__('You deleted the customer.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer.')); } catch (\Exception $exception) { - $this->messageManager->addError($exception->getMessage()); + $this->messageManager->addErrorMessage($exception->getMessage()); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php index 25b4ddd4e1732..d4697ecd6169c 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php @@ -43,7 +43,7 @@ public function execute() //do nothing } } catch (NoSuchEntityException $e) { - $this->messageManager->addException($e, __('Something went wrong while editing the customer.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while editing the customer.')); $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('customer/*/index'); return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 7220de0356817..41d2c43bdaf75 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -234,13 +234,13 @@ protected function saveCustomer(CustomerInterface $customer) $this->disableAddressValidation($customer); $this->customerRepository->save($customer); } catch (\Magento\Framework\Exception\InputException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId($e->getMessage())); $this->logger->critical($e); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId($e->getMessage())); $this->logger->critical($e); } catch (\Exception $e) { - $this->getMessageManager()->addError($this->getErrorWithCustomerId('We can\'t save the customer.')); + $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId('We can\'t save the customer.')); $this->logger->critical($e); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php index 5a9c52bf9b1c0..f55c81da7e0b9 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php @@ -60,7 +60,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php index edaeea6a15eb2..85286573bc5e7 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php @@ -58,7 +58,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersDeleted) { - $this->messageManager->addSuccess(__('A total of %1 record(s) were deleted.', $customersDeleted)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were deleted.', $customersDeleted)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php index 25c56ac60c14b..e072c5cb4cd49 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php @@ -64,7 +64,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index 4b40722ba9ab2..e52c9a772ed2d 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -64,7 +64,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php index 1e4fa91cbf899..3b9370c32bf6d 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php @@ -44,7 +44,9 @@ public function execute() \Magento\Customer\Model\AccountManagement::EMAIL_REMINDER, $customer->getWebsiteId() ); - $this->messageManager->addSuccess(__('The customer will receive an email with a link to reset password.')); + $this->messageManager->addSuccessMessage( + __('The customer will receive an email with a link to reset password.') + ); } catch (NoSuchEntityException $exception) { $resultRedirect->setPath('customer/index'); return $resultRedirect; @@ -57,7 +59,7 @@ public function execute() } catch (SecurityViolationException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); } catch (\Exception $exception) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $exception, __('Something went wrong while resetting customer password.') ); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 38ed688a835bc..3ee33af9ec073 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -354,7 +354,7 @@ public function execute() $this->_getSession()->unsCustomerFormData(); // Done Saving customer, finish save action $this->_coreRegistry->register(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); - $this->messageManager->addSuccess(__('You saved the customer.')); + $this->messageManager->addSuccessMessage(__('You saved the customer.')); $returnToEdit = (bool)$this->getRequest()->getParam('back', false); } catch (\Magento\Framework\Validator\Exception $exception) { $messages = $exception->getMessages(); @@ -378,7 +378,10 @@ public function execute() $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Exception $exception) { - $this->messageManager->addException($exception, __('Something went wrong while saving the customer.')); + $this->messageManager->addExceptionMessage( + $exception, + __('Something went wrong while saving the customer.') + ); $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php b/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php index 1fd06a3182948..2747ba1a665fd 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php @@ -55,10 +55,10 @@ public function execute() // unlock customer if ($customerId) { $this->authentication->unlock($customerId); - $this->getMessageManager()->addSuccess(__('Customer has been unlocked successfully.')); + $this->getMessageManager()->addSuccessMessage(__('Customer has been unlocked successfully.')); } } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php index 41311abee5da8..8677abfa89904 100644 --- a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php +++ b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php @@ -255,7 +255,7 @@ protected function addValidMessage($customerAddress, $validationResult) : (string)__('You will not be charged tax.'); } - $this->messageManager->addSuccess(implode(' ', $message)); + $this->messageManager->addSuccessMessage(implode(' ', $message)); return $this; } @@ -280,7 +280,7 @@ protected function addInvalidMessage($customerAddress) $message[] = (string)__('You will be charged tax.'); } - $this->messageManager->addError(implode(' ', $message)); + $this->messageManager->addErrorMessage(implode(' ', $message)); return $this; } @@ -307,7 +307,7 @@ protected function addErrorMessage($customerAddress) $email = $this->scopeConfig->getValue('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE); $message[] = (string)__('If you believe this is an error, please contact us at %1', $email); - $this->messageManager->addError(implode(' ', $message)); + $this->messageManager->addErrorMessage(implode(' ', $message)); return $this; } From e56a4c7a62e346472c433f41dabcd320a2ca4cdf Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Tue, 20 Aug 2019 10:02:26 +0300 Subject: [PATCH 0404/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer module Fix. Unit tests --- .../Test/Unit/Controller/Account/ConfirmTest.php | 6 +++--- .../Test/Unit/Controller/Account/CreatePostTest.php | 4 ++-- .../Test/Unit/Controller/Account/LoginPostTest.php | 10 +++++----- .../Test/Unit/Controller/Address/DeleteTest.php | 6 +++--- .../Unit/Controller/Adminhtml/Group/SaveTest.php | 4 ++-- .../Controller/Adminhtml/Index/InlineEditTest.php | 4 ++-- .../Adminhtml/Index/MassAssignGroupTest.php | 4 ++-- .../Controller/Adminhtml/Index/MassDeleteTest.php | 4 ++-- .../Controller/Adminhtml/Index/MassSubscribeTest.php | 4 ++-- .../Adminhtml/Index/MassUnsubscribeTest.php | 4 ++-- .../Controller/Adminhtml/Index/NewsletterTest.php | 2 +- .../Controller/Adminhtml/Index/ResetPasswordTest.php | 6 +++--- .../Unit/Controller/Adminhtml/Index/SaveTest.php | 12 ++++++------ .../Unit/Controller/Adminhtml/Locks/UnlockTest.php | 4 ++-- .../Unit/Observer/AfterAddressSaveObserverTest.php | 6 +++--- 15 files changed, 40 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 01fc465d4ae84..0684594d510d7 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -205,7 +205,7 @@ public function testNoCustomerIdInRequest($customerId, $key) $exception = new \Exception('Bad request.'); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($this->equalTo($exception), $this->equalTo('There was an error confirming the account')); $testUrl = 'http://example.com'; @@ -281,7 +281,7 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); @@ -399,7 +399,7 @@ public function testSuccessRedirect( ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php index f8f47eedba3ef..ac52c395d6787 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php @@ -371,7 +371,7 @@ public function testSuccessMessage( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); @@ -502,7 +502,7 @@ public function testSuccessRedirect( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 762c76b695dee..51b84d807dc13 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -222,7 +222,7 @@ public function testExecuteEmptyLoginData() ->willReturn([]); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('A login and a password are required.')) ->willReturnSelf(); @@ -551,7 +551,7 @@ protected function mockExceptions($exception, $username) $url ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); @@ -563,7 +563,7 @@ protected function mockExceptions($exception, $username) case \Magento\Framework\Exception\AuthenticationException::class: $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' @@ -580,7 +580,7 @@ protected function mockExceptions($exception, $username) case '\Exception': $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('An unspecified error occurred. Please contact us for assistance.')) ->willReturnSelf(); break; @@ -591,7 +591,7 @@ protected function mockExceptions($exception, $username) . 'Please wait and try again later.' ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); $this->session->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 4064b8586257d..7424b0f649fdc 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -146,7 +146,7 @@ public function testExecute() ->method('deleteById') ->with($addressId); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You deleted the address.')); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -183,11 +183,11 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t delete the address right now.')) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, __('We can\'t delete the address right now.')); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index 5f7064d5b124b..c9f885315b0ef 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -167,7 +167,7 @@ public function testExecuteWithTaxClassAndException() ->method('save') ->with($this->group); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the customer group.')); $exception = new \Exception('Exception'); $this->resultRedirect->expects($this->at(0)) @@ -175,7 +175,7 @@ public function testExecuteWithTaxClassAndException() ->with('customer/group') ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Exception'); $this->dataObjectProcessorMock->expects($this->once()) ->method('buildOutputDataArray') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 45e64f6557d51..c198eb3a212fa 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -366,7 +366,7 @@ public function testExecuteLocalizedException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('[Customer ID: 12] Exception message'); $this->logger->expects($this->once()) ->method('critical') @@ -394,7 +394,7 @@ public function testExecuteException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('[Customer ID: 12] We can\'t save the customer.'); $this->logger->expects($this->once()) ->method('critical') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php index 10144bdc318c1..4157359959ae4 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -170,7 +170,7 @@ public function testExecute() ->willReturnMap([[10, $customerMock], [11, $customerMock], [12, $customerMock]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -199,7 +199,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php index 190ff2c06618f..b436b5b137c78 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php @@ -155,7 +155,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) were deleted.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -179,7 +179,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index daf9c64fe7b7b..33e578224400b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -171,7 +171,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 05624661a2de4..971efc0e490bc 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -171,7 +171,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php index d2f8b8776081e..5ec39360000cb 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php @@ -150,7 +150,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccess', 'addMessage', 'addException'] + ['addSuccessMessage', 'addMessageMessage', 'addExceptionMessage'] )->getMock(); $contextArgs = [ diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php index 66e5b57eaa424..67ac60e6b9057 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -141,7 +141,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccess', 'addMessage', 'addException', 'addErrorMessage'] + ['addSuccessMessage', 'addMessage', 'addExceptionMessage', 'addErrorMessage'] )->getMock(); $this->resultRedirectFactoryMock = $this->getMockBuilder( @@ -442,7 +442,7 @@ public function testResetPasswordActionException() $this->messageManager->expects( $this->once() )->method( - 'addException' + 'addExceptionMessage' )->with( $this->equalTo($exception), $this->equalTo('Something went wrong while resetting customer password.') @@ -502,7 +502,7 @@ public function testResetPasswordActionSendEmail() $this->messageManager->expects( $this->once() )->method( - 'addSuccess' + 'addSuccessMessage' )->with( $this->equalTo('The customer will receive an email with a link to reset password.') ); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 57f384d32d980..5f34583c855c1 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -475,7 +475,7 @@ public function testExecuteWithExistentCustomer() ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the customer.')) ->willReturnSelf(); @@ -662,7 +662,7 @@ public function testExecuteWithNewCustomer() ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the customer.')) ->willReturnSelf(); @@ -804,7 +804,7 @@ public function testExecuteWithNewCustomerAndValidationException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->messageManagerMock->expects($this->once()) ->method('addMessage') @@ -951,7 +951,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->messageManagerMock->expects($this->once()) ->method('addMessage') @@ -1099,10 +1099,10 @@ public function testExecuteWithNewCustomerAndException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccess'); + ->method('addSuccessMessage'); $this->messageManagerMock->expects($this->once()) - ->method('addException') + ->method('addExceptionMessage') ->with($exception, __('Something went wrong while saving the customer.')); $this->sessionMock->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php index c92d4ed7812ba..55b4092af7141 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php @@ -118,7 +118,7 @@ public function testExecute() ->with($this->equalTo('customer_id')) ->will($this->returnValue($customerId)); $this->authenticationMock->expects($this->once())->method('unlock')->with($customerId); - $this->messageManagerMock->expects($this->once())->method('addSuccess'); + $this->messageManagerMock->expects($this->once())->method('addSuccessMessage'); $this->redirectMock->expects($this->once()) ->method('setPath') ->with($this->equalTo('customer/index/edit')) @@ -141,7 +141,7 @@ public function testExecuteWithException() ->method('unlock') ->with($customerId) ->willThrowException(new \Exception($phrase)); - $this->messageManagerMock->expects($this->once())->method('addError'); + $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); $this->controller->execute(); } } diff --git a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 8592d1bda66c1..4501b611aa11f 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -575,7 +575,7 @@ public function testAfterAddressSaveNewGroup( if ($resultValidMessage) { $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($resultValidMessage) ->willReturnSelf(); } @@ -585,7 +585,7 @@ public function testAfterAddressSaveNewGroup( ->with($vatId) ->willReturn($vatId); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($resultInvalidMessage) ->willReturnSelf(); } @@ -595,7 +595,7 @@ public function testAfterAddressSaveNewGroup( ->with('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE) ->willReturn('admin@example.com'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($resultErrorMessage) ->willReturnSelf(); } From 5271f434440d2f589c782a996d8d73bedbb36bc8 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 20 Aug 2019 11:32:03 +0300 Subject: [PATCH 0405/2437] MC-19334: Reindex error when website have store without store view --- .../Command/IndexerReindexCommandTest.php | 61 +++++++++++++++++++ ...second_store_group_with_second_website.php | 21 +++++++ ...ore_group_with_second_website_rollback.php | 8 +++ 3 files changed, 90 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php b/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php new file mode 100644 index 0000000000000..1779281f2cb80 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Indexer\Console\Command; + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit_Framework_MockObject_MockObject as Mock; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Tests for \Magento\Indexer\Console\Command\IndexerReindexCommand. + * + * @magentoDbIsolation disabled + */ +class IndexerReindexCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var InputInterface|Mock + */ + private $inputMock; + + /** + * @var OutputInterface|Mock + */ + private $outputMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + Bootstrap::getInstance()->reinitialize(); + $this->objectManager = Bootstrap::getObjectManager(); + + $this->inputMock = $this->getMockBuilder(InputInterface::class) + ->getMockForAbstractClass(); + $this->outputMock = $this->getMockBuilder(OutputInterface::class) + ->getMockForAbstractClass(); + } + + /** + * @magentoDataFixture Magento/Store/_files/second_store_group_with_second_website.php + */ + public function testReindexAll() + { + $command = $this->objectManager->create(IndexerReindexCommand::class); + $status = $command->run($this->inputMock, $this->outputMock); + $this->assertEquals(0, $status, 'Index wasn\'t success'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php new file mode 100644 index 0000000000000..a66bfb9138716 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php @@ -0,0 +1,21 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require_once __DIR__ . '/website.php'; + +/** + * @var \Magento\Store\Model\Group $storeGroup + */ +$storeGroup = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Group::class); +$storeGroup->setCode('some_group') + ->setName('custom store group') + ->setWebsite($website); +$storeGroup->save($storeGroup); + +/* Refresh stores memory cache */ +$objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->reinitStores(); diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website_rollback.php new file mode 100644 index 0000000000000..e014dc9402475 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require_once __DIR__ . '/website_rollback.php'; From 5bc42334e00cb5bd508a026e1edc1809a258ec36 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Tue, 20 Aug 2019 12:44:04 +0300 Subject: [PATCH 0406/2437] MC-19334: Reindex error when website have store without store view --- .../Command/IndexerReindexCommandTest.php | 24 ++++++++++++------- ...second_store_group_with_second_website.php | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php b/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php index 1779281f2cb80..80b6f887d6501 100644 --- a/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Indexer/Console/Command/IndexerReindexCommandTest.php @@ -17,6 +17,7 @@ * Tests for \Magento\Indexer\Console\Command\IndexerReindexCommand. * * @magentoDbIsolation disabled + * @magentoAppIsolation enabled */ class IndexerReindexCommandTest extends \PHPUnit\Framework\TestCase { @@ -35,18 +36,22 @@ class IndexerReindexCommandTest extends \PHPUnit\Framework\TestCase */ private $outputMock; + /** + * @var IndexerReindexCommand + */ + private $command; + /** * @inheritdoc */ protected function setUp() { - Bootstrap::getInstance()->reinitialize(); $this->objectManager = Bootstrap::getObjectManager(); - $this->inputMock = $this->getMockBuilder(InputInterface::class) - ->getMockForAbstractClass(); - $this->outputMock = $this->getMockBuilder(OutputInterface::class) - ->getMockForAbstractClass(); + $this->inputMock = $this->getMockBuilder(InputInterface::class)->getMockForAbstractClass(); + $this->outputMock = $this->getMockBuilder(OutputInterface::class)->getMockForAbstractClass(); + + $this->command = $this->objectManager->get(IndexerReindexCommand::class); } /** @@ -54,8 +59,11 @@ protected function setUp() */ public function testReindexAll() { - $command = $this->objectManager->create(IndexerReindexCommand::class); - $status = $command->run($this->inputMock, $this->outputMock); - $this->assertEquals(0, $status, 'Index wasn\'t success'); + $status = $this->command->run($this->inputMock, $this->outputMock); + $this->assertEquals( + \Magento\Framework\Console\Cli::RETURN_SUCCESS, + $status, + 'Index wasn\'t success' + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php index a66bfb9138716..799cda7e97423 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_group_with_second_website.php @@ -11,7 +11,7 @@ /** * @var \Magento\Store\Model\Group $storeGroup */ -$storeGroup = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Group::class); +$storeGroup = $objectManager->create(\Magento\Store\Model\Group::class); $storeGroup->setCode('some_group') ->setName('custom store group') ->setWebsite($website); From 0cfebd6660dbcb89abba40aeb3e03706d80f97bb Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 20 Aug 2019 14:53:48 +0400 Subject: [PATCH 0407/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Updated automated test script --- .../Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml | 2 +- .../AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index a944aa72e5ccd..c108068b00648 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -139,7 +139,7 @@ <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories"/> <dontSee selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="dontSeeCategoryInTree"/> </actionGroup> - <actionGroup name="deleteCategoryByName" extends="DeleteCategory"> + <actionGroup name="AdminDeleteCategoryByName" extends="DeleteCategory"> <arguments> <argument name="categoryName" type="string" defaultValue="category1"/> </arguments> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 58997aa7a5182..3663035ca47af 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -10,6 +10,7 @@ <test name="AdminCategoryWithRestrictedUrlKeyNotCreatedTest"> <annotations> <features value="CatalogUrlRewrite"/> + <stoty value="Subcategory 'liquid-hand-soap' is not opened in category 'soap'"/> <title value="Category with restricted Url Key cannot be created"/> <description value="Category with restricted Url Key cannot be created"/> <severity value="MAJOR"/> @@ -23,16 +24,16 @@ <after> <!--Delete created categories--> <comment userInput="Delete created categories" stepKey="commentDeleteCreatedCategories"/> - <actionGroup ref="deleteCategoryByName" stepKey="deleteAdminCategory"> + <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteAdminCategory"> <argument name="categoryName" value="admin"/> </actionGroup> - <actionGroup ref="deleteCategoryByName" stepKey="deleteSoapCategory"> + <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteSoapCategory"> <argument name="categoryName" value="soap"/> </actionGroup> - <actionGroup ref="deleteCategoryByName" stepKey="deleteRestCategory"> + <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteRestCategory"> <argument name="categoryName" value="rest"/> </actionGroup> - <actionGroup ref="deleteCategoryByName" stepKey="deleteGraphQlCategory"> + <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteGraphQlCategory"> <argument name="categoryName" value="graphql"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> From c9060b41f1c603d1b7ee6cd30fa603ed3673bce1 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Fri, 16 Aug 2019 17:56:36 +0300 Subject: [PATCH 0408/2437] MC-17869: Cart Total is shown as NaN when 100% discount applied through Cart Rule - Grand totals correct rounding added. --- .../Quote/Model/Quote/Address/Total/Grand.php | 57 +++++++++++++------ .../Model/Quote/Address/Total/GrandTest.php | 45 ++++++++++++--- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php index 13f41f909d43f..2cefec2c9035a 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php @@ -3,43 +3,64 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model\Quote\Address\Total; -class Grand extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Pricing\PriceCurrencyInterface as PriceRounder; +use Magento\Quote\Api\Data\ShippingAssignmentInterface as ShippingAssignment; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address\Total; + +/** + * Collect grand totals. + */ +class Grand extends AbstractTotal { + /** + * @var PriceRounder + */ + private $priceRounder; + + /** + * @param PriceRounder|null $priceRounder + */ + public function __construct(?PriceRounder $priceRounder) + { + $this->priceRounder = $priceRounder?: ObjectManager::getInstance()->get(PriceRounder::class); + } + /** * Collect grand total address amount * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @return $this + * @param Quote $quote + * @param ShippingAssignment $shippingAssignment + * @param Total $total + * @return Grand * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function collect( - \Magento\Quote\Model\Quote $quote, - \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, - \Magento\Quote\Model\Quote\Address\Total $total - ) { - $grandTotal = $total->getGrandTotal(); - $baseGrandTotal = $total->getBaseGrandTotal(); + public function collect(Quote $quote, ShippingAssignment $shippingAssignment, Total $total): Grand + { $totals = array_sum($total->getAllTotalAmounts()); $baseTotals = array_sum($total->getAllBaseTotalAmounts()); + $grandTotal = $this->priceRounder->roundPrice($total->getGrandTotal() + $totals, 4); + $baseGrandTotal = $this->priceRounder->roundPrice($total->getBaseGrandTotal() + $baseTotals, 4); - $total->setGrandTotal($grandTotal + $totals); - $total->setBaseGrandTotal($baseGrandTotal + $baseTotals); + $total->setGrandTotal($grandTotal); + $total->setBaseGrandTotal($baseGrandTotal); return $this; } /** * Add grand total information to address * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @return $this + * @param Quote $quote + * @param Total $total + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) + public function fetch(Quote $quote, Total $total): array { return [ 'code' => $this->getCode(), diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php index 34c51f294c05f..6771583b5bbb0 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php @@ -6,18 +6,43 @@ namespace Magento\Quote\Test\Unit\Model\Quote\Address\Total; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Address\Total\Grand; +use Magento\Framework\Pricing\PriceCurrencyInterface as PriceRounder; +use PHPUnit_Framework_MockObject_MockObject as ObjectMock; +use PHPUnit\Framework\TestCase; -class GrandTest extends \PHPUnit\Framework\TestCase +/** + * Grand totals collector test. + */ +class GrandTest extends TestCase { /** - * @var \Magento\Quote\Model\Quote\Address\Total\Grand + * @var PriceRounder|ObjectMock + */ + private $priceRounder; + + /** + * @var Grand */ - protected $model; + private $model; + /** + * @inheritDoc + */ protected function setUp() { - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject(\Magento\Quote\Model\Quote\Address\Total\Grand::class); + $this->priceRounder = $this->getMockBuilder(PriceRounder::class) + ->disableOriginalConstructor() + ->setMethods(['roundPrice']) + ->getMockForAbstractClass(); + + $helper = new ObjectManager($this); + $this->model = $helper->getObject( + Grand::class, + [ + 'priceRounder' => $this->priceRounder, + ] + ); } public function testCollect() @@ -27,14 +52,20 @@ public function testCollect() $grandTotal = 6.4; // 1 + 2 + 3.4 $grandTotalBase = 15.7; // 4 + 5 + 6.7 - $totalMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Total::class, [ + $this->priceRounder->expects($this->at(0))->method('roundPrice')->willReturn($grandTotal + 2); + $this->priceRounder->expects($this->at(1))->method('roundPrice')->willReturn($grandTotalBase + 2); + + $totalMock = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address\Total::class, + [ 'getAllTotalAmounts', 'getAllBaseTotalAmounts', 'setGrandTotal', 'setBaseGrandTotal', 'getGrandTotal', 'getBaseGrandTotal' - ]); + ] + ); $totalMock->expects($this->once())->method('getGrandTotal')->willReturn(2); $totalMock->expects($this->once())->method('getBaseGrandTotal')->willReturn(2); $totalMock->expects($this->once())->method('getAllTotalAmounts')->willReturn($totals); From 0b8838ccfc4d5d7b86f8646969ec38fddbed2904 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <26227105+MilaLesechko@users.noreply.github.com> Date: Tue, 20 Aug 2019 10:21:53 -0500 Subject: [PATCH 0409/2437] Convert AdvancedSearchEntityTest to MFTF --- .../Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml index e87ee83c3c18a..3fbe9f86eab96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CustomAttributeData.xml @@ -63,4 +63,8 @@ <data key="attribute_code">description</data> <data key="value"><p>dfj_full</p></data> </entity> + <entity name="ProductShortDescriptionAdvancedSearchADC123" type="custom_attribute"> + <data key="attribute_code">short_description</data> + <data key="value"><p>dfj_short</p></data> + </entity> </entities> From 4f93f813049504bb89bd9be8826fc9a3358cf2a4 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <26227105+MilaLesechko@users.noreply.github.com> Date: Tue, 20 Aug 2019 10:22:00 -0500 Subject: [PATCH 0410/2437] Convert AdvancedSearchEntityTest to MFTF --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 70d58be4b0df8..378e41e933fe7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1236,5 +1236,6 @@ <data key="weight">1</data> <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">ProductDescriptionAdvancedSearchADC123</requiredEntity> + <requiredEntity type="custom_attribute_array">ProductShortDescriptionAdvancedSearchADC123</requiredEntity> </entity> </entities> From 491790e2fec093d8b26c2a52d0aa987b8d1fee61 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <26227105+MilaLesechko@users.noreply.github.com> Date: Tue, 20 Aug 2019 10:22:05 -0500 Subject: [PATCH 0411/2437] Convert AdvancedSearchEntityTest to MFTF --- .../StorefrontAdvancedSearchByPartialShortDescriptionTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml index 2afb4b5a341c1..0edc3f31216bb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialShortDescriptionTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> - <argument name="short_description" value="dfj_short"/> + <argument name="short_description" value="abc_short"/> </actionGroup> </test> </tests> From e6cfdb3152d302c092b3420437e481450e28ea7f Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Tue, 20 Aug 2019 13:21:24 -0500 Subject: [PATCH 0412/2437] MC-19500: fix related-product changes affection on third party extensions --- .../Magento/Catalog/view/frontend/web/js/related-products.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/web/js/related-products.js b/app/code/Magento/Catalog/view/frontend/web/js/related-products.js index d17c25d421e02..5845b6d7afe38 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/related-products.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/related-products.js @@ -26,8 +26,8 @@ define([ * @private */ _create: function () { - $(this.options.selectAllLink).on('click', $.proxy(this._selectAllRelated, this)); - $(this.options.relatedCheckbox).on('click', $.proxy(this._addRelatedToProduct, this)); + $(this.options.selectAllLink, this.element).on('click', $.proxy(this._selectAllRelated, this)); + $(this.options.relatedCheckbox, this.element).on('click', $.proxy(this._addRelatedToProduct, this)); this._showRelatedProducts( this.element.find(this.options.elementsSelector), this.element.data('limit'), From 9c4c3a05c7c14de1c1fd3d28f56bd409cd9f2a39 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 21 Aug 2019 09:24:51 +0300 Subject: [PATCH 0413/2437] MC-19415: Displayed incorrect price - revert MC-17919 --- .../Indexer/Stock/DefaultStock.php | 10 ++- .../Model/Indexer/Stock/Action/FullTest.php | 71 ++++--------------- 2 files changed, 21 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php index 3670b93b8cb48..519466c505539 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php @@ -230,6 +230,8 @@ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = f { $connection = $this->getConnection(); $qtyExpr = $connection->getCheckSql('cisi.qty > 0', 'cisi.qty', 0); + $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $linkField = $metadata->getLinkField(); $select = $connection->select()->from( ['e' => $this->getTable('catalog_product_entity')], @@ -243,6 +245,12 @@ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = f ['cisi' => $this->getTable('cataloginventory_stock_item')], 'cisi.stock_id = cis.stock_id AND cisi.product_id = e.entity_id', [] + )->joinInner( + ['mcpei' => $this->getTable('catalog_product_entity_int')], + 'e.' . $linkField . ' = mcpei.' . $linkField + . ' AND mcpei.attribute_id = ' . $this->_getAttribute('status')->getId() + . ' AND mcpei.value = ' . ProductStatus::STATUS_ENABLED, + [] )->columns( ['qty' => $qtyExpr] )->where( @@ -284,7 +292,6 @@ protected function _prepareIndexTable($entityIds = null) */ protected function _updateIndex($entityIds) { - $this->deleteOldRecords($entityIds); $connection = $this->getConnection(); $select = $this->_getStockStatusSelect($entityIds, true); $select = $this->getQueryProcessorComposite()->processQuery($select, $entityIds, true); @@ -307,6 +314,7 @@ protected function _updateIndex($entityIds) } } + $this->deleteOldRecords($entityIds); $this->_updateIndexTable($data); return $this; diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Indexer/Stock/Action/FullTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Indexer/Stock/Action/FullTest.php index 00c13619ff2c1..6bf1f5fbf0be2 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Indexer/Stock/Action/FullTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/Indexer/Stock/Action/FullTest.php @@ -5,42 +5,24 @@ */ namespace Magento\CatalogInventory\Model\Indexer\Stock\Action; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\ObjectManagerInterface; -use Magento\CatalogInventory\Model\Indexer\Stock\Processor; -use Magento\Catalog\Model\CategoryFactory; -use Magento\Catalog\Block\Product\ListProduct; -use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\Catalog\Model\Product; -use PHPUnit\Framework\TestCase; - /** * Full reindex Test */ -class FullTest extends TestCase +class FullTest extends \PHPUnit\Framework\TestCase { /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var Processor + * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor */ protected $_processor; - /** - * @inheritdoc - */ protected function setUp() { - $this->objectManager = Bootstrap::getObjectManager(); - $this->_processor = $this->objectManager->get(Processor::class); + $this->_processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class + ); } /** - * Reindex all - * * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoDataFixture Magento/Catalog/_files/product_simple.php @@ -49,9 +31,13 @@ public function testReindexAll() { $this->_processor->reindexAll(); - $categoryFactory = $this->objectManager->get(CategoryFactory::class); - /** @var ListProduct $listProduct */ - $listProduct = $this->objectManager->get(ListProduct::class); + $categoryFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Catalog\Model\CategoryFactory::class + ); + /** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */ + $listProduct = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Catalog\Block\Product\ListProduct::class + ); $category = $categoryFactory->create()->load(2); $layer = $listProduct->getLayer(); @@ -75,37 +61,4 @@ public function testReindexAll() $this->assertEquals(100, $product->getQty()); } } - - /** - * Reindex with disabled product - * - * @return void - * @magentoDbIsolation disabled - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Catalog/_files/products_with_layered_navigation_attribute.php - */ - public function testReindexAllWithDisabledProduct(): void - { - $productCollectionFactory = $this->objectManager->get(CollectionFactory::class); - $productCollection = $productCollectionFactory - ->create() - ->addAttributeToSelect('*') - ->addAttributeToFilter('sku', ['eq' => 'simple3']) - ->addAttributeToSort('created_at', 'DESC') - ->joinField( - 'stock_status', - 'cataloginventory_stock_status', - 'stock_status', - 'product_id=entity_id', - '{{table}}.stock_id=1', - 'left' - )->load(); - - $this->assertCount(1, $productCollection); - - /** @var Product $product */ - foreach ($productCollection as $product) { - $this->assertEquals(1, $product->getData('stock_status')); - } - } } From 39504ceabde0a171af102826095a89df2ab9d058 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 21 Aug 2019 06:43:30 +0000 Subject: [PATCH 0414/2437] MC-19051: [backport for 2.3.3] Nightly build jobs were failing lately after un-skipping tests in MC-5777 --- .../Catalog/Model/Product/Type/Price.php | 5 +- .../Model/Indexer/ReindexRuleProduct.php | 51 ++++--- .../Model/Indexer/ReindexRuleProductPrice.php | 75 +++++----- .../Product/CollectionProcessor.php | 2 +- ...ProductSelectBuilderByCatalogRulePrice.php | 3 +- ...CatalogProductCollectionPricesObserver.php | 2 +- .../ProcessAdminFinalPriceObserver.php | 3 +- .../ProcessFrontFinalPriceObserver.php | 2 +- .../Pricing/Price/CatalogRulePrice.php | 2 +- .../Indexer/ReindexRuleProductPriceTest.php | 131 ++++++++--------- .../Model/Indexer/ReindexRuleProductTest.php | 135 +++++++++++------- .../Pricing/Price/CatalogRulePriceTest.php | 8 +- 12 files changed, 238 insertions(+), 181 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php index dc73baef3f768..23a058b5a57c9 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php @@ -596,7 +596,10 @@ public function calculatePrice( ) { \Magento\Framework\Profiler::start('__PRODUCT_CALCULATE_PRICE__'); if ($wId instanceof Store) { + $sId = $wId->getId(); $wId = $wId->getWebsiteId(); + } else { + $sId = $this->_storeManager->getWebsite($wId)->getDefaultGroup()->getDefaultStoreId(); } $finalPrice = $basePrice; @@ -610,7 +613,7 @@ public function calculatePrice( ); if ($rulePrice === false) { - $date = $this->_localeDate->date(null, null, false); + $date = $this->_localeDate->scopeDate($sId); $rulePrice = $this->_ruleFactory->create()->getRulePrice($date, $wId, $gId, $productId); } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index 55a234bb8ae27..e589c8595ce2c 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -8,7 +8,10 @@ use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; -use Magento\Framework\App\ObjectManager; +use Magento\CatalogRule\Model\Rule; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\ScopeInterface; /** * Reindex rule relations with products. @@ -16,7 +19,7 @@ class ReindexRuleProduct { /** - * @var \Magento\Framework\App\ResourceConnection + * @var ResourceConnection */ private $resource; @@ -31,36 +34,40 @@ class ReindexRuleProduct private $tableSwapper; /** - * @param \Magento\Framework\App\ResourceConnection $resource + * @var TimezoneInterface + */ + private $localeDate; + + /** + * @param ResourceConnection $resource * @param ActiveTableSwitcher $activeTableSwitcher - * @param TableSwapper|null $tableSwapper + * @param TableSwapper $tableSwapper + * @param TimezoneInterface $localeDate */ public function __construct( - \Magento\Framework\App\ResourceConnection $resource, + ResourceConnection $resource, ActiveTableSwitcher $activeTableSwitcher, - TableSwapper $tableSwapper = null + TableSwapper $tableSwapper, + TimezoneInterface $localeDate ) { $this->resource = $resource; $this->activeTableSwitcher = $activeTableSwitcher; - $this->tableSwapper = $tableSwapper ?? - ObjectManager::getInstance()->get(TableSwapper::class); + $this->tableSwapper = $tableSwapper; + $this->localeDate = $localeDate; } /** * Reindex information about rule relations with products. * - * @param \Magento\CatalogRule\Model\Rule $rule + * @param Rule $rule * @param int $batchCount * @param bool $useAdditionalTable * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function execute( - \Magento\CatalogRule\Model\Rule $rule, - $batchCount, - $useAdditionalTable = false - ) { + public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) + { if (!$rule->getIsActive() || empty($rule->getWebsiteIds())) { return false; } @@ -84,21 +91,26 @@ public function execute( $ruleId = $rule->getId(); $customerGroupIds = $rule->getCustomerGroupIds(); - $fromTime = strtotime($rule->getFromDate()); - $toTime = strtotime($rule->getToDate()); - $toTime = $toTime ? $toTime + \Magento\CatalogRule\Model\Indexer\IndexBuilder::SECONDS_IN_DAY - 1 : 0; $sortOrder = (int)$rule->getSortOrder(); $actionOperator = $rule->getSimpleAction(); $actionAmount = $rule->getDiscountAmount(); $actionStop = $rule->getStopRulesProcessing(); $rows = []; + foreach ($websiteIds as $websiteId) { + $scopeTz = new \DateTimeZone( + $this->localeDate->getConfigTimezone(ScopeInterface::SCOPE_WEBSITE, $websiteId) + ); + $fromTime = (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp(); + $toTime = $rule->getToDate() + ? (new \DateTime($rule->getToDate(), $scopeTz))->getTimestamp() + IndexBuilder::SECONDS_IN_DAY - 1 + : 0; - foreach ($productIds as $productId => $validationByWebsite) { - foreach ($websiteIds as $websiteId) { + foreach ($productIds as $productId => $validationByWebsite) { if (empty($validationByWebsite[$websiteId])) { continue; } + foreach ($customerGroupIds as $customerGroupId) { $rows[] = [ 'rule_id' => $ruleId, @@ -123,6 +135,7 @@ public function execute( if (!empty($rows)) { $connection->insertMultiple($indexTable, $rows); } + return true; } } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php index 6a87be3c50a64..11ba87730bec1 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php @@ -6,54 +6,58 @@ namespace Magento\CatalogRule\Model\Indexer; +use Magento\Catalog\Model\Product; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\StoreManagerInterface; + /** * Reindex product prices according rule settings. */ class ReindexRuleProductPrice { /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ private $storeManager; /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder + * @var RuleProductsSelectBuilder */ private $ruleProductsSelectBuilder; /** - * @var \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator + * @var ProductPriceCalculator */ private $productPriceCalculator; /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var TimezoneInterface */ - private $dateTime; + private $localeDate; /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor + * @var RuleProductPricesPersistor */ private $pricesPersistor; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager * @param RuleProductsSelectBuilder $ruleProductsSelectBuilder * @param ProductPriceCalculator $productPriceCalculator - * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime - * @param \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor + * @param TimezoneInterface $localeDate + * @param RuleProductPricesPersistor $pricesPersistor */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder $ruleProductsSelectBuilder, - \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator $productPriceCalculator, - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime, - \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor $pricesPersistor + StoreManagerInterface $storeManager, + RuleProductsSelectBuilder $ruleProductsSelectBuilder, + ProductPriceCalculator $productPriceCalculator, + TimezoneInterface $localeDate, + RuleProductPricesPersistor $pricesPersistor ) { $this->storeManager = $storeManager; $this->ruleProductsSelectBuilder = $ruleProductsSelectBuilder; $this->productPriceCalculator = $productPriceCalculator; - $this->dateTime = $dateTime; + $this->localeDate = $localeDate; $this->pricesPersistor = $pricesPersistor; } @@ -61,22 +65,16 @@ public function __construct( * Reindex product prices. * * @param int $batchCount - * @param \Magento\Catalog\Model\Product|null $product + * @param Product|null $product * @param bool $useAdditionalTable * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function execute( - $batchCount, - \Magento\Catalog\Model\Product $product = null, - $useAdditionalTable = false - ) { - $fromDate = mktime(0, 0, 0, date('m'), date('d') - 1); - $toDate = mktime(0, 0, 0, date('m'), date('d') + 1); - + public function execute($batchCount, Product $product = null, $useAdditionalTable = false) + { /** * Update products rules prices per each website separately - * because of max join limit in mysql + * because for each website date in website's timezone should be used */ foreach ($this->storeManager->getWebsites() as $website) { $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $product, $useAdditionalTable); @@ -84,6 +82,13 @@ public function execute( $stopFlags = []; $prevKey = null; + $storeGroup = $this->storeManager->getGroup($website->getDefaultGroupId()); + $currentDate = $this->localeDate->scopeDate($storeGroup->getDefaultStoreId(), null, true); + $previousDate = (clone $currentDate)->modify('-1 day'); + $previousDate->setTime(23, 59, 59); + $nextDate = (clone $currentDate)->modify('+1 day'); + $nextDate->setTime(0, 0, 0); + while ($ruleData = $productsStmt->fetch()) { $ruleProductId = $ruleData['product_id']; $productKey = $ruleProductId . @@ -100,12 +105,11 @@ public function execute( } } - $ruleData['from_time'] = $this->roundTime($ruleData['from_time']); - $ruleData['to_time'] = $this->roundTime($ruleData['to_time']); /** * Build prices for each day */ - for ($time = $fromDate; $time <= $toDate; $time += IndexBuilder::SECONDS_IN_DAY) { + foreach ([$previousDate, $currentDate, $nextDate] as $date) { + $time = $date->getTimestamp(); if (($ruleData['from_time'] == 0 || $time >= $ruleData['from_time']) && ($ruleData['to_time'] == 0 || $time <= $ruleData['to_time']) @@ -118,7 +122,7 @@ public function execute( if (!isset($dayPrices[$priceKey])) { $dayPrices[$priceKey] = [ - 'rule_date' => $time, + 'rule_date' => $date, 'website_id' => $ruleData['website_id'], 'customer_group_id' => $ruleData['customer_group_id'], 'product_id' => $ruleProductId, @@ -151,18 +155,7 @@ public function execute( } $this->pricesPersistor->execute($dayPrices, $useAdditionalTable); } - return true; - } - /** - * @param int $timeStamp - * @return int - */ - private function roundTime($timeStamp) - { - if (is_numeric($timeStamp) && $timeStamp != 0) { - $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00', $timeStamp)); - } - return $timeStamp; + return true; } } diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/CollectionProcessor.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/CollectionProcessor.php index 0dee9eda5b6e8..1fd6f0cbc986f 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/CollectionProcessor.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/CollectionProcessor.php @@ -90,7 +90,7 @@ public function addPriceData(ProductCollection $productCollection, $joinColumn = ), $connection->quoteInto( 'catalog_rule.rule_date = ?', - $this->dateTime->formatDate($this->localeDate->date(null, null, false), false) + $this->dateTime->formatDate($this->localeDate->scopeDate($store->getId()), false) ), ] ), diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php index 02d2631058a1a..48c463fc18b80 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php @@ -88,7 +88,8 @@ public function __construct( */ public function build($productId) { - $currentDate = $this->dateTime->formatDate($this->localeDate->date(null, null, false), false); + $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore()); + $currentDate = $this->dateTime->formatDate($timestamp, false); $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); diff --git a/app/code/Magento/CatalogRule/Observer/PrepareCatalogProductCollectionPricesObserver.php b/app/code/Magento/CatalogRule/Observer/PrepareCatalogProductCollectionPricesObserver.php index a635c5611eff6..bf0c85e671dd7 100644 --- a/app/code/Magento/CatalogRule/Observer/PrepareCatalogProductCollectionPricesObserver.php +++ b/app/code/Magento/CatalogRule/Observer/PrepareCatalogProductCollectionPricesObserver.php @@ -105,7 +105,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($observer->getEvent()->hasDate()) { $date = new \DateTime($observer->getEvent()->getDate()); } else { - $date = $this->localeDate->date(null, null, false); + $date = (new \DateTime())->setTimestamp($this->localeDate->scopeTimeStamp($store)); } $productIds = []; diff --git a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php index 2fd23ae391474..89ed519cfb8c8 100644 --- a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php +++ b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php @@ -65,7 +65,8 @@ public function __construct( public function execute(\Magento\Framework\Event\Observer $observer) { $product = $observer->getEvent()->getProduct(); - $date = $this->localeDate->date(null, null, false); + $storeId = $product->getStoreId(); + $date = $this->localeDate->scopeDate($storeId); $key = false; $ruleData = $this->coreRegistry->registry('rule_data'); diff --git a/app/code/Magento/CatalogRule/Observer/ProcessFrontFinalPriceObserver.php b/app/code/Magento/CatalogRule/Observer/ProcessFrontFinalPriceObserver.php index b27768ae091ed..075fe9e51f7dc 100644 --- a/app/code/Magento/CatalogRule/Observer/ProcessFrontFinalPriceObserver.php +++ b/app/code/Magento/CatalogRule/Observer/ProcessFrontFinalPriceObserver.php @@ -80,7 +80,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) if ($observer->hasDate()) { $date = new \DateTime($observer->getEvent()->getDate()); } else { - $date = $this->localeDate->date(null, null, false); + $date = $this->localeDate->scopeDate($storeId); } if ($observer->hasWebsiteId()) { diff --git a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php index 8bce5456ffa72..7cbbc547571ab 100644 --- a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php @@ -88,7 +88,7 @@ public function getValue() $this->value = (float)$this->product->getData(self::PRICE_CODE) ?: false; } else { $this->value = $this->ruleResource->getRulePrice( - $this->dateTime->date(null, null, false), + $this->dateTime->scopeDate($this->storeManager->getStore()->getId()), $this->storeManager->getStore()->getWebsiteId(), $this->customerSession->getCustomerGroupId(), $this->product->getId() diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php index 6d7f0673ed281..5f63283df6760 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php @@ -6,65 +6,62 @@ namespace Magento\CatalogRule\Test\Unit\Model\Indexer; -use Magento\CatalogRule\Model\Indexer\IndexBuilder; +use Magento\Catalog\Model\Product; +use Magento\CatalogRule\Model\Indexer\ProductPriceCalculator; +use Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice; +use Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor; +use Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Api\Data\GroupInterface; +use Magento\Store\Api\Data\WebsiteInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; class ReindexRuleProductPriceTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice + * @var ReindexRuleProductPrice */ private $model; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var RuleProductsSelectBuilder|MockObject */ private $ruleProductsSelectBuilderMock; /** - * @var \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator|\PHPUnit_Framework_MockObject_MockObject + * @var ProductPriceCalculator|MockObject */ private $productPriceCalculatorMock; /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ - private $dateTimeMock; + private $localeDate; /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor|\PHPUnit_Framework_MockObject_MockObject + * @var RuleProductPricesPersistor|MockObject */ private $pricesPersistorMock; protected function setUp() { - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->ruleProductsSelectBuilderMock = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->productPriceCalculatorMock = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ProductPriceCalculator::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dateTimeMock = $this->getMockBuilder(\Magento\Framework\Stdlib\DateTime\DateTime::class) - ->disableOriginalConstructor() - ->getMock(); - $this->pricesPersistorMock = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\RuleProductPricesPersistor::class) - ->disableOriginalConstructor() - ->getMock(); - $this->model = new \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice( + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->ruleProductsSelectBuilderMock = $this->createMock(RuleProductsSelectBuilder::class); + $this->productPriceCalculatorMock = $this->createMock(ProductPriceCalculator::class); + $this->localeDate = $this->createMock(TimezoneInterface::class); + $this->pricesPersistorMock = $this->createMock(RuleProductPricesPersistor::class); + + $this->model = new ReindexRuleProductPrice( $this->storeManagerMock, $this->ruleProductsSelectBuilderMock, $this->productPriceCalculatorMock, - $this->dateTimeMock, + $this->localeDate, $this->pricesPersistorMock ); } @@ -72,19 +69,32 @@ protected function setUp() public function testExecute() { $websiteId = 234; - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); - - $websiteMock = $this->getMockBuilder(\Magento\Store\Api\Data\WebsiteInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $websiteMock->expects($this->once())->method('getId')->willReturn($websiteId); - $this->storeManagerMock->expects($this->once())->method('getWebsites')->willReturn([$websiteMock]); - - $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class) - ->disableOriginalConstructor() - ->getMock(); + $defaultGroupId = 11; + $defaultStoreId = 22; + + $websiteMock = $this->createMock(WebsiteInterface::class); + $websiteMock->expects($this->once()) + ->method('getId') + ->willReturn($websiteId); + $websiteMock->expects($this->once()) + ->method('getDefaultGroupId') + ->willReturn($defaultGroupId); + $this->storeManagerMock->expects($this->once()) + ->method('getWebsites') + ->willReturn([$websiteMock]); + $groupMock = $this->createMock(GroupInterface::class); + $groupMock->method('getId') + ->willReturn($defaultStoreId); + $groupMock->expects($this->once()) + ->method('getDefaultStoreId') + ->willReturn($defaultStoreId); + $this->storeManagerMock->expects($this->once()) + ->method('getGroup') + ->with($defaultGroupId) + ->willReturn($groupMock); + + $productMock = $this->createMock(Product::class); + $statementMock = $this->createMock(\Zend_Db_Statement_Interface::class); $this->ruleProductsSelectBuilderMock->expects($this->once()) ->method('build') ->with($websiteId, $productMock, true) @@ -99,29 +109,22 @@ public function testExecute() 'action_stop' => true ]; - $this->dateTimeMock->expects($this->at(0)) - ->method('date') - ->with('Y-m-d 00:00:00', $ruleData['from_time']) - ->willReturn($ruleData['from_time']); - $this->dateTimeMock->expects($this->at(1)) - ->method('timestamp') - ->with($ruleData['from_time']) - ->willReturn($ruleData['from_time']); - - $this->dateTimeMock->expects($this->at(2)) - ->method('date') - ->with('Y-m-d 00:00:00', $ruleData['to_time']) - ->willReturn($ruleData['to_time']); - $this->dateTimeMock->expects($this->at(3)) - ->method('timestamp') - ->with($ruleData['to_time']) - ->willReturn($ruleData['to_time']); - - $statementMock->expects($this->at(0))->method('fetch')->willReturn($ruleData); - $statementMock->expects($this->at(1))->method('fetch')->willReturn(false); - - $this->productPriceCalculatorMock->expects($this->atLeastOnce())->method('calculate'); - $this->pricesPersistorMock->expects($this->once())->method('execute'); + $this->localeDate->expects($this->once()) + ->method('scopeDate') + ->with($defaultStoreId, null, true) + ->willReturn(new \DateTime()); + + $statementMock->expects($this->at(0)) + ->method('fetch') + ->willReturn($ruleData); + $statementMock->expects($this->at(1)) + ->method('fetch') + ->willReturn(false); + + $this->productPriceCalculatorMock->expects($this->atLeastOnce()) + ->method('calculate'); + $this->pricesPersistorMock->expects($this->once()) + ->method('execute'); $this->assertTrue($this->model->execute(1, $productMock, true)); } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php index 0dbbaee8d2871..ff566fa3cc774 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php @@ -8,89 +8,107 @@ use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface; +use Magento\CatalogRule\Model\Indexer\ReindexRuleProduct; +use Magento\CatalogRule\Model\Rule; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\MockObject\MockObject; class ReindexRuleProductTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct + * @var ReindexRuleProduct */ private $model; /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + * @var ResourceConnection|MockObject */ private $resourceMock; /** - * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject + * @var ActiveTableSwitcher|MockObject */ private $activeTableSwitcherMock; /** - * @var IndexerTableSwapperInterface|\PHPUnit_Framework_MockObject_MockObject + * @var IndexerTableSwapperInterface|MockObject */ private $tableSwapperMock; + /** + * @var TimezoneInterface|MockObject + */ + private $localeDateMock; + protected function setUp() { - $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $this->activeTableSwitcherMock = $this->getMockBuilder(ActiveTableSwitcher::class) - ->disableOriginalConstructor() - ->getMock(); - $this->tableSwapperMock = $this->getMockForAbstractClass( - IndexerTableSwapperInterface::class - ); - $this->model = new \Magento\CatalogRule\Model\Indexer\ReindexRuleProduct( + $this->resourceMock = $this->createMock(ResourceConnection::class); + $this->activeTableSwitcherMock = $this->createMock(ActiveTableSwitcher::class); + $this->tableSwapperMock = $this->createMock(IndexerTableSwapperInterface::class); + $this->localeDateMock = $this->createMock(TimezoneInterface::class); + + $this->model = new ReindexRuleProduct( $this->resourceMock, $this->activeTableSwitcherMock, - $this->tableSwapperMock + $this->tableSwapperMock, + $this->localeDateMock ); } public function testExecuteIfRuleInactive() { - $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) - ->disableOriginalConstructor() - ->getMock(); - $ruleMock->expects($this->once())->method('getIsActive')->willReturn(false); + $ruleMock = $this->createMock(Rule::class); + $ruleMock->expects($this->once()) + ->method('getIsActive') + ->willReturn(false); $this->assertFalse($this->model->execute($ruleMock, 100, true)); } public function testExecuteIfRuleWithoutWebsiteIds() { - $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) - ->disableOriginalConstructor() - ->getMock(); - $ruleMock->expects($this->once())->method('getIsActive')->willReturn(true); - $ruleMock->expects($this->once())->method('getWebsiteIds')->willReturn(null); + $ruleMock = $this->createMock(Rule::class); + $ruleMock->expects($this->once()) + ->method('getIsActive') + ->willReturn(true); + $ruleMock->expects($this->once()) + ->method('getWebsiteIds') + ->willReturn(null); $this->assertFalse($this->model->execute($ruleMock, 100, true)); } public function testExecute() { + $websiteId = 3; + $websiteTz = 'America/Los_Angeles'; $productIds = [ - 4 => [1 => 1], - 5 => [1 => 1], - 6 => [1 => 1], + 4 => [$websiteId => 1], + 5 => [$websiteId => 1], + 6 => [$websiteId => 1], ]; - $ruleMock = $this->getMockBuilder(\Magento\CatalogRule\Model\Rule::class) - ->disableOriginalConstructor() - ->getMock(); - $ruleMock->expects($this->once())->method('getIsActive')->willReturn(true); - $ruleMock->expects($this->exactly(2))->method('getWebsiteIds')->willReturn(1); - $ruleMock->expects($this->once())->method('getMatchingProductIds')->willReturn($productIds); + + $ruleMock = $this->createMock(Rule::class); + $ruleMock->expects($this->once()) + ->method('getIsActive') + ->willReturn(true); + $ruleMock->expects($this->exactly(2)) + ->method('getWebsiteIds') + ->willReturn([$websiteId]); + $ruleMock->expects($this->once()) + ->method('getMatchingProductIds') + ->willReturn($productIds); $this->tableSwapperMock->expects($this->once()) ->method('getWorkingTableName') ->with('catalogrule_product') ->willReturn('catalogrule_product_replica'); - $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); + $connectionMock = $this->createMock(AdapterInterface::class); + $this->resourceMock->expects($this->at(0)) + ->method('getConnection') + ->willReturn($connectionMock); $this->resourceMock->expects($this->at(1)) ->method('getTableName') ->with('catalogrule_product') @@ -100,21 +118,42 @@ public function testExecute() ->with('catalogrule_product_replica') ->willReturn('catalogrule_product_replica'); - $ruleMock->expects($this->once())->method('getId')->willReturn(100); - $ruleMock->expects($this->once())->method('getCustomerGroupIds')->willReturn([10]); - $ruleMock->expects($this->once())->method('getFromDate')->willReturn('2017-06-21'); - $ruleMock->expects($this->once())->method('getToDate')->willReturn('2017-06-30'); - $ruleMock->expects($this->once())->method('getSortOrder')->willReturn(1); - $ruleMock->expects($this->once())->method('getSimpleAction')->willReturn('simple_action'); - $ruleMock->expects($this->once())->method('getDiscountAmount')->willReturn(43); - $ruleMock->expects($this->once())->method('getStopRulesProcessing')->willReturn(true); + $ruleMock->expects($this->once()) + ->method('getId') + ->willReturn(100); + $ruleMock->expects($this->once()) + ->method('getCustomerGroupIds') + ->willReturn([10]); + $ruleMock->expects($this->atLeastOnce()) + ->method('getFromDate') + ->willReturn('2017-06-21'); + $ruleMock->expects($this->atLeastOnce()) + ->method('getToDate') + ->willReturn('2017-06-30'); + $ruleMock->expects($this->once()) + ->method('getSortOrder') + ->willReturn(1); + $ruleMock->expects($this->once()) + ->method('getSimpleAction') + ->willReturn('simple_action'); + $ruleMock->expects($this->once()) + ->method('getDiscountAmount') + ->willReturn(43); + $ruleMock->expects($this->once()) + ->method('getStopRulesProcessing') + ->willReturn(true); + + $this->localeDateMock->expects($this->once()) + ->method('getConfigTimezone') + ->with(ScopeInterface::SCOPE_WEBSITE, $websiteId) + ->willReturn($websiteTz); $batchRows = [ [ 'rule_id' => 100, 'from_time' => 1498028400, 'to_time' => 1498892399, - 'website_id' => 1, + 'website_id' => $websiteId, 'customer_group_id' => 10, 'product_id' => 4, 'action_operator' => 'simple_action', @@ -126,7 +165,7 @@ public function testExecute() 'rule_id' => 100, 'from_time' => 1498028400, 'to_time' => 1498892399, - 'website_id' => 1, + 'website_id' => $websiteId, 'customer_group_id' => 10, 'product_id' => 5, 'action_operator' => 'simple_action', @@ -141,7 +180,7 @@ public function testExecute() 'rule_id' => 100, 'from_time' => 1498028400, 'to_time' => 1498892399, - 'website_id' => 1, + 'website_id' => $websiteId, 'customer_group_id' => 10, 'product_id' => 6, 'action_operator' => 'simple_action', diff --git a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php index cb1a7f53f752c..7514d2bc4b5c5 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php @@ -112,6 +112,7 @@ protected function setUp() */ public function testGetValue() { + $storeId = 5; $coreWebsiteId = 2; $productId = 4; $customerGroupId = 3; @@ -120,9 +121,12 @@ public function testGetValue() $catalogRulePrice = 55.12; $convertedPrice = 45.34; + $this->coreStoreMock->expects($this->once()) + ->method('getId') + ->willReturn($storeId); $this->dataTimeMock->expects($this->once()) - ->method('date') - ->with(null, null, false) + ->method('scopeDate') + ->with($storeId) ->willReturn($date); $this->coreStoreMock->expects($this->once()) ->method('getWebsiteId') From 8145725564ebe7b0d6030985e1e821bafeec1f15 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 21 Aug 2019 14:00:55 +0300 Subject: [PATCH 0415/2437] MC-13951: Declarative upgrade leads to the re-creation of constraints --- .../Patch/Schema/ChangeTmpTablesEngine.php | 68 ---------------- app/code/Magento/Bundle/etc/db_schema.xml | 6 +- .../Patch/Schema/ChangeTmpTablesEngine.php | 74 ----------------- app/code/Magento/Catalog/etc/db_schema.xml | 38 ++++----- .../Patch/Schema/ChangeTmpTablesEngine.php | 61 -------------- .../CatalogInventory/etc/db_schema.xml | 6 +- .../Patch/Schema/ChangeTmpTablesEngine.php | 61 -------------- .../Magento/Downloadable/etc/db_schema.xml | 2 +- .../Patch/Schema/AddProductIdConstraint.php | 79 ------------------- app/code/Magento/Wishlist/etc/db_schema.xml | 4 + 10 files changed, 30 insertions(+), 369 deletions(-) delete mode 100644 app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php diff --git a/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index c6a67cc5a110c..0000000000000 --- a/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Bundle\Setup\Patch\Schema; - -use Magento\Framework\Setup\Patch\SchemaPatchInterface; -use Magento\Framework\Setup\SchemaSetupInterface; - -/** - * Change engine for temporary tables to InnoDB. - */ -class ChangeTmpTablesEngine implements SchemaPatchInterface -{ - /** - * @var SchemaSetupInterface - */ - private $schemaSetup; - - /** - * @param SchemaSetupInterface $schemaSetup - */ - public function __construct(SchemaSetupInterface $schemaSetup) - { - $this->schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tables = [ - 'catalog_product_index_price_bundle_tmp', - 'catalog_product_index_price_bundle_sel_tmp', - 'catalog_product_index_price_bundle_opt_tmp', - ]; - foreach ($tables as $table) { - $tableName = $this->schemaSetup->getTable($table); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Bundle/etc/db_schema.xml b/app/code/Magento/Bundle/etc/db_schema.xml index 97e86e5c17359..ade8fbf7cf1ce 100644 --- a/app/code/Magento/Bundle/etc/db_schema.xml +++ b/app/code/Magento/Bundle/etc/db_schema.xml @@ -203,7 +203,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_bundle_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_bundle_tmp" resource="default" engine="innodb" comment="Catalog Product Index Price Bundle Tmp"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -265,7 +265,7 @@ <column name="selection_id"/> </constraint> </table> - <table name="catalog_product_index_price_bundle_sel_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_bundle_sel_tmp" resource="default" engine="innodb" comment="Catalog Product Index Price Bundle Sel Tmp"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -320,7 +320,7 @@ <column name="option_id"/> </constraint> </table> - <table name="catalog_product_index_price_bundle_opt_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_bundle_opt_tmp" resource="default" engine="innodb" comment="Catalog Product Index Price Bundle Opt Tmp"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> diff --git a/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index c39247f9b30df..0000000000000 --- a/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Catalog\Setup\Patch\Schema; - -use Magento\Framework\Setup\Patch\SchemaPatchInterface; -use Magento\Framework\Setup\SchemaSetupInterface; - -/** - * Change engine for temporary tables to InnoDB. - */ -class ChangeTmpTablesEngine implements SchemaPatchInterface -{ - /** - * @var SchemaSetupInterface - */ - private $schemaSetup; - - /** - * @param SchemaSetupInterface $schemaSetup - */ - public function __construct(SchemaSetupInterface $schemaSetup) - { - $this->schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tables = [ - 'catalog_product_index_price_cfg_opt_agr_tmp', - 'catalog_product_index_price_cfg_opt_tmp', - 'catalog_product_index_price_final_tmp', - 'catalog_product_index_price_opt_tmp', - 'catalog_product_index_price_opt_agr_tmp', - 'catalog_product_index_eav_tmp', - 'catalog_product_index_eav_decimal_tmp', - 'catalog_product_index_price_tmp', - 'catalog_category_product_index_tmp', - ]; - foreach ($tables as $table) { - $tableName = $this->schemaSetup->getTable($table); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 6fef4ca6e9128..b5e02b1daaa01 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -1238,7 +1238,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_cfg_opt_agr_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_cfg_opt_agr_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Config Option Aggregate Temp Table"> <column xsi:type="int" name="parent_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Parent ID"/> @@ -1279,7 +1279,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_cfg_opt_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_cfg_opt_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Config Option Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1327,7 +1327,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_final_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_final_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Final Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1375,7 +1375,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_opt_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_opt_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Option Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1418,7 +1418,7 @@ <column name="option_id"/> </constraint> </table> - <table name="catalog_product_index_price_opt_agr_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_opt_agr_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Option Aggregate Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1470,7 +1470,7 @@ <column name="value"/> </index> </table> - <table name="catalog_product_index_eav_tmp" resource="default" engine="memory" + <table name="catalog_product_index_eav_tmp" resource="default" engine="innodb" comment="Catalog Product EAV Indexer Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1489,13 +1489,13 @@ <column name="value"/> <column name="source_id"/> </constraint> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_ATTRIBUTE_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_ATTRIBUTE_ID" indexType="btree"> <column name="attribute_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_STORE_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_STORE_ID" indexType="btree"> <column name="store_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_VALUE" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_TMP_VALUE" indexType="btree"> <column name="value"/> </index> </table> @@ -1528,7 +1528,7 @@ <column name="value"/> </index> </table> - <table name="catalog_product_index_eav_decimal_tmp" resource="default" engine="memory" + <table name="catalog_product_index_eav_decimal_tmp" resource="default" engine="innodb" comment="Catalog Product EAV Decimal Indexer Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1547,13 +1547,13 @@ <column name="value"/> <column name="source_id"/> </constraint> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_ATTRIBUTE_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_ATTRIBUTE_ID" indexType="btree"> <column name="attribute_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_STORE_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_STORE_ID" indexType="btree"> <column name="store_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_VALUE" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_EAV_DECIMAL_TMP_VALUE" indexType="btree"> <column name="value"/> </index> </table> @@ -1592,7 +1592,7 @@ <column name="min_price"/> </index> </table> - <table name="catalog_product_index_price_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_tmp" resource="default" engine="innodb" comment="Catalog Product Price Indexer Temp Table"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> @@ -1617,17 +1617,17 @@ <column name="customer_group_id"/> <column name="website_id"/> </constraint> - <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_CUSTOMER_GROUP_ID" indexType="btree"> <column name="customer_group_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> - <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE" indexType="hash"> + <index referenceId="CATALOG_PRODUCT_INDEX_PRICE_TMP_MIN_PRICE" indexType="btree"> <column name="min_price"/> </index> </table> - <table name="catalog_category_product_index_tmp" resource="default" engine="memory" + <table name="catalog_category_product_index_tmp" resource="default" engine="innodb" comment="Catalog Category Product Indexer temporary table"> <column xsi:type="int" name="category_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Category ID"/> @@ -1646,7 +1646,7 @@ <column name="product_id"/> <column name="store_id"/> </constraint> - <index referenceId="CAT_CTGR_PRD_IDX_TMP_PRD_ID_CTGR_ID_STORE_ID" indexType="hash"> + <index referenceId="CAT_CTGR_PRD_IDX_TMP_PRD_ID_CTGR_ID_STORE_ID" indexType="btree"> <column name="product_id"/> <column name="category_id"/> <column name="store_id"/> diff --git a/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index 7f43cd279d4e3..0000000000000 --- a/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogInventory\Setup\Patch\Schema; - -use Magento\Framework\Setup\Patch\SchemaPatchInterface; -use Magento\Framework\Setup\SchemaSetupInterface; - -/** - * Change engine for temporary tables to InnoDB. - */ -class ChangeTmpTablesEngine implements SchemaPatchInterface -{ - /** - * @var SchemaSetupInterface - */ - private $schemaSetup; - - /** - * @param SchemaSetupInterface $schemaSetup - */ - public function __construct(SchemaSetupInterface $schemaSetup) - { - $this->schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tableName = $this->schemaSetup->getTable('cataloginventory_stock_status_tmp'); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/CatalogInventory/etc/db_schema.xml b/app/code/Magento/CatalogInventory/etc/db_schema.xml index 5ac7fedc5aa18..67a200eb37125 100644 --- a/app/code/Magento/CatalogInventory/etc/db_schema.xml +++ b/app/code/Magento/CatalogInventory/etc/db_schema.xml @@ -142,7 +142,7 @@ <column name="website_id"/> </index> </table> - <table name="cataloginventory_stock_status_tmp" resource="default" engine="memory" + <table name="cataloginventory_stock_status_tmp" resource="default" engine="innodb" comment="Cataloginventory Stock Status Indexer Tmp"> <column xsi:type="int" name="product_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Product Id"/> @@ -159,10 +159,10 @@ <column name="website_id"/> <column name="stock_id"/> </constraint> - <index referenceId="CATALOGINVENTORY_STOCK_STATUS_TMP_STOCK_ID" indexType="hash"> + <index referenceId="CATALOGINVENTORY_STOCK_STATUS_TMP_STOCK_ID" indexType="btree"> <column name="stock_id"/> </index> - <index referenceId="CATALOGINVENTORY_STOCK_STATUS_TMP_WEBSITE_ID" indexType="hash"> + <index referenceId="CATALOGINVENTORY_STOCK_STATUS_TMP_WEBSITE_ID" indexType="btree"> <column name="website_id"/> </index> </table> diff --git a/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index caf2f7745a3dd..0000000000000 --- a/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Downloadable\Setup\Patch\Schema; - -use Magento\Framework\Setup\Patch\SchemaPatchInterface; -use Magento\Framework\Setup\SchemaSetupInterface; - -/** - * Change engine for temporary tables to InnoDB. - */ -class ChangeTmpTablesEngine implements SchemaPatchInterface -{ - /** - * @var SchemaSetupInterface - */ - private $schemaSetup; - - /** - * @param SchemaSetupInterface $schemaSetup - */ - public function __construct(SchemaSetupInterface $schemaSetup) - { - $this->schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tableName = $this->schemaSetup->getTable('catalog_product_index_price_downlod_tmp'); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Downloadable/etc/db_schema.xml b/app/code/Magento/Downloadable/etc/db_schema.xml index ccbefa4fb3992..ee7b3c5683ea1 100644 --- a/app/code/Magento/Downloadable/etc/db_schema.xml +++ b/app/code/Magento/Downloadable/etc/db_schema.xml @@ -233,7 +233,7 @@ <column name="website_id"/> </constraint> </table> - <table name="catalog_product_index_price_downlod_tmp" resource="default" engine="memory" + <table name="catalog_product_index_price_downlod_tmp" resource="default" engine="innodb" comment="Temporary Indexer Table for price of downloadable products"> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Entity ID"/> diff --git a/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php b/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php deleted file mode 100644 index 5c65fce10ccd2..0000000000000 --- a/app/code/Magento/Wishlist/Setup/Patch/Schema/AddProductIdConstraint.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Wishlist\Setup\Patch\Schema; - -use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Framework\Setup\Patch\SchemaPatchInterface; -use Magento\Framework\Setup\SchemaSetupInterface; - -/** - * Class AddProductIdConstraint - */ -class AddProductIdConstraint implements SchemaPatchInterface -{ - /** - * @var SchemaSetupInterface - */ - private $schemaSetup; - - /** - * @param SchemaSetupInterface $schemaSetup - */ - public function __construct( - SchemaSetupInterface $schemaSetup - ) { - $this->schemaSetup = $schemaSetup; - } - - /** - * Run code inside patch. - * - * @return void - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $this->schemaSetup->getConnection()->addForeignKey( - $this->schemaSetup->getConnection()->getForeignKeyName( - $this->schemaSetup->getTable('wishlist_item_option'), - 'product_id', - $this->schemaSetup->getTable('catalog_product_entity'), - 'entity_id' - ), - $this->schemaSetup->getTable('wishlist_item_option'), - 'product_id', - $this->schemaSetup->getTable('catalog_product_entity'), - 'entity_id', - AdapterInterface::FK_ACTION_CASCADE, - true - ); - - $this->schemaSetup->endSetup(); - } - - /** - * Get array of patches that have to be executed prior to this. - * - * @return string[] - */ - public static function getDependencies() - { - return []; - } - - /** - * Get aliases (previous names) for the patch. - * - * @return string[] - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Wishlist/etc/db_schema.xml b/app/code/Magento/Wishlist/etc/db_schema.xml index 8a02f411ad0db..a1c50c240a9fb 100644 --- a/app/code/Magento/Wishlist/etc/db_schema.xml +++ b/app/code/Magento/Wishlist/etc/db_schema.xml @@ -77,5 +77,9 @@ <constraint xsi:type="foreign" referenceId="FK_A014B30B04B72DD0EAB3EECD779728D6" table="wishlist_item_option" column="wishlist_item_id" referenceTable="wishlist_item" referenceColumn="wishlist_item_id" onDelete="CASCADE"/> + <constraint xsi:type="foreign" referenceId="WISHLIST_ITEM_OPTION_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID" + table="wishlist_item_option" + column="product_id" referenceTable="catalog_product_entity" referenceColumn="entity_id" + onDelete="CASCADE"/> </table> </schema> From 96f56f1354707933aa7f071d05bd6ae4e8cb2c11 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 21 Aug 2019 14:16:21 +0300 Subject: [PATCH 0416/2437] MC-19415: Displayed incorrect price - revert MC-18009 --- .../Catalog/Model/ProductLink/Search.php | 1 + .../Adminhtml/Product/SearchTest.php | 23 ++----------------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductLink/Search.php b/app/code/Magento/Catalog/Model/ProductLink/Search.php index ad7f3370ab3fe..681c01bb1281b 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Search.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Search.php @@ -60,6 +60,7 @@ public function prepareCollection( ): \Magento\Catalog\Model\ResourceModel\Product\Collection { $productCollection = $this->productCollectionFactory->create(); $productCollection->addAttributeToSelect(ProductInterface::NAME); + $productCollection->setVisibility($this->catalogVisibility->getVisibleInCatalogIds()); $productCollection->setPage($pageNum, $limit); $this->filter->addFilter($productCollection, 'fulltext', ['fulltext' => $searchKey]); $productCollection->setPage($pageNum, $limit); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index 0704d59a1431c..8a33543e93439 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -38,8 +38,7 @@ public function testExecuteNonExistingSearchKey() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody, true); - $this->assertEmpty($jsonResponse['options']); + $this->assertContains('{"options":[],"total":0}', $responseBody); } /** @@ -58,24 +57,6 @@ public function testExecuteNotVisibleIndividuallyProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody, true); - $this->assertEquals(1, $jsonResponse['total']); - $this->assertCount(1, $jsonResponse['options']); - } - - /** - * @magentoDataFixture Magento/Catalog/_files/multiple_mixed_products.php - */ - public function testExecuteEnabledAndDisabledProducts() : void - { - $this->getRequest() - ->setPostValue('searchKey', 'simple') - ->setPostValue('page', 1) - ->setPostValue('limit', 50); - $this->dispatch('backend/catalog/product/search'); - $responseBody = $this->getResponse()->getBody(); - $jsonResponse = json_decode($responseBody, true); - $this->assertEquals(7, $jsonResponse['total']); - $this->assertCount(7, $jsonResponse['options']); + $this->assertContains('{"options":[],"total":0}', $responseBody); } } From 3a04bb7564bdb6da33aed7ebf886149d87d612e0 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 21 Aug 2019 15:20:23 +0300 Subject: [PATCH 0417/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer module --- .../Customer/Controller/Account/CreatePost.php | 12 +++++------- app/code/Magento/Customer/etc/frontend/di.xml | 14 +++++++++++++- .../customerAlreadyExistsErrorMessage.phtml | 10 ++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 510d82879637f..3a6c1945eb9de 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -394,14 +394,12 @@ public function execute() return $resultRedirect; } catch (StateException $e) { - $url = $this->urlModel->getUrl('customer/account/forgotpassword'); - // @codingStandardsIgnoreStart - $message = __( - 'There is already an account with this email address. If you are sure that it is your email address, <a href="%1">click here</a> to get your password and access your account.', - $url + $this->messageManager->addComplexErrorMessage( + 'customerAlreadyExistsErrorMessage', + [ + 'url' => $this->urlModel->getUrl('customer/account/forgotpassword'), + ] ); - // @codingStandardsIgnoreEnd - $this->messageManager->addErrorMessage($message); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); foreach ($e->getErrors() as $error) { diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index c31742519e581..db6f2a6fc5318 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -77,4 +77,16 @@ </argument> </arguments> </type> -</config> \ No newline at end of file + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <arguments> + <argument name="configurationsMap" xsi:type="array"> + <item name="customerAlreadyExistsErrorMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/customerAlreadyExistsErrorMessage.phtml</item> + </item> + </item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml new file mode 100644 index 0000000000000..27ffc1433a368 --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> + +<?= $block->escapeHtml(__('There is already an account with this email address. If you are sure that it is your email address, <a href="%1">click here</a> to get your password and access your account.', $block->getData('url')), ['a']); From 246f2c9fc940408dd97904dd3c5385f8aad9939d Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 21 Aug 2019 16:59:20 +0400 Subject: [PATCH 0418/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Updated automated test script --- .../Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 3663035ca47af..1e71d8e152651 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -10,7 +10,7 @@ <test name="AdminCategoryWithRestrictedUrlKeyNotCreatedTest"> <annotations> <features value="CatalogUrlRewrite"/> - <stoty value="Subcategory 'liquid-hand-soap' is not opened in category 'soap'"/> + <stories value="Subcategory 'liquid-hand-soap' is not opened in category 'soap'"/> <title value="Category with restricted Url Key cannot be created"/> <description value="Category with restricted Url Key cannot be created"/> <severity value="MAJOR"/> From 7432d2d59b4d0da1866df8cd75a30fe1870f3dac Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Wed, 21 Aug 2019 16:05:59 +0300 Subject: [PATCH 0419/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) --- .../SearchAdapter/Query/Builder/Match.php | 16 +--------------- .../Framework/DB/Helper/Mysql/Fulltext.php | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index f8f70170de155..a1d2d63096e46 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -82,8 +82,7 @@ public function __construct( */ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType) { - $preparedValue = $this->prepareValue($requestQuery->getValue()); - $queryValue = $this->prepareQuery($preparedValue, $conditionType); + $queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType); $queries = $this->buildQueries($requestQuery->getMatches(), $queryValue); $requestQueryBoost = $requestQuery->getBoost() ?: 1; foreach ($queries as $query) { @@ -116,19 +115,6 @@ protected function prepareQuery($queryValue, $conditionType) ]; } - /** - * Removes special query characters which are cause of mysql error: '(', ')', '?' - * - * @param string $queryValue - * @return string - */ - private function prepareValue($queryValue) - { - $pattern = '/(\(|\)|\?)/'; - $replace = ''; - return preg_replace($pattern, $replace, $queryValue); - } - /** * Creates valid ElasticSearch search conditions from Match queries. * diff --git a/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php b/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php index 5c50faf71a854..1493e89e08645 100644 --- a/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php +++ b/lib/internal/Magento/Framework/DB/Helper/Mysql/Fulltext.php @@ -19,7 +19,7 @@ class Fulltext * * @var string */ - const SPECIAL_CHARACTERS = '-+<>*()~'; + const SPECIAL_CHARACTERS = '-+<>*()~?'; /** * FULLTEXT search in MySQL search mode "natural language" From 444fb5c24fd6ef5e3ac647b08d1e6f2fecf5cfda Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 21 Aug 2019 18:03:56 +0300 Subject: [PATCH 0420/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer --- .../Customer/Controller/Account/CreatePost.php | 14 ++++++-------- .../Magento/Customer/Controller/Address/Delete.php | 2 +- app/code/Magento/Customer/etc/frontend/di.xml | 12 ++++++++++++ .../messages/confirmAccountSuccessMessage.phtml | 10 ++++++++++ .../messages/unableDeleteAddressMessage.phtml | 10 ++++++++++ 5 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml create mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 3a6c1945eb9de..6c65d8c8d282c 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -364,15 +364,13 @@ public function execute() ); $confirmationStatus = $this->accountManagement->getConfirmationStatus($customer->getId()); if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { - $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); - // @codingStandardsIgnoreStart - $this->messageManager->addSuccessMessage( - __( - 'You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', - $email - ) + $this->messageManager->addComplexSuccessMessage( + 'confirmAccountSuccessMessage', + [ + 'url' => $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()), + ] ); - // @codingStandardsIgnoreEnd + $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); $resultRedirect->setUrl($this->_redirect->success($url)); } else { diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index c90489bfc978e..188ed31bed0ab 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -29,7 +29,7 @@ public function execute() $this->_addressRepository->deleteById($addressId); $this->messageManager->addSuccessMessage(__('You deleted the address.')); } else { - $this->messageManager->addErrorMessage(__('We can\'t delete the address right now.')); + $this->messageManager->addComplexErrorMessage('unableDeleteAddressMessage'); } } catch (\Exception $other) { $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index db6f2a6fc5318..e43bc4af1fcae 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -86,6 +86,18 @@ <item name="template" xsi:type="string">Magento_Customer::messages/customerAlreadyExistsErrorMessage.phtml</item> </item> </item> + <item name="confirmAccountSuccessMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/confirmAccountSuccessMessage.phtml</item> + </item> + </item> + <item name="unableDeleteAddressMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/unableDeleteAddressMessage.phtml</item> + </item> + </item> </argument> </arguments> </type> diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml new file mode 100644 index 0000000000000..3356c1248b37e --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> + +<?= $block->escapeHtml(__('You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', $block->getData('url')), ['a']); diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml new file mode 100644 index 0000000000000..c49a1d4e46420 --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> + +<?= $block->escapeHtml(__('We can\'t delete the address right now.')); From 1e67a692ec0cb57b5fbed607cfb468eb1d987b7a Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 21 Aug 2019 18:34:13 +0300 Subject: [PATCH 0421/2437] MC-19334: Reindex error when website have store without store view --- app/code/Magento/Store/Model/ScopeTreeProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Store/Model/ScopeTreeProvider.php b/app/code/Magento/Store/Model/ScopeTreeProvider.php index a22d5abb8c486..d15030fe88ac6 100644 --- a/app/code/Magento/Store/Model/ScopeTreeProvider.php +++ b/app/code/Magento/Store/Model/ScopeTreeProvider.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Store\Model; use Magento\Framework\App\Config\ScopeConfigInterface; From c7436240a4802b45b9fadab1039c8d63a8fd7005 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 21 Aug 2019 19:09:54 +0300 Subject: [PATCH 0422/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer --- app/code/Magento/Customer/Controller/Address/Delete.php | 2 +- app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index 188ed31bed0ab..9df06c69d4a88 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -32,7 +32,7 @@ public function execute() $this->messageManager->addComplexErrorMessage('unableDeleteAddressMessage'); } } catch (\Exception $other) { - $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); + $this->messageManager->addException($other, __('We can\'t delete the address right now.')); } } return $this->resultRedirectFactory->create()->setPath('*/*/index'); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 64c94fa230fb1..bb3589a75f721 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -96,7 +96,7 @@ public function execute() $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( From e81a669dd943159d11b16f239349c2e0ddcac80a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 22 Aug 2019 04:53:16 +0000 Subject: [PATCH 0423/2437] MC-19051: [backport for 2.3.3] Nightly build jobs were failing lately after un-skipping tests in MC-5777 --- .../Model/Indexer/ReindexRuleProductTest.php | 47 +++++-------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php index ff566fa3cc774..a86ab736fb289 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductTest.php @@ -89,17 +89,6 @@ public function testExecute() 6 => [$websiteId => 1], ]; - $ruleMock = $this->createMock(Rule::class); - $ruleMock->expects($this->once()) - ->method('getIsActive') - ->willReturn(true); - $ruleMock->expects($this->exactly(2)) - ->method('getWebsiteIds') - ->willReturn([$websiteId]); - $ruleMock->expects($this->once()) - ->method('getMatchingProductIds') - ->willReturn($productIds); - $this->tableSwapperMock->expects($this->once()) ->method('getWorkingTableName') ->with('catalogrule_product') @@ -118,30 +107,18 @@ public function testExecute() ->with('catalogrule_product_replica') ->willReturn('catalogrule_product_replica'); - $ruleMock->expects($this->once()) - ->method('getId') - ->willReturn(100); - $ruleMock->expects($this->once()) - ->method('getCustomerGroupIds') - ->willReturn([10]); - $ruleMock->expects($this->atLeastOnce()) - ->method('getFromDate') - ->willReturn('2017-06-21'); - $ruleMock->expects($this->atLeastOnce()) - ->method('getToDate') - ->willReturn('2017-06-30'); - $ruleMock->expects($this->once()) - ->method('getSortOrder') - ->willReturn(1); - $ruleMock->expects($this->once()) - ->method('getSimpleAction') - ->willReturn('simple_action'); - $ruleMock->expects($this->once()) - ->method('getDiscountAmount') - ->willReturn(43); - $ruleMock->expects($this->once()) - ->method('getStopRulesProcessing') - ->willReturn(true); + $ruleMock = $this->createMock(Rule::class); + $ruleMock->expects($this->once())->method('getIsActive')->willReturn(true); + $ruleMock->expects($this->exactly(2))->method('getWebsiteIds')->willReturn([$websiteId]); + $ruleMock->expects($this->once())->method('getMatchingProductIds')->willReturn($productIds); + $ruleMock->expects($this->once())->method('getId')->willReturn(100); + $ruleMock->expects($this->once())->method('getCustomerGroupIds')->willReturn([10]); + $ruleMock->expects($this->atLeastOnce())->method('getFromDate')->willReturn('2017-06-21'); + $ruleMock->expects($this->atLeastOnce())->method('getToDate')->willReturn('2017-06-30'); + $ruleMock->expects($this->once())->method('getSortOrder')->willReturn(1); + $ruleMock->expects($this->once())->method('getSimpleAction')->willReturn('simple_action'); + $ruleMock->expects($this->once())->method('getDiscountAmount')->willReturn(43); + $ruleMock->expects($this->once())->method('getStopRulesProcessing')->willReturn(true); $this->localeDateMock->expects($this->once()) ->method('getConfigTimezone') From 0bf86e6a094e56ff945fc39a48459cfb574c6b0c Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 21 Aug 2019 23:27:51 +0300 Subject: [PATCH 0424/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer --- .../Customer/Controller/Account/Confirm.php | 4 +- .../Controller/Account/Confirmation.php | 4 +- .../Customer/Controller/Account/LoginPost.php | 1 + .../Controller/Adminhtml/Group/Delete.php | 4 +- .../Adminhtml/Index/AbstractMassAction.php | 1 + .../Controller/Adminhtml/Index/Edit.php | 16 +- .../Controller/Adminhtml/Index/InlineEdit.php | 20 ++- .../Unit/Controller/Account/ConfirmTest.php | 22 +-- .../Controller/Account/CreatePostTest.php | 34 +++-- .../Unit/Controller/Account/LoginPostTest.php | 54 ++++--- .../Unit/Controller/Address/DeleteTest.php | 2 +- .../Controller/Adminhtml/Group/SaveTest.php | 2 +- .../Adminhtml/Index/InlineEditTest.php | 20 ++- .../Controller/Adminhtml/Index/SaveTest.php | 80 ++++++---- .../Observer/AfterAddressSaveObserverTest.php | 138 ++++++++++-------- .../confirmAccountSuccessMessage.phtml | 1 - .../messages/unableDeleteAddressMessage.phtml | 1 - 17 files changed, 239 insertions(+), 165 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 4f0fb3ff08550..3b19a8b33512a 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -151,7 +151,9 @@ public function execute() $customerId = $this->getRequest()->getParam('id', false); $key = $this->getRequest()->getParam('key', false); if (empty($customerId) || empty($key)) { - throw new \Exception(__('Bad request.')); + $this->messageManager->addErrorMessage(__('Bad request.')); + $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); + return $resultRedirect->setUrl($this->_redirect->error($url)); } // log in and send greeting email diff --git a/app/code/Magento/Customer/Controller/Account/Confirmation.php b/app/code/Magento/Customer/Controller/Account/Confirmation.php index bd9066dcd464a..aebc0547f3536 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirmation.php +++ b/app/code/Magento/Customer/Controller/Account/Confirmation.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -15,6 +14,9 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Framework\Exception\State\InvalidTransitionException; +/** + * Class Confirmation + */ class Confirmation extends \Magento\Customer\Controller\AbstractAccount { /** diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index a7632401933e6..046ae5c70276f 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -26,6 +26,7 @@ use Magento\Framework\Phrase; /** + * Class LoginPost * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LoginPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index 819a49178a24d..661ef1cace69b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -9,6 +8,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; +/** + * Class Delete + */ class Delete extends \Magento\Customer\Controller\Adminhtml\Group implements HttpPostActionInterface { /** diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index 08c6e5148ade5..e2bde42351d45 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -73,6 +73,7 @@ public function execute() /** * Return component referer url + * * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php index d4697ecd6169c..3d7f75884057f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php @@ -9,6 +9,9 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Exception\NoSuchEntityException; +/** + * Class Edit + */ class Edit extends \Magento\Customer\Controller\Adminhtml\Index implements HttpGetActionInterface { /** @@ -33,14 +36,11 @@ public function execute() $customer = $this->_customerRepository->getById($customerId); $customerData['account'] = $this->customerMapper->toFlatArray($customer); $customerData['account'][CustomerInterface::ID] = $customerId; - try { - $addresses = $customer->getAddresses(); - foreach ($addresses as $address) { - $customerData['address'][$address->getId()] = $this->addressMapper->toFlatArray($address); - $customerData['address'][$address->getId()]['id'] = $address->getId(); - } - } catch (NoSuchEntityException $e) { - //do nothing + + $addresses = $customer->getAddresses(); + foreach ($addresses as $address) { + $customerData['address'][$address->getId()] = $this->addressMapper->toFlatArray($address); + $customerData['address'][$address->getId()]['id'] = $address->getId(); } } catch (NoSuchEntityException $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong while editing the customer.')); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index 41d2c43bdaf75..ba8c84a0ac273 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -128,10 +128,12 @@ public function execute() $postItems = $this->getRequest()->getParam('items', []); if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { - return $resultJson->setData([ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ]); + return $resultJson->setData( + [ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ] + ); } foreach (array_keys($postItems) as $customerId) { @@ -147,10 +149,12 @@ public function execute() $this->getEmailNotification()->credentialsChanged($this->getCustomer(), $currentCustomer->getEmail()); } - return $resultJson->setData([ - 'messages' => $this->getErrorMessages(), - 'error' => $this->isErrorExists() - ]); + return $resultJson->setData( + [ + 'messages' => $this->getErrorMessages(), + 'error' => $this->isErrorExists() + ] + ); } /** diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 0684594d510d7..335d1068206b3 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -255,10 +255,12 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['id', false, $customerId], - ['key', false, $key], - ]); + ->willReturnMap( + [ + ['id', false, $customerId], + ['key', false, $key], + ] + ); $this->customerRepositoryMock->expects($this->any()) ->method('getById') @@ -372,11 +374,13 @@ public function testSuccessRedirect( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['id', false, $customerId], - ['key', false, $key], - ['back_url', false, $backUrl], - ]); + ->willReturnMap( + [ + ['id', false, $customerId], + ['key', false, $key], + ['back_url', false, $backUrl], + ] + ); $this->customerRepositoryMock->expects($this->any()) ->method('getById') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php index ac52c395d6787..faf55347dba78 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php @@ -346,11 +346,13 @@ public function testSuccessMessage( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ]); + ->willReturnMap( + [ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ] + ); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -477,11 +479,13 @@ public function testSuccessRedirect( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ]); + ->willReturnMap( + [ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ] + ); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -508,10 +512,12 @@ public function testSuccessRedirect( $this->urlMock->expects($this->any()) ->method('getUrl') - ->willReturnMap([ - ['*/*/index', ['_secure' => true], $successUrl], - ['*/*/create', ['_secure' => true], $successUrl], - ]); + ->willReturnMap( + [ + ['*/*/index', ['_secure' => true], $successUrl], + ['*/*/create', ['_secure' => true], $successUrl], + ] + ); $this->redirectMock->expects($this->once()) ->method('success') ->with($this->equalTo($successUrl)) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 51b84d807dc13..fd70a55ad7def 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -93,12 +93,14 @@ protected function setUp() $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) ->disableOriginalConstructor() - ->setMethods([ - 'isLoggedIn', - 'setCustomerDataAsLoggedIn', - 'regenerateId', - 'setUsername', - ]) + ->setMethods( + [ + 'isLoggedIn', + 'setCustomerDataAsLoggedIn', + 'regenerateId', + 'setUsername', + ] + ) ->getMock(); $this->accountManagement = $this->getMockBuilder(\Magento\Customer\Api\AccountManagementInterface::class) @@ -253,10 +255,12 @@ public function testExecuteSuccessCustomRedirect() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -335,10 +339,12 @@ public function testExecuteSuccess() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -426,10 +432,12 @@ public function testExecuteWithException( $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn([ - 'username' => $username, - 'password' => $password, - ]); + ->willReturn( + [ + 'username' => $username, + 'password' => $password, + ] + ); $exception = new $exceptionData['exception'](__($exceptionData['message'])); @@ -488,10 +496,12 @@ protected function prepareContext() $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() - ->setMethods([ - 'isPost', - 'getPost', - ]) + ->setMethods( + [ + 'isPost', + 'getPost', + ] + ) ->getMock(); $this->resultRedirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 7424b0f649fdc..3634aaf469f9b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -183,7 +183,7 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addComplexErrorMessage') ->with(__('We can\'t delete the address right now.')) ->willThrowException($exception); $this->messageManager->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index c9f885315b0ef..b0b8075325f8b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -175,7 +175,7 @@ public function testExecuteWithTaxClassAndException() ->with('customer/group') ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('Exception'); $this->dataObjectProcessorMock->expects($this->once()) ->method('buildOutputDataArray') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index c198eb3a212fa..769c77dfcb4ea 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -291,10 +291,12 @@ protected function prepareMocksForErrorMessagesProcessing() ->willReturn('Error text'); $this->resultJson->expects($this->once()) ->method('setData') - ->with([ - 'messages' => ['Error text'], - 'error' => true, - ]) + ->with( + [ + 'messages' => ['Error text'], + 'error' => true, + ] + ) ->willReturnSelf(); } @@ -340,10 +342,12 @@ public function testExecuteWithoutItems() $this->resultJson ->expects($this->once()) ->method('setData') - ->with([ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ]) + ->with( + [ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ] + ) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 5f34583c855c1..9724ac13dde8c 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -338,10 +338,12 @@ public function testExecuteWithExistentCustomer() $this->requestMock->expects($this->atLeastOnce()) ->method('getPostValue') - ->willReturnMap([ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ]); + ->willReturnMap( + [ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ] + ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -542,10 +544,12 @@ public function testExecuteWithNewCustomer() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap([ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ]); + ->willReturnMap( + [ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ] + ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -723,10 +727,12 @@ public function testExecuteWithNewCustomerAndValidationException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap([ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ]); + ->willReturnMap( + [ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ] + ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -812,10 +818,12 @@ public function testExecuteWithNewCustomerAndValidationException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with([ - 'customer' => $extractedData, - 'subscription' => $subscription, - ]); + ->with( + [ + 'customer' => $extractedData, + 'subscription' => $subscription, + ] + ); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -870,10 +878,12 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap([ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ]); + ->willReturnMap( + [ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ] + ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -959,10 +969,12 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with([ - 'customer' => $extractedData, - 'subscription' => $subscription, - ]); + ->with( + [ + 'customer' => $extractedData, + 'subscription' => $subscription, + ] + ); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -1017,10 +1029,12 @@ public function testExecuteWithNewCustomerAndException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap([ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ]); + ->willReturnMap( + [ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ] + ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -1107,10 +1121,12 @@ public function testExecuteWithNewCustomerAndException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with([ - 'customer' => $extractedData, - 'subscription' => $subscription, - ]); + ->with( + [ + 'customer' => $extractedData, + 'subscription' => $subscription, + ] + ); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) diff --git a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 4501b611aa11f..79766178a7670 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -210,9 +210,11 @@ public function testAfterAddressSaveRestricted( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomerAddress', - ]) + ->setMethods( + [ + 'getCustomerAddress', + ] + ) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -225,10 +227,12 @@ public function testAfterAddressSaveRestricted( $this->registry->expects($this->any()) ->method('registry') - ->willReturnMap([ - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, $processedFlag], - [BeforeAddressSaveObserver::VIV_CURRENTLY_SAVED_ADDRESS, $registeredAddressId], - ]); + ->willReturnMap( + [ + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, $processedFlag], + [BeforeAddressSaveObserver::VIV_CURRENTLY_SAVED_ADDRESS, $registeredAddressId], + ] + ); $this->helperAddress->expects($this->any()) ->method('getTaxCalculationAddressType') @@ -266,11 +270,13 @@ public function testAfterAddressSaveException() $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomer', - 'getForceProcess', - 'getVatId', - ]) + ->setMethods( + [ + 'getCustomer', + 'getForceProcess', + 'getVatId', + ] + ) ->getMock(); $address->expects($this->any()) ->method('getCustomer') @@ -284,9 +290,11 @@ public function testAfterAddressSaveException() $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomerAddress', - ]) + ->setMethods( + [ + 'getCustomerAddress', + ] + ) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -303,10 +311,12 @@ public function testAfterAddressSaveException() ->willReturn(false); $this->registry->expects($this->any()) ->method('register') - ->willReturnMap([ - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, true, false, $this->registry], - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, false, true, $this->registry], - ]); + ->willReturnMap( + [ + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, true, false, $this->registry], + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, false, true, $this->registry], + ] + ); $this->model->execute($observer); } @@ -336,13 +346,15 @@ public function testAfterAddressSaveDefaultGroup( $customer = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getStore', - 'getDisableAutoGroupChange', - 'getGroupId', - 'setGroupId', - 'save', - ]) + ->setMethods( + [ + 'getStore', + 'getDisableAutoGroupChange', + 'getGroupId', + 'setGroupId', + 'save', + ] + ) ->getMock(); $customer->expects($this->exactly(2)) ->method('getStore') @@ -363,12 +375,14 @@ public function testAfterAddressSaveDefaultGroup( $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomer', - 'getForceProcess', - 'getVatId', - 'getCountry', - ]) + ->setMethods( + [ + 'getCustomer', + 'getForceProcess', + 'getVatId', + 'getCountry', + ] + ) ->getMock(); $address->expects($this->once()) ->method('getCustomer') @@ -385,9 +399,11 @@ public function testAfterAddressSaveDefaultGroup( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomerAddress', - ]) + ->setMethods( + [ + 'getCustomerAddress', + ] + ) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -457,10 +473,12 @@ public function testAfterAddressSaveNewGroup( $validationResult = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() - ->setMethods([ - 'getIsValid', - 'getRequestSuccess', - ]) + ->setMethods( + [ + 'getIsValid', + 'getRequestSuccess', + ] + ) ->getMock(); $validationResult->expects($this->any()) ->method('getIsValid') @@ -471,13 +489,15 @@ public function testAfterAddressSaveNewGroup( $customer = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getStore', - 'getDisableAutoGroupChange', - 'getGroupId', - 'setGroupId', - 'save', - ]) + ->setMethods( + [ + 'getStore', + 'getDisableAutoGroupChange', + 'getGroupId', + 'setGroupId', + 'save', + ] + ) ->getMock(); $customer->expects($this->exactly(2)) ->method('getStore') @@ -503,14 +523,16 @@ public function testAfterAddressSaveNewGroup( $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomer', - 'getForceProcess', - 'getVatId', - 'getCountryId', - 'getCountry', - 'setVatValidationResult', - ]) + ->setMethods( + [ + 'getCustomer', + 'getForceProcess', + 'getVatId', + 'getCountryId', + 'getCountry', + 'setVatValidationResult', + ] + ) ->getMock(); $address->expects($this->any()) ->method('getCustomer') @@ -534,9 +556,11 @@ public function testAfterAddressSaveNewGroup( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods([ - 'getCustomerAddress', - ]) + ->setMethods( + [ + 'getCustomerAddress', + ] + ) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml index 3356c1248b37e..a2edb20e967c6 100644 --- a/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/messages/confirmAccountSuccessMessage.phtml @@ -6,5 +6,4 @@ /** @var \Magento\Framework\View\Element\Template $block */ ?> - <?= $block->escapeHtml(__('You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', $block->getData('url')), ['a']); diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml index c49a1d4e46420..a5313078e13cc 100644 --- a/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml @@ -6,5 +6,4 @@ /** @var \Magento\Framework\View\Element\Template $block */ ?> - <?= $block->escapeHtml(__('We can\'t delete the address right now.')); From 60823ed12471599b895a700c0c3724a5b6d87572 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Thu, 22 Aug 2019 10:12:58 +0300 Subject: [PATCH 0425/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento_Customer --- app/code/Magento/Customer/Controller/Account/Confirm.php | 4 +++- app/code/Magento/Customer/Controller/Account/EditPost.php | 2 ++ .../Customer/Test/Unit/Controller/Account/ConfirmTest.php | 2 +- .../Customer/Test/Unit/Controller/Address/DeleteTest.php | 6 +++--- .../messages/customerAlreadyExistsErrorMessage.phtml | 1 - 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 3b19a8b33512a..6809442bbc929 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -24,7 +24,9 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Confirm extends \Magento\Customer\Controller\AbstractAccount +class Confirm + extends \Magento\Customer\Controller\AbstractAccount + implements \Magento\Framework\App\Action\Action\ActionInterface { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index d2300b7b490c5..0203226fc2e3a 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -345,9 +345,11 @@ private function processChangeEmailRequest(\Magento\Customer\Api\Data\CustomerIn $this->getRequest()->getPost('current_password') ); } catch (InvalidEmailOrPasswordException $e) { + // @codingStandardsIgnoreStart throw new InvalidEmailOrPasswordException( __("The password doesn't match this account. Verify the password and try again.") ); + // @codingStandardsIgnoreEnd } } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 335d1068206b3..61efd9d562b45 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -205,7 +205,7 @@ public function testNoCustomerIdInRequest($customerId, $key) $exception = new \Exception('Bad request.'); $this->messageManagerMock->expects($this->once()) - ->method('addExceptionMessage') + ->method('addException') ->with($this->equalTo($exception), $this->equalTo('There was an error confirming the account')); $testUrl = 'http://example.com'; diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 3634aaf469f9b..046c023d35c64 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -184,10 +184,10 @@ public function testExecuteWithException() $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) ->method('addComplexErrorMessage') - ->with(__('We can\'t delete the address right now.')) - ->willThrowException($exception); + ->with('unableDeleteAddressMessage') + ->willReturnSelf(); $this->messageManager->expects($this->once()) - ->method('addExceptionMessage') + ->method('addException') ->with($exception, __('We can\'t delete the address right now.')); $this->resultRedirect->expects($this->once()) ->method('setPath') diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml index 27ffc1433a368..32982551b5b16 100644 --- a/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/messages/customerAlreadyExistsErrorMessage.phtml @@ -6,5 +6,4 @@ /** @var \Magento\Framework\View\Element\Template $block */ ?> - <?= $block->escapeHtml(__('There is already an account with this email address. If you are sure that it is your email address, <a href="%1">click here</a> to get your password and access your account.', $block->getData('url')), ['a']); From 3910cacc104294b5fb92d562034620300461cfc6 Mon Sep 17 00:00:00 2001 From: Andrii Meysar <andrii.meysar@transoftgroup.com> Date: Thu, 22 Aug 2019 13:48:36 +0300 Subject: [PATCH 0426/2437] MC-19566: Failing Integration Magento.Catalog.Model.Indexer.Product.Flat.Action.RelationTest.testExecute --- .../Catalog/Model/Indexer/Product/Flat/AbstractAction.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php index ebad10e197622..a0acacd4dfd2f 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/AbstractAction.php @@ -254,10 +254,12 @@ protected function _updateRelationProducts($storeId, $productIds = null) * * @param int $storeId * @return \Magento\Catalog\Model\Indexer\Product\Flat\AbstractAction + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _cleanRelationProducts($storeId) { - if (!$this->_productIndexerHelper->isAddChildData()) { + if (!$this->_productIndexerHelper->isAddChildData() || !$this->_isFlatTableExists($storeId)) { return $this; } From ef93c70aef566175173241f16fa54e6059300bf1 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Wed, 21 Aug 2019 16:49:52 +0300 Subject: [PATCH 0427/2437] MC-15523: Watermark is possible to set up for swatch image type - Updated automated test script --- .../Test/AdminSetUpWatermarkForSwatchImageTest.xml | 12 ++++++------ .../Test/Mftf/Section/AdminDesignConfigSection.xml | 5 +---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml index 66043a51db183..569952019b29b 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml @@ -44,14 +44,14 @@ <waitForPageLoad stepKey="waitForWatermarksPage"/> <!-- See Base, Thumbnail, Small image types are displayed --> <comment userInput="See Base, Thumbnail, Small image types are displayed" stepKey="commentSeeImageTypes"/> - <seeElement selector="{{AdminDesignConfigSection.imageWatermarkBase}}" stepKey="seeElementBaseWatermark"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkThumbnail}}" stepKey="waitForThumbnailVisible" /> - <seeElement selector="{{AdminDesignConfigSection.imageWatermarkThumbnail}}" stepKey="seeElementThumbnailWatermark"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkSmall}}" stepKey="waitForSmallVisible" /> - <seeElement selector="{{AdminDesignConfigSection.imageWatermarkSmall}}" stepKey="seeElementSmallWatermark"/> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Base')}}" stepKey="seeElementBaseWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkType('Thumbnail')}}" stepKey="waitForThumbnailVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Thumbnail')}}" stepKey="seeElementThumbnailWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkType('Small')}}" stepKey="waitForSmallVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Small')}}" stepKey="seeElementSmallWatermark"/> <!-- See Swatch Image type is absent --> <comment userInput="See Swatch Image type is absent" stepKey="commentSeeTypeAbsent"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <dontSeeElement selector="{{AdminDesignConfigSection.imageWatermarkSwatchImage}}" stepKey="dontSeeImageWatermarkSwatchImage"/> + <dontSeeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Swatch')}}" stepKey="dontSeeImageWatermarkSwatchImage"/> </test> </tests> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index a65dcc5a1aa14..cf420598ca44e 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -31,9 +31,6 @@ <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> - <element name="imageWatermarkBase" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Base')]"/> - <element name="imageWatermarkThumbnail" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Thumbnail')]"/> - <element name="imageWatermarkSmall" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Small')]"/> - <element name="imageWatermarkSwatchImage" type="text" selector="//span[contains(@data-bind, 'label') and contains(text(), 'Swatch Image')]"/> + <element name="imageWatermarkType" type="text" selector="//div[contains(@class, 'fieldset-wrapper-title')]//span[contains(text(), '{{watermarkType}}')]" parameterized="true"/> </section> </sections> From 4d338c7df9e23c178e7f09bd9a201942113e1152 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 22 Aug 2019 10:57:21 -0500 Subject: [PATCH 0428/2437] MC-19145: [CLOUD] Internal error after DHL was configured --- app/code/Magento/Dhl/Model/Carrier.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 0a1632a45cb0c..0890466e8a40f 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -825,8 +825,10 @@ protected function _getAllItems() $fullItems[] = array_fill(0, $qty, $this->_getWeight($itemWeight)); } } - $fullItems = array_merge(...$fullItems); - sort($fullItems); + if ($fullItems) { + $fullItems = array_merge(...$fullItems); + sort($fullItems); + } return $fullItems; } From 7035daca3f6b47038d120813ec7164b76ac4bc94 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Thu, 22 Aug 2019 13:42:48 -0500 Subject: [PATCH 0429/2437] MC-19184: Quick Search is broken --- .../CatalogSearch/view/frontend/templates/result.phtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml index c63e6ff4abe0f..921e1a81d8719 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +/** This changes need to valid applying filters and configuration before search process is started. */ +$productList = $block->getProductListHtml(); ?> <?php if ($block->getResultCount()) : ?> <?= /* @noEscape */ $block->getChildHtml('tagged_product_list_rss_link') ?> @@ -16,7 +19,7 @@ </div> </div> <?php endif; ?> - <?= $block->getProductListHtml() ?> + <?= $productList ?> </div> <?php else : ?> From 4d62ac8a56828fa7702381ba2f0b651a6266e099 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Fri, 28 Jun 2019 16:20:22 +0300 Subject: [PATCH 0430/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Fix CR comments --- .../Model/Order/Shipment/TrackRepository.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index c8fc9e5fc5600..93396976565ea 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -16,8 +16,8 @@ use Magento\Sales\Api\Data\ShipmentTrackSearchResultInterfaceFactory; use Magento\Sales\Api\ShipmentTrackRepositoryInterface; use Magento\Sales\Model\Spi\ShipmentTrackResourceInterface; -use \Magento\Sales\Api\OrderRepositoryInterface; -use \Magento\Framework\App\ObjectManager; +use Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory; +use Magento\Framework\App\ObjectManager; use Psr\Log\LoggerInterface; /** @@ -46,9 +46,9 @@ class TrackRepository implements ShipmentTrackRepositoryInterface private $collectionProcessor; /** - * @var OrderRepositoryInterface + * @var CollectionFactory */ - private $orderRepository; + private $shipmentCollection; /** * @var LoggerInterface @@ -60,7 +60,7 @@ class TrackRepository implements ShipmentTrackRepositoryInterface * @param ShipmentTrackInterfaceFactory $trackFactory * @param ShipmentTrackSearchResultInterfaceFactory $searchResultFactory * @param CollectionProcessorInterface $collectionProcessor - * @param OrderRepositoryInterface|null $orderRepository + * @param CollectionFactory|null $shipmentCollection * @param LoggerInterface|null $logger */ public function __construct( @@ -68,15 +68,15 @@ public function __construct( ShipmentTrackInterfaceFactory $trackFactory, ShipmentTrackSearchResultInterfaceFactory $searchResultFactory, CollectionProcessorInterface $collectionProcessor, - OrderRepositoryInterface $orderRepository = null, + CollectionFactory $shipmentCollection = null, LoggerInterface $logger = null ) { $this->trackResource = $trackResource; $this->trackFactory = $trackFactory; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor; - $this->orderRepository = $orderRepository ?: - ObjectManager::getInstance()->get(OrderRepositoryInterface::class); + $this->shipmentCollection = $shipmentCollection ?: + ObjectManager::getInstance()->get(CollectionFactory::class); $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); } @@ -120,9 +120,9 @@ public function delete(ShipmentTrackInterface $entity) */ public function save(ShipmentTrackInterface $entity) { - $shipmentCollection = $this->orderRepository->get($entity['order_id'])->getShipmentsCollection(); + $shipments = $this->shipmentCollection->create()->addFieldToFilter('order_id', $entity['order_id']); $shipmentId = []; - foreach ($shipmentCollection as $shipment) { + foreach ($shipments->getItems() as $shipment) { $shipmentId[] = $shipment->getId(); } From 302070f611e884abbda6460cb6231101a2eefcb4 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Fri, 23 Aug 2019 07:56:11 -0500 Subject: [PATCH 0431/2437] MC-19184: Quick Search is broken - fixed static tests --- .../Magento/CatalogSearch/view/frontend/templates/result.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml index 921e1a81d8719..32b26eec9dbe6 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml @@ -19,7 +19,7 @@ $productList = $block->getProductListHtml(); </div> </div> <?php endif; ?> - <?= $productList ?> + <?= /* @noEscape */ $productList ?> </div> <?php else : ?> From 41cb1fcf99b3f758c03bb383a79547c38c8aa9b1 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Fri, 23 Aug 2019 16:35:48 +0300 Subject: [PATCH 0432/2437] Code refactoring --- .../User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml index 4a021597ce405..bcf6d2cc0b7ea 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -19,9 +19,13 @@ </annotations> <before> + <magentoCLI command="config:set admin/captcha/enable 0" stepKey="disableAdminCaptcha"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> </before> <after> + <magentoCLI command="config:set admin/captcha/enable 1" stepKey="enableAdminCaptcha"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> <actionGroup ref="logout" stepKey="logOut"/> </after> @@ -36,7 +40,7 @@ <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"> - <argument name="qty" value="2"/> + <argument name="qty" value="2"/> </actionGroup> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> From 35b8a04d5a0f7c919e02c2727bd0a5778fe39e90 Mon Sep 17 00:00:00 2001 From: Vitalii Zabaznov <vzabaznov@magento.com> Date: Fri, 23 Aug 2019 09:50:54 -0500 Subject: [PATCH 0433/2437] MC-19612: Revert MC-15378 --- app/code/Magento/Customer/Model/Visitor.php | 5 +++++ .../Magento/Customer/Controller/AccountTest.php | 15 --------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Customer/Model/Visitor.php b/app/code/Magento/Customer/Model/Visitor.php index 4f129f05aa82c..17394c4d94129 100644 --- a/app/code/Magento/Customer/Model/Visitor.php +++ b/app/code/Magento/Customer/Model/Visitor.php @@ -169,6 +169,11 @@ public function initByRequest($observer) $this->setLastVisitAt((new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); + // prevent saving Visitor for safe methods, e.g. GET request + if ($this->requestSafety->isSafeMethod()) { + return $this; + } + if (!$this->getId()) { $this->setSessionId($this->session->getSessionId()); $this->save(); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 566dfbadedd29..32f12dada57a6 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -798,21 +798,6 @@ public function loginPostRedirectDataProvider() ]; } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - * @magentoAppArea frontend - */ - public function testCheckVisitorModel() - { - /** @var \Magento\Customer\Model\Visitor $visitor */ - $visitor = $this->_objectManager->get(\Magento\Customer\Model\Visitor::class); - $this->login(1); - $this->assertNull($visitor->getId()); - $this->dispatch('customer/account/index'); - $this->assertNotNull($visitor->getId()); - } - /** * @param string $email * @return void From 0023dcec912a1a442d67ab0c8d9cab36490c8be2 Mon Sep 17 00:00:00 2001 From: Vitaliy Honcharenko <vgoncharenko@magento.com> Date: Fri, 23 Aug 2019 16:31:54 -0500 Subject: [PATCH 0434/2437] MC-19184: Quick Search is broken - fixed performance tests --- .../ResourceModel/Fulltext/Collection/SearchResultApplier.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index 3ae2d384782c3..b15d99dcc2df3 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -50,7 +50,6 @@ public function apply() foreach ($this->searchResult->getItems() as $item) { $ids[] = (int)$item->getId(); } - $this->collection->setPageSize(null); $this->collection->getSelect()->where('e.entity_id IN (?)', $ids); $orderList = join(',', $ids); $this->collection->getSelect()->reset(\Magento\Framework\DB\Select::ORDER); From f0bb8d5beb29e9b9db7c9f660650919a4efa59f5 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 23 Aug 2019 23:08:13 -0400 Subject: [PATCH 0435/2437] Correct cart_item_id source for address items [`\Magento\Quote\Model\Quote\Address::getAllItems`](https://github.com/magento/magento2/blob/cf4dc427fed594f74b7168735ee1eb93febfc143/app/code/Magento/Quote/Model/Quote/Address.php#L592-L636) returns `\Magento\Quote\Model\Quote\Address\Item[]` when the quote has multiple shipping addresses and `Magento\Quote\Model\Quote\Item[]` with a single shipping address. These objects have different methods for accessing the quote item id and both variants need to be accommodated in the extractor. Fixes magento/graphql-ce#822 --- .../Model/Cart/ExtractQuoteAddressData.php | 8 +++- .../Guest/GetAvailableShippingMethodsTest.php | 48 ++++++++++--------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php index c4d795293220f..ce14bcf2d71ea 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php @@ -61,8 +61,14 @@ public function execute(QuoteAddress $address): array $addressItemsData = []; foreach ($address->getAllItems() as $addressItem) { + if ($addressItem instanceof \Magento\Quote\Model\Quote\Item) { + $itemId = $addressItem->getItemId(); + } else { + $itemId = $addressItem->getQuoteItemId(); + } + $addressItemsData[] = [ - 'cart_item_id' => $addressItem->getQuoteItemId(), + 'cart_item_id' => $itemId, 'quantity' => $addressItem->getQty() ]; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php index 5d90d26d4983c..ff20274d6930f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php @@ -139,29 +139,33 @@ private function getQuery(string $maskedQuoteId): string query { cart (cart_id: "{$maskedQuoteId}") { shipping_addresses { - available_shipping_methods { - amount { - value - currency - } - base_amount { - value - currency - } - carrier_code - carrier_title - error_message - method_code - method_title - price_excl_tax { - value - currency - } - price_incl_tax { - value - currency - } + cart_items { + cart_item_id + quantity + } + available_shipping_methods { + amount { + value + currency } + base_amount { + value + currency + } + carrier_code + carrier_title + error_message + method_code + method_title + price_excl_tax { + value + currency + } + price_incl_tax { + value + currency + } + } } } } From b2a21ede77296e921f2c43833e3de85004d6e9d3 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 23 Aug 2019 23:54:42 -0400 Subject: [PATCH 0436/2437] Fix static test and remove unused import --- .../Model/Cart/ExtractQuoteAddressData.php | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php index ce14bcf2d71ea..27dd1959cb5d7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php @@ -7,7 +7,6 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Customer\Model\Address\AbstractAddress; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Model\Quote\Address as QuoteAddress; @@ -41,19 +40,22 @@ public function execute(QuoteAddress $address): array $addressData = $this->dataObjectConverter->toFlatArray($address, [], AddressInterface::class); $addressData['model'] = $address; - $addressData = array_merge($addressData, [ - 'country' => [ - 'code' => $address->getCountryId(), - 'label' => $address->getCountry() - ], - 'region' => [ - 'code' => $address->getRegionCode(), - 'label' => $address->getRegion() - ], - 'street' => $address->getStreet(), - 'items_weight' => $address->getWeight(), - 'customer_notes' => $address->getCustomerNotes() - ]); + $addressData = array_merge( + $addressData, + [ + 'country' => [ + 'code' => $address->getCountryId(), + 'label' => $address->getCountry() + ], + 'region' => [ + 'code' => $address->getRegionCode(), + 'label' => $address->getRegion() + ], + 'street' => $address->getStreet(), + 'items_weight' => $address->getWeight(), + 'customer_notes' => $address->getCustomerNotes() + ] + ); if (!$address->hasItems()) { return $addressData; From 5b360cb19ff4bc8c3ad6c75d98dcb92044eba97d Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Sun, 25 Aug 2019 07:19:30 +0300 Subject: [PATCH 0437/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento/Customer/Controller/Account/CreatePost.php --- .../Customer/Controller/Account/Confirm.php | 14 +- .../Controller/Account/Confirmation.php | 10 +- .../Controller/Account/CreatePost.php | 49 +++--- .../Customer/Controller/Account/EditPost.php | 10 +- .../Customer/Controller/Account/LoginPost.php | 7 +- .../Controller/Account/ResetPasswordPost.php | 12 +- .../Customer/Controller/Address/Delete.php | 4 +- .../Adminhtml/Customer/InvalidateToken.php | 6 +- .../Controller/Adminhtml/Group/Delete.php | 10 +- .../Controller/Adminhtml/Group/Save.php | 2 +- .../Customer/Controller/Adminhtml/Index.php | 4 +- .../Adminhtml/Index/AbstractMassAction.php | 3 +- .../Controller/Adminhtml/Index/Delete.php | 6 +- .../Controller/Adminhtml/Index/Edit.php | 18 +-- .../Controller/Adminhtml/Index/InlineEdit.php | 26 ++-- .../Adminhtml/Index/MassAssignGroup.php | 2 +- .../Controller/Adminhtml/Index/MassDelete.php | 2 +- .../Adminhtml/Index/MassSubscribe.php | 2 +- .../Adminhtml/Index/MassUnsubscribe.php | 2 +- .../Adminhtml/Index/ResetPassword.php | 6 +- .../Controller/Adminhtml/Index/Save.php | 7 +- .../Controller/Adminhtml/Locks/Unlock.php | 4 +- .../Observer/AfterAddressSaveObserver.php | 6 +- .../StorefrontClearAllCompareProductsTest.xml | 1 + .../Unit/Controller/Account/ConfirmTest.php | 26 ++-- .../Controller/Account/CreatePostTest.php | 38 ++--- .../Unit/Controller/Account/LoginPostTest.php | 64 ++++---- .../Unit/Controller/Address/DeleteTest.php | 8 +- .../Controller/Adminhtml/Group/SaveTest.php | 2 +- .../Adminhtml/Index/InlineEditTest.php | 24 ++- .../Adminhtml/Index/MassAssignGroupTest.php | 4 +- .../Adminhtml/Index/MassDeleteTest.php | 4 +- .../Adminhtml/Index/MassSubscribeTest.php | 4 +- .../Adminhtml/Index/MassUnsubscribeTest.php | 4 +- .../Adminhtml/Index/NewsletterTest.php | 2 +- .../Adminhtml/Index/ResetPasswordTest.php | 6 +- .../Controller/Adminhtml/Index/SaveTest.php | 92 +++++------ .../Controller/Adminhtml/Locks/UnlockTest.php | 4 +- .../Observer/AfterAddressSaveObserverTest.php | 144 ++++++++---------- app/code/Magento/Customer/etc/frontend/di.xml | 26 +--- .../messages/unableDeleteAddressMessage.phtml | 9 -- 41 files changed, 281 insertions(+), 393 deletions(-) delete mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 6809442bbc929..2b3cb9aa61ab5 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -24,9 +24,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Confirm - extends \Magento\Customer\Controller\AbstractAccount - implements \Magento\Framework\App\Action\Action\ActionInterface +class Confirm extends \Magento\Customer\Controller\AbstractAccount { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface @@ -153,9 +151,7 @@ public function execute() $customerId = $this->getRequest()->getParam('id', false); $key = $this->getRequest()->getParam('key', false); if (empty($customerId) || empty($key)) { - $this->messageManager->addErrorMessage(__('Bad request.')); - $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); - return $resultRedirect->setUrl($this->_redirect->error($url)); + throw new \Exception(__('Bad request.')); } // log in and send greeting email @@ -167,13 +163,13 @@ public function execute() $metadata->setPath('/'); $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata); } - $this->messageManager->addSuccessMessage($this->getSuccessMessage()); + $this->messageManager->addSuccess($this->getSuccessMessage()); $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { - $this->messageManager->addExceptionMessage($e, __('This confirmation key is invalid or has expired.')); + $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.')); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('There was an error confirming the account')); + $this->messageManager->addException($e, __('There was an error confirming the account')); } $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); diff --git a/app/code/Magento/Customer/Controller/Account/Confirmation.php b/app/code/Magento/Customer/Controller/Account/Confirmation.php index aebc0547f3536..a3e2db0207630 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirmation.php +++ b/app/code/Magento/Customer/Controller/Account/Confirmation.php @@ -1,5 +1,6 @@ <?php /** + * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -14,9 +15,6 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Framework\Exception\State\InvalidTransitionException; -/** - * Class Confirmation - */ class Confirmation extends \Magento\Customer\Controller\AbstractAccount { /** @@ -93,11 +91,11 @@ public function execute() $email, $this->storeManager->getStore()->getWebsiteId() ); - $this->messageManager->addSuccessMessage(__('Please check your email for confirmation key.')); + $this->messageManager->addSuccess(__('Please check your email for confirmation key.')); } catch (InvalidTransitionException $e) { - $this->messageManager->addSuccessMessage(__('This email does not require confirmation.')); + $this->messageManager->addSuccess(__('This email does not require confirmation.')); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Wrong email.')); + $this->messageManager->addException($e, __('Wrong email.')); $resultRedirect->setPath('*/*/*', ['email' => $email, '_secure' => true]); return $resultRedirect; } diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 6c65d8c8d282c..a2be0f68b56cb 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Controller\Account; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; @@ -349,33 +351,34 @@ public function execute() $confirmation = $this->getRequest()->getParam('password_confirmation'); $redirectUrl = $this->session->getBeforeAuthUrl(); $this->checkPasswordConfirmation($password, $confirmation); + + $extensionAttributes = $customer->getExtensionAttributes(); + $extensionAttributes->setIsSubscribed($this->getRequest()->getParam('is_subscribed', false)); + $customer->setExtensionAttributes($extensionAttributes); + $customer = $this->accountManagement ->createAccount($customer, $password, $redirectUrl); - if ($this->getRequest()->getParam('is_subscribed', false)) { - $extensionAttributes = $customer->getExtensionAttributes(); - $extensionAttributes->setIsSubscribed(true); - $customer->setExtensionAttributes($extensionAttributes); - $this->customerRepository->save($customer); - } $this->_eventManager->dispatch( 'customer_register_success', ['account_controller' => $this, 'customer' => $customer] ); $confirmationStatus = $this->accountManagement->getConfirmationStatus($customer->getId()); if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { - $this->messageManager->addComplexSuccessMessage( - 'confirmAccountSuccessMessage', - [ - 'url' => $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()), - ] + $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); + // @codingStandardsIgnoreStart + $this->messageManager->addSuccess( + __( + 'You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', + $email + ) ); - + // @codingStandardsIgnoreEnd $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); $resultRedirect->setUrl($this->_redirect->success($url)); } else { $this->session->setCustomerDataAsLoggedIn($customer); - $this->messageManager->addSuccessMessage($this->getSuccessMessage()); + $this->messageManager->addSuccess($this->getSuccessMessage()); $requestedRedirect = $this->accountRedirect->getRedirectCookie(); if (!$this->scopeConfig->getValue('customer/startup/redirect_dashboard') && $requestedRedirect) { $resultRedirect->setUrl($this->_redirect->success($requestedRedirect)); @@ -392,21 +395,23 @@ public function execute() return $resultRedirect; } catch (StateException $e) { - $this->messageManager->addComplexErrorMessage( - 'customerAlreadyExistsErrorMessage', - [ - 'url' => $this->urlModel->getUrl('customer/account/forgotpassword'), - ] + $url = $this->urlModel->getUrl('customer/account/forgotpassword'); + // @codingStandardsIgnoreStart + $message = __( + 'There is already an account with this email address. If you are sure that it is your email address, <a href="%1">click here</a> to get your password and access your account.', + $url ); + // @codingStandardsIgnoreEnd + $this->messageManager->addError($message); } catch (InputException $e) { - $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); foreach ($e->getErrors() as $error) { - $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); + $this->messageManager->addError($this->escaper->escapeHtml($error->getMessage())); } } catch (LocalizedException $e) { - $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('We can\'t save the customer.')); + $this->messageManager->addException($e, __('We can\'t save the customer.')); } $this->session->setCustomerFormData($this->getRequest()->getPostValue()); diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index 0203226fc2e3a..4eb41cedea29a 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -216,7 +216,7 @@ public function execute() $isPasswordChanged ); $this->dispatchSuccessEvent($customerCandidateDataObject); - $this->messageManager->addSuccessMessage(__('You saved the account information.')); + $this->messageManager->addSuccess(__('You saved the account information.')); return $resultRedirect->setPath('customer/account'); } catch (InvalidEmailOrPasswordException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -227,7 +227,7 @@ public function execute() ); $this->session->logout(); $this->session->start(); - $this->messageManager->addErrorMessage($message); + $this->messageManager->addError($message); return $resultRedirect->setPath('customer/account/login'); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -235,9 +235,9 @@ public function execute() $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage($e, __('We can\'t save the customer.')); + $this->messageManager->addException($e, __('We can\'t save the customer.')); } $this->session->setCustomerFormData($this->getRequest()->getPostValue()); @@ -345,11 +345,9 @@ private function processChangeEmailRequest(\Magento\Customer\Api\Data\CustomerIn $this->getRequest()->getPost('current_password') ); } catch (InvalidEmailOrPasswordException $e) { - // @codingStandardsIgnoreStart throw new InvalidEmailOrPasswordException( __("The password doesn't match this account. Verify the password and try again.") ); - // @codingStandardsIgnoreEnd } } } diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 046ae5c70276f..04051fbbf366b 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -26,7 +26,6 @@ use Magento\Framework\Phrase; /** - * Class LoginPost * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LoginPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface @@ -218,17 +217,17 @@ public function execute() $message = $e->getMessage(); } catch (\Exception $e) { // PA DSS violation: throwing or logging an exception here can disclose customer password - $this->messageManager->addErrorMessage( + $this->messageManager->addError( __('An unspecified error occurred. Please contact us for assistance.') ); } finally { if (isset($message)) { - $this->messageManager->addErrorMessage($message); + $this->messageManager->addError($message); $this->session->setUsername($login['username']); } } } else { - $this->messageManager->addErrorMessage(__('A login and a password are required.')); + $this->messageManager->addError(__('A login and a password are required.')); } } diff --git a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php index a127f2acf538f..27a00f86dd95d 100644 --- a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php +++ b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php @@ -73,13 +73,13 @@ public function execute() $passwordConfirmation = (string)$this->getRequest()->getPost('password_confirmation'); if ($password !== $passwordConfirmation) { - $this->messageManager->addErrorMessage(__("New Password and Confirm New Password values didn't match.")); + $this->messageManager->addError(__("New Password and Confirm New Password values didn't match.")); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; } if (iconv_strlen($password) <= 0) { - $this->messageManager->addErrorMessage(__('Please enter a new password.')); + $this->messageManager->addError(__('Please enter a new password.')); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; @@ -92,17 +92,17 @@ public function execute() $password ); $this->session->unsRpToken(); - $this->messageManager->addSuccessMessage(__('You updated your password.')); + $this->messageManager->addSuccess(__('You updated your password.')); $resultRedirect->setPath('*/*/login'); return $resultRedirect; } catch (InputException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addErrorMessage($error->getMessage()); + $this->messageManager->addError($error->getMessage()); } } catch (\Exception $exception) { - $this->messageManager->addErrorMessage(__('Something went wrong while saving the new password.')); + $this->messageManager->addError(__('Something went wrong while saving the new password.')); } $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index 9df06c69d4a88..a30e15db4b3f8 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -27,9 +27,9 @@ public function execute() $address = $this->_addressRepository->getById($addressId); if ($address->getCustomerId() === $this->_getSession()->getCustomerId()) { $this->_addressRepository->deleteById($addressId); - $this->messageManager->addSuccessMessage(__('You deleted the address.')); + $this->messageManager->addSuccess(__('You deleted the address.')); } else { - $this->messageManager->addComplexErrorMessage('unableDeleteAddressMessage'); + $this->messageManager->addError(__('We can\'t delete the address right now.')); } } catch (\Exception $other) { $this->messageManager->addException($other, __('We can\'t delete the address right now.')); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php index 7747d80595cdc..b69410ecbfce7 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php @@ -139,14 +139,14 @@ public function execute() if ($customerId = $this->getRequest()->getParam('customer_id')) { try { $this->tokenService->revokeCustomerAccessToken($customerId); - $this->messageManager->addSuccessMessage(__('You have revoked the customer\'s tokens.')); + $this->messageManager->addSuccess(__('You have revoked the customer\'s tokens.')); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } } else { - $this->messageManager->addErrorMessage(__('We can\'t find a customer to revoke.')); + $this->messageManager->addError(__('We can\'t find a customer to revoke.')); $resultRedirect->setPath('customer/index/index'); } return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index 661ef1cace69b..ab32ea08a44aa 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -1,5 +1,6 @@ <?php /** + * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -8,9 +9,6 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; -/** - * Class Delete - */ class Delete extends \Magento\Customer\Controller\Adminhtml\Group implements HttpPostActionInterface { /** @@ -26,12 +24,12 @@ public function execute() if ($id) { try { $this->groupRepository->deleteById($id); - $this->messageManager->addSuccessMessage(__('You deleted the customer group.')); + $this->messageManager->addSuccess(__('You deleted the customer group.')); } catch (NoSuchEntityException $e) { - $this->messageManager->addErrorMessage(__('The customer group no longer exists.')); + $this->messageManager->addError(__('The customer group no longer exists.')); return $resultRedirect->setPath('customer/*/'); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); return $resultRedirect->setPath('customer/group/edit', ['id' => $id]); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index bb3589a75f721..5ffce4cbcd989 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -93,7 +93,7 @@ public function execute() $this->groupRepository->save($customerGroup); - $this->messageManager->addSuccessMessage(__('You saved the customer group.')); + $this->messageManager->addSuccess(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { $this->messageManager->addError($e->getMessage()); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index.php b/app/code/Magento/Customer/Controller/Adminhtml/Index.php index ffae1e9f8bf1e..a0317a51260da 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index.php @@ -311,7 +311,7 @@ protected function _addSessionErrorMessages($messages) protected function actUponMultipleCustomers(callable $singleAction, $customerIds) { if (!is_array($customerIds)) { - $this->messageManager->addErrorMessage(__('Please select customer(s).')); + $this->messageManager->addError(__('Please select customer(s).')); return 0; } $customersUpdated = 0; @@ -320,7 +320,7 @@ protected function actUponMultipleCustomers(callable $singleAction, $customerIds $singleAction($customerId); $customersUpdated++; } catch (\Exception $exception) { - $this->messageManager->addErrorMessage($exception->getMessage()); + $this->messageManager->addError($exception->getMessage()); } } return $customersUpdated; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index e2bde42351d45..e26b49aaebe7a 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -64,7 +64,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); @@ -73,7 +73,6 @@ public function execute() /** * Return component referer url - * * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php index 4b2f2614948cf..ab39ca098162f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php @@ -31,7 +31,7 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addErrorMessage(__('Customer could not be deleted.')); + $this->messageManager->addError(__('Customer could not be deleted.')); return $resultRedirect->setPath('customer/index'); } @@ -39,9 +39,9 @@ public function execute() if (!empty($customerId)) { try { $this->_customerRepository->deleteById($customerId); - $this->messageManager->addSuccessMessage(__('You deleted the customer.')); + $this->messageManager->addSuccess(__('You deleted the customer.')); } catch (\Exception $exception) { - $this->messageManager->addErrorMessage($exception->getMessage()); + $this->messageManager->addError($exception->getMessage()); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php index 3d7f75884057f..25b4ddd4e1732 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Edit.php @@ -9,9 +9,6 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Exception\NoSuchEntityException; -/** - * Class Edit - */ class Edit extends \Magento\Customer\Controller\Adminhtml\Index implements HttpGetActionInterface { /** @@ -36,14 +33,17 @@ public function execute() $customer = $this->_customerRepository->getById($customerId); $customerData['account'] = $this->customerMapper->toFlatArray($customer); $customerData['account'][CustomerInterface::ID] = $customerId; - - $addresses = $customer->getAddresses(); - foreach ($addresses as $address) { - $customerData['address'][$address->getId()] = $this->addressMapper->toFlatArray($address); - $customerData['address'][$address->getId()]['id'] = $address->getId(); + try { + $addresses = $customer->getAddresses(); + foreach ($addresses as $address) { + $customerData['address'][$address->getId()] = $this->addressMapper->toFlatArray($address); + $customerData['address'][$address->getId()]['id'] = $address->getId(); + } + } catch (NoSuchEntityException $e) { + //do nothing } } catch (NoSuchEntityException $e) { - $this->messageManager->addExceptionMessage($e, __('Something went wrong while editing the customer.')); + $this->messageManager->addException($e, __('Something went wrong while editing the customer.')); $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('customer/*/index'); return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php index ba8c84a0ac273..7220de0356817 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php @@ -128,12 +128,10 @@ public function execute() $postItems = $this->getRequest()->getParam('items', []); if (!($this->getRequest()->getParam('isAjax') && count($postItems))) { - return $resultJson->setData( - [ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ] - ); + return $resultJson->setData([ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ]); } foreach (array_keys($postItems) as $customerId) { @@ -149,12 +147,10 @@ public function execute() $this->getEmailNotification()->credentialsChanged($this->getCustomer(), $currentCustomer->getEmail()); } - return $resultJson->setData( - [ - 'messages' => $this->getErrorMessages(), - 'error' => $this->isErrorExists() - ] - ); + return $resultJson->setData([ + 'messages' => $this->getErrorMessages(), + 'error' => $this->isErrorExists() + ]); } /** @@ -238,13 +234,13 @@ protected function saveCustomer(CustomerInterface $customer) $this->disableAddressValidation($customer); $this->customerRepository->save($customer); } catch (\Magento\Framework\Exception\InputException $e) { - $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); $this->logger->critical($e); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId($e->getMessage())); + $this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage())); $this->logger->critical($e); } catch (\Exception $e) { - $this->getMessageManager()->addErrorMessage($this->getErrorWithCustomerId('We can\'t save the customer.')); + $this->getMessageManager()->addError($this->getErrorWithCustomerId('We can\'t save the customer.')); $this->logger->critical($e); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php index f55c81da7e0b9..5a9c52bf9b1c0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php @@ -60,7 +60,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php index 85286573bc5e7..edaeea6a15eb2 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassDelete.php @@ -58,7 +58,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersDeleted) { - $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were deleted.', $customersDeleted)); + $this->messageManager->addSuccess(__('A total of %1 record(s) were deleted.', $customersDeleted)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php index e072c5cb4cd49..25c56ac60c14b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php @@ -64,7 +64,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index e52c9a772ed2d..4b40722ba9ab2 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -64,7 +64,7 @@ protected function massAction(AbstractCollection $collection) } if ($customersUpdated) { - $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); + $this->messageManager->addSuccess(__('A total of %1 record(s) were updated.', $customersUpdated)); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php index 3b9370c32bf6d..1e4fa91cbf899 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php @@ -44,9 +44,7 @@ public function execute() \Magento\Customer\Model\AccountManagement::EMAIL_REMINDER, $customer->getWebsiteId() ); - $this->messageManager->addSuccessMessage( - __('The customer will receive an email with a link to reset password.') - ); + $this->messageManager->addSuccess(__('The customer will receive an email with a link to reset password.')); } catch (NoSuchEntityException $exception) { $resultRedirect->setPath('customer/index'); return $resultRedirect; @@ -59,7 +57,7 @@ public function execute() } catch (SecurityViolationException $exception) { $this->messageManager->addErrorMessage($exception->getMessage()); } catch (\Exception $exception) { - $this->messageManager->addExceptionMessage( + $this->messageManager->addException( $exception, __('Something went wrong while resetting customer password.') ); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 3ee33af9ec073..38ed688a835bc 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -354,7 +354,7 @@ public function execute() $this->_getSession()->unsCustomerFormData(); // Done Saving customer, finish save action $this->_coreRegistry->register(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); - $this->messageManager->addSuccessMessage(__('You saved the customer.')); + $this->messageManager->addSuccess(__('You saved the customer.')); $returnToEdit = (bool)$this->getRequest()->getParam('back', false); } catch (\Magento\Framework\Validator\Exception $exception) { $messages = $exception->getMessages(); @@ -378,10 +378,7 @@ public function execute() $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } catch (\Exception $exception) { - $this->messageManager->addExceptionMessage( - $exception, - __('Something went wrong while saving the customer.') - ); + $this->messageManager->addException($exception, __('Something went wrong while saving the customer.')); $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php b/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php index 2747ba1a665fd..1fd06a3182948 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Locks/Unlock.php @@ -55,10 +55,10 @@ public function execute() // unlock customer if ($customerId) { $this->authentication->unlock($customerId); - $this->getMessageManager()->addSuccessMessage(__('Customer has been unlocked successfully.')); + $this->getMessageManager()->addSuccess(__('Customer has been unlocked successfully.')); } } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addError($e->getMessage()); } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php index 8677abfa89904..41311abee5da8 100644 --- a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php +++ b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php @@ -255,7 +255,7 @@ protected function addValidMessage($customerAddress, $validationResult) : (string)__('You will not be charged tax.'); } - $this->messageManager->addSuccessMessage(implode(' ', $message)); + $this->messageManager->addSuccess(implode(' ', $message)); return $this; } @@ -280,7 +280,7 @@ protected function addInvalidMessage($customerAddress) $message[] = (string)__('You will be charged tax.'); } - $this->messageManager->addErrorMessage(implode(' ', $message)); + $this->messageManager->addError(implode(' ', $message)); return $this; } @@ -307,7 +307,7 @@ protected function addErrorMessage($customerAddress) $email = $this->scopeConfig->getValue('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE); $message[] = (string)__('If you believe this is an error, please contact us at %1', $email); - $this->messageManager->addErrorMessage(implode(' ', $message)); + $this->messageManager->addError(implode(' ', $message)); return $this; } diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index d7372b07de14b..ada3adbfeb83b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -116,6 +116,7 @@ <deleteData createDataKey="createSimpleCategory1" stepKey="deleteSimpleCategory1"/> <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> <deleteData createDataKey="createConfigProduct1" stepKey="deleteConfigProduct1"/> <deleteData createDataKey="createVirtualProduct1" stepKey="deleteVirtualProduct1"/> <deleteData createDataKey="createBundleProduct1" stepKey="deleteBundleProduct1"/> diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 61efd9d562b45..01fc465d4ae84 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -255,12 +255,10 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap( - [ - ['id', false, $customerId], - ['key', false, $key], - ] - ); + ->willReturnMap([ + ['id', false, $customerId], + ['key', false, $key], + ]); $this->customerRepositoryMock->expects($this->any()) ->method('getById') @@ -283,7 +281,7 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($this->stringContains($successMessage)) ->willReturnSelf(); @@ -374,13 +372,11 @@ public function testSuccessRedirect( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap( - [ - ['id', false, $customerId], - ['key', false, $key], - ['back_url', false, $backUrl], - ] - ); + ->willReturnMap([ + ['id', false, $customerId], + ['key', false, $key], + ['back_url', false, $backUrl], + ]); $this->customerRepositoryMock->expects($this->any()) ->method('getById') @@ -403,7 +399,7 @@ public function testSuccessRedirect( ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($this->stringContains($successMessage)) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php index faf55347dba78..f8f47eedba3ef 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php @@ -346,13 +346,11 @@ public function testSuccessMessage( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap( - [ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ] - ); + ->willReturnMap([ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ]); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -373,7 +371,7 @@ public function testSuccessMessage( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); @@ -479,13 +477,11 @@ public function testSuccessRedirect( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap( - [ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ] - ); + ->willReturnMap([ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ]); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -506,18 +502,16 @@ public function testSuccessRedirect( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); $this->urlMock->expects($this->any()) ->method('getUrl') - ->willReturnMap( - [ - ['*/*/index', ['_secure' => true], $successUrl], - ['*/*/create', ['_secure' => true], $successUrl], - ] - ); + ->willReturnMap([ + ['*/*/index', ['_secure' => true], $successUrl], + ['*/*/create', ['_secure' => true], $successUrl], + ]); $this->redirectMock->expects($this->once()) ->method('success') ->with($this->equalTo($successUrl)) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index fd70a55ad7def..762c76b695dee 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -93,14 +93,12 @@ protected function setUp() $this->session = $this->getMockBuilder(\Magento\Customer\Model\Session::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'isLoggedIn', - 'setCustomerDataAsLoggedIn', - 'regenerateId', - 'setUsername', - ] - ) + ->setMethods([ + 'isLoggedIn', + 'setCustomerDataAsLoggedIn', + 'regenerateId', + 'setUsername', + ]) ->getMock(); $this->accountManagement = $this->getMockBuilder(\Magento\Customer\Api\AccountManagementInterface::class) @@ -224,7 +222,7 @@ public function testExecuteEmptyLoginData() ->willReturn([]); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with(__('A login and a password are required.')) ->willReturnSelf(); @@ -255,12 +253,10 @@ public function testExecuteSuccessCustomRedirect() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn( - [ - 'username' => $username, - 'password' => $password, - ] - ); + ->willReturn([ + 'username' => $username, + 'password' => $password, + ]); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -339,12 +335,10 @@ public function testExecuteSuccess() $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn( - [ - 'username' => $username, - 'password' => $password, - ] - ); + ->willReturn([ + 'username' => $username, + 'password' => $password, + ]); $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMockForAbstractClass(); @@ -432,12 +426,10 @@ public function testExecuteWithException( $this->request->expects($this->once()) ->method('getPost') ->with('login') - ->willReturn( - [ - 'username' => $username, - 'password' => $password, - ] - ); + ->willReturn([ + 'username' => $username, + 'password' => $password, + ]); $exception = new $exceptionData['exception'](__($exceptionData['message'])); @@ -496,12 +488,10 @@ protected function prepareContext() $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'isPost', - 'getPost', - ] - ) + ->setMethods([ + 'isPost', + 'getPost', + ]) ->getMock(); $this->resultRedirect = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -561,7 +551,7 @@ protected function mockExceptions($exception, $username) $url ); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($message) ->willReturnSelf(); @@ -573,7 +563,7 @@ protected function mockExceptions($exception, $username) case \Magento\Framework\Exception\AuthenticationException::class: $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' @@ -590,7 +580,7 @@ protected function mockExceptions($exception, $username) case '\Exception': $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with(__('An unspecified error occurred. Please contact us for assistance.')) ->willReturnSelf(); break; @@ -601,7 +591,7 @@ protected function mockExceptions($exception, $username) . 'Please wait and try again later.' ); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($message) ->willReturnSelf(); $this->session->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 046c023d35c64..4064b8586257d 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -146,7 +146,7 @@ public function testExecute() ->method('deleteById') ->with($addressId); $this->messageManager->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('You deleted the address.')); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -183,9 +183,9 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addComplexErrorMessage') - ->with('unableDeleteAddressMessage') - ->willReturnSelf(); + ->method('addError') + ->with(__('We can\'t delete the address right now.')) + ->willThrowException($exception); $this->messageManager->expects($this->once()) ->method('addException') ->with($exception, __('We can\'t delete the address right now.')); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index b0b8075325f8b..5f7064d5b124b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -167,7 +167,7 @@ public function testExecuteWithTaxClassAndException() ->method('save') ->with($this->group); $this->messageManager->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('You saved the customer group.')); $exception = new \Exception('Exception'); $this->resultRedirect->expects($this->at(0)) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 769c77dfcb4ea..45e64f6557d51 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -291,12 +291,10 @@ protected function prepareMocksForErrorMessagesProcessing() ->willReturn('Error text'); $this->resultJson->expects($this->once()) ->method('setData') - ->with( - [ - 'messages' => ['Error text'], - 'error' => true, - ] - ) + ->with([ + 'messages' => ['Error text'], + 'error' => true, + ]) ->willReturnSelf(); } @@ -342,12 +340,10 @@ public function testExecuteWithoutItems() $this->resultJson ->expects($this->once()) ->method('setData') - ->with( - [ - 'messages' => [__('Please correct the data sent.')], - 'error' => true, - ] - ) + ->with([ + 'messages' => [__('Please correct the data sent.')], + 'error' => true, + ]) ->willReturnSelf(); $this->assertSame($this->resultJson, $this->controller->execute()); } @@ -370,7 +366,7 @@ public function testExecuteLocalizedException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('[Customer ID: 12] Exception message'); $this->logger->expects($this->once()) ->method('critical') @@ -398,7 +394,7 @@ public function testExecuteException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('[Customer ID: 12] We can\'t save the customer.'); $this->logger->expects($this->once()) ->method('critical') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php index 4157359959ae4..10144bdc318c1 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -170,7 +170,7 @@ public function testExecute() ->willReturnMap([[10, $customerMock], [11, $customerMock], [12, $customerMock]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -199,7 +199,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php index b436b5b137c78..190ff2c06618f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php @@ -155,7 +155,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('A total of %1 record(s) were deleted.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -179,7 +179,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index 33e578224400b..daf9c64fe7b7b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -171,7 +171,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 971efc0e490bc..05624661a2de4 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -171,7 +171,7 @@ public function testExecute() ->willReturnMap([[10, true], [11, true], [12, true]]); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('A total of %1 record(s) were updated.', count($customersIds))); $this->resultRedirectMock->expects($this->any()) @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php index 5ec39360000cb..d2f8b8776081e 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/NewsletterTest.php @@ -150,7 +150,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccessMessage', 'addMessageMessage', 'addExceptionMessage'] + ['addSuccess', 'addMessage', 'addException'] )->getMock(); $contextArgs = [ diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php index 67ac60e6b9057..66e5b57eaa424 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -141,7 +141,7 @@ protected function setUp() $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccessMessage', 'addMessage', 'addExceptionMessage', 'addErrorMessage'] + ['addSuccess', 'addMessage', 'addException', 'addErrorMessage'] )->getMock(); $this->resultRedirectFactoryMock = $this->getMockBuilder( @@ -442,7 +442,7 @@ public function testResetPasswordActionException() $this->messageManager->expects( $this->once() )->method( - 'addExceptionMessage' + 'addException' )->with( $this->equalTo($exception), $this->equalTo('Something went wrong while resetting customer password.') @@ -502,7 +502,7 @@ public function testResetPasswordActionSendEmail() $this->messageManager->expects( $this->once() )->method( - 'addSuccessMessage' + 'addSuccess' )->with( $this->equalTo('The customer will receive an email with a link to reset password.') ); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 9724ac13dde8c..57f384d32d980 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -338,12 +338,10 @@ public function testExecuteWithExistentCustomer() $this->requestMock->expects($this->atLeastOnce()) ->method('getPostValue') - ->willReturnMap( - [ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ] - ); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -477,7 +475,7 @@ public function testExecuteWithExistentCustomer() ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('You saved the customer.')) ->willReturnSelf(); @@ -544,12 +542,10 @@ public function testExecuteWithNewCustomer() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap( - [ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ] - ); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -666,7 +662,7 @@ public function testExecuteWithNewCustomer() ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); $this->messageManagerMock->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with(__('You saved the customer.')) ->willReturnSelf(); @@ -727,12 +723,10 @@ public function testExecuteWithNewCustomerAndValidationException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap( - [ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ] - ); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -810,7 +804,7 @@ public function testExecuteWithNewCustomerAndValidationException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccessMessage'); + ->method('addSuccess'); $this->messageManagerMock->expects($this->once()) ->method('addMessage') @@ -818,12 +812,10 @@ public function testExecuteWithNewCustomerAndValidationException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with( - [ - 'customer' => $extractedData, - 'subscription' => $subscription, - ] - ); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -878,12 +870,10 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap( - [ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ] - ); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -961,7 +951,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccessMessage'); + ->method('addSuccess'); $this->messageManagerMock->expects($this->once()) ->method('addMessage') @@ -969,12 +959,10 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with( - [ - 'customer' => $extractedData, - 'subscription' => $subscription, - ] - ); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) @@ -1029,12 +1017,10 @@ public function testExecuteWithNewCustomerAndException() $this->requestMock->expects($this->any()) ->method('getPostValue') - ->willReturnMap( - [ - [null, null, $postValue], - [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ] - ); + ->willReturnMap([ + [null, null, $postValue], + [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], + ]); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( @@ -1113,20 +1099,18 @@ public function testExecuteWithNewCustomerAndException() ->method('register'); $this->messageManagerMock->expects($this->never()) - ->method('addSuccessMessage'); + ->method('addSuccess'); $this->messageManagerMock->expects($this->once()) - ->method('addExceptionMessage') + ->method('addException') ->with($exception, __('Something went wrong while saving the customer.')); $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') - ->with( - [ - 'customer' => $extractedData, - 'subscription' => $subscription, - ] - ); + ->with([ + 'customer' => $extractedData, + 'subscription' => $subscription, + ]); /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php index 55b4092af7141..c92d4ed7812ba 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Locks/UnlockTest.php @@ -118,7 +118,7 @@ public function testExecute() ->with($this->equalTo('customer_id')) ->will($this->returnValue($customerId)); $this->authenticationMock->expects($this->once())->method('unlock')->with($customerId); - $this->messageManagerMock->expects($this->once())->method('addSuccessMessage'); + $this->messageManagerMock->expects($this->once())->method('addSuccess'); $this->redirectMock->expects($this->once()) ->method('setPath') ->with($this->equalTo('customer/index/edit')) @@ -141,7 +141,7 @@ public function testExecuteWithException() ->method('unlock') ->with($customerId) ->willThrowException(new \Exception($phrase)); - $this->messageManagerMock->expects($this->once())->method('addErrorMessage'); + $this->messageManagerMock->expects($this->once())->method('addError'); $this->controller->execute(); } } diff --git a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 79766178a7670..8592d1bda66c1 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -210,11 +210,9 @@ public function testAfterAddressSaveRestricted( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomerAddress', - ] - ) + ->setMethods([ + 'getCustomerAddress', + ]) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -227,12 +225,10 @@ public function testAfterAddressSaveRestricted( $this->registry->expects($this->any()) ->method('registry') - ->willReturnMap( - [ - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, $processedFlag], - [BeforeAddressSaveObserver::VIV_CURRENTLY_SAVED_ADDRESS, $registeredAddressId], - ] - ); + ->willReturnMap([ + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, $processedFlag], + [BeforeAddressSaveObserver::VIV_CURRENTLY_SAVED_ADDRESS, $registeredAddressId], + ]); $this->helperAddress->expects($this->any()) ->method('getTaxCalculationAddressType') @@ -270,13 +266,11 @@ public function testAfterAddressSaveException() $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomer', - 'getForceProcess', - 'getVatId', - ] - ) + ->setMethods([ + 'getCustomer', + 'getForceProcess', + 'getVatId', + ]) ->getMock(); $address->expects($this->any()) ->method('getCustomer') @@ -290,11 +284,9 @@ public function testAfterAddressSaveException() $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomerAddress', - ] - ) + ->setMethods([ + 'getCustomerAddress', + ]) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -311,12 +303,10 @@ public function testAfterAddressSaveException() ->willReturn(false); $this->registry->expects($this->any()) ->method('register') - ->willReturnMap( - [ - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, true, false, $this->registry], - [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, false, true, $this->registry], - ] - ); + ->willReturnMap([ + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, true, false, $this->registry], + [AfterAddressSaveObserver::VIV_PROCESSED_FLAG, false, true, $this->registry], + ]); $this->model->execute($observer); } @@ -346,15 +336,13 @@ public function testAfterAddressSaveDefaultGroup( $customer = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getStore', - 'getDisableAutoGroupChange', - 'getGroupId', - 'setGroupId', - 'save', - ] - ) + ->setMethods([ + 'getStore', + 'getDisableAutoGroupChange', + 'getGroupId', + 'setGroupId', + 'save', + ]) ->getMock(); $customer->expects($this->exactly(2)) ->method('getStore') @@ -375,14 +363,12 @@ public function testAfterAddressSaveDefaultGroup( $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomer', - 'getForceProcess', - 'getVatId', - 'getCountry', - ] - ) + ->setMethods([ + 'getCustomer', + 'getForceProcess', + 'getVatId', + 'getCountry', + ]) ->getMock(); $address->expects($this->once()) ->method('getCustomer') @@ -399,11 +385,9 @@ public function testAfterAddressSaveDefaultGroup( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomerAddress', - ] - ) + ->setMethods([ + 'getCustomerAddress', + ]) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -473,12 +457,10 @@ public function testAfterAddressSaveNewGroup( $validationResult = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getIsValid', - 'getRequestSuccess', - ] - ) + ->setMethods([ + 'getIsValid', + 'getRequestSuccess', + ]) ->getMock(); $validationResult->expects($this->any()) ->method('getIsValid') @@ -489,15 +471,13 @@ public function testAfterAddressSaveNewGroup( $customer = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getStore', - 'getDisableAutoGroupChange', - 'getGroupId', - 'setGroupId', - 'save', - ] - ) + ->setMethods([ + 'getStore', + 'getDisableAutoGroupChange', + 'getGroupId', + 'setGroupId', + 'save', + ]) ->getMock(); $customer->expects($this->exactly(2)) ->method('getStore') @@ -523,16 +503,14 @@ public function testAfterAddressSaveNewGroup( $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomer', - 'getForceProcess', - 'getVatId', - 'getCountryId', - 'getCountry', - 'setVatValidationResult', - ] - ) + ->setMethods([ + 'getCustomer', + 'getForceProcess', + 'getVatId', + 'getCountryId', + 'getCountry', + 'setVatValidationResult', + ]) ->getMock(); $address->expects($this->any()) ->method('getCustomer') @@ -556,11 +534,9 @@ public function testAfterAddressSaveNewGroup( $observer = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() - ->setMethods( - [ - 'getCustomerAddress', - ] - ) + ->setMethods([ + 'getCustomerAddress', + ]) ->getMock(); $observer->expects($this->once()) ->method('getCustomerAddress') @@ -599,7 +575,7 @@ public function testAfterAddressSaveNewGroup( if ($resultValidMessage) { $this->messageManager->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($resultValidMessage) ->willReturnSelf(); } @@ -609,7 +585,7 @@ public function testAfterAddressSaveNewGroup( ->with($vatId) ->willReturn($vatId); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($resultInvalidMessage) ->willReturnSelf(); } @@ -619,7 +595,7 @@ public function testAfterAddressSaveNewGroup( ->with('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE) ->willReturn('admin@example.com'); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($resultErrorMessage) ->willReturnSelf(); } diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index e43bc4af1fcae..c31742519e581 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -77,28 +77,4 @@ </argument> </arguments> </type> - <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> - <arguments> - <argument name="configurationsMap" xsi:type="array"> - <item name="customerAlreadyExistsErrorMessage" xsi:type="array"> - <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> - <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Customer::messages/customerAlreadyExistsErrorMessage.phtml</item> - </item> - </item> - <item name="confirmAccountSuccessMessage" xsi:type="array"> - <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> - <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Customer::messages/confirmAccountSuccessMessage.phtml</item> - </item> - </item> - <item name="unableDeleteAddressMessage" xsi:type="array"> - <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> - <item name="data" xsi:type="array"> - <item name="template" xsi:type="string">Magento_Customer::messages/unableDeleteAddressMessage.phtml</item> - </item> - </item> - </argument> - </arguments> - </type> -</config> +</config> \ No newline at end of file diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml deleted file mode 100644 index a5313078e13cc..0000000000000 --- a/app/code/Magento/Customer/view/frontend/templates/messages/unableDeleteAddressMessage.phtml +++ /dev/null @@ -1,9 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/** @var \Magento\Framework\View\Element\Template $block */ -?> -<?= $block->escapeHtml(__('We can\'t delete the address right now.')); From d12a2ab560aab2290df06cac5047be3edbb09991 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Sun, 25 Aug 2019 08:13:11 +0300 Subject: [PATCH 0438/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento/Customer/Controller/Account/CreatePost.php --- .../Controller/Account/CreatePost.php | 78 ++++++++++++++----- .../Controller/Account/CreatePostTest.php | 38 +++++---- app/code/Magento/Customer/etc/frontend/di.xml | 32 +++++++- ...tomerVatBillingAddressSuccessMessage.phtml | 9 +++ ...omerVatShippingAddressSuccessMessage.phtml | 9 +++ 5 files changed, 129 insertions(+), 37 deletions(-) create mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/customerVatBillingAddressSuccessMessage.phtml create mode 100644 app/code/Magento/Customer/view/frontend/templates/messages/customerVatShippingAddressSuccessMessage.phtml diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index a2be0f68b56cb..2145d7dba0049 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -21,6 +21,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\MessageInterface; use Magento\Framework\Phrase; use Magento\Store\Model\StoreManagerInterface; use Magento\Customer\Api\AccountManagementInterface; @@ -118,6 +119,11 @@ class CreatePost extends AbstractAccount implements CsrfAwareActionInterface, Ht */ protected $session; + /** + * @var StoreManagerInterface + */ + protected $storeManager; + /** * @var AccountRedirect */ @@ -365,20 +371,19 @@ public function execute() ); $confirmationStatus = $this->accountManagement->getConfirmationStatus($customer->getId()); if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { - $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); - // @codingStandardsIgnoreStart - $this->messageManager->addSuccess( - __( - 'You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', - $email - ) + $this->messageManager->addComplexSuccessMessage( + 'confirmAccountSuccessMessage', + [ + 'url' => $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()), + ] ); - // @codingStandardsIgnoreEnd $url = $this->urlModel->getUrl('*/*/index', ['_secure' => true]); $resultRedirect->setUrl($this->_redirect->success($url)); } else { $this->session->setCustomerDataAsLoggedIn($customer); - $this->messageManager->addSuccess($this->getSuccessMessage()); + + $this->messageManager->addMessage($this->getMessageManagerSuccessMessage()); + $requestedRedirect = $this->accountRedirect->getRedirectCookie(); if (!$this->scopeConfig->getValue('customer/startup/redirect_dashboard') && $requestedRedirect) { $resultRedirect->setUrl($this->_redirect->success($requestedRedirect)); @@ -395,23 +400,21 @@ public function execute() return $resultRedirect; } catch (StateException $e) { - $url = $this->urlModel->getUrl('customer/account/forgotpassword'); - // @codingStandardsIgnoreStart - $message = __( - 'There is already an account with this email address. If you are sure that it is your email address, <a href="%1">click here</a> to get your password and access your account.', - $url + $this->messageManager->addComplexErrorMessage( + 'customerAlreadyExistsErrorMessage', + [ + 'url' => $this->urlModel->getUrl('customer/account/forgotpassword'), + ] ); - // @codingStandardsIgnoreEnd - $this->messageManager->addError($message); } catch (InputException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($this->escaper->escapeHtml($error->getMessage())); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (LocalizedException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t save the customer.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t save the customer.')); } $this->session->setCustomerFormData($this->getRequest()->getPostValue()); @@ -437,6 +440,8 @@ protected function checkPasswordConfirmation($password, $confirmation) /** * Retrieve success message * + * @deprecated + * @see getMessageManagerSuccessMessage() * @return string */ protected function getSuccessMessage() @@ -462,4 +467,37 @@ protected function getSuccessMessage() } return $message; } + + /** + * Retrieve success message manager message + * + * @return MessageInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + protected function getMessageManagerSuccessMessage(): MessageInterface + { + if ($this->addressHelper->isVatValidationEnabled()) { + if ($this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING) { + $identifier = 'customerVatShippingAddressSuccessMessage'; + } else { + $identifier = 'customerVatBillingAddressSuccessMessage'; + } + + $message = $this->messageManager + ->createMessage(MessageInterface::TYPE_SUCCESS, $identifier) + ->setData( + [ + 'url' => $this->urlModel->getUrl('customer/address/edit'), + ] + ); + } else { + $message = $this->messageManager + ->createMessage(MessageInterface::TYPE_SUCCESS) + ->setText( + __('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName()) + ); + } + + return $message; + } } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php index f8f47eedba3ef..faf55347dba78 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php @@ -346,11 +346,13 @@ public function testSuccessMessage( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ]); + ->willReturnMap( + [ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ] + ); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -371,7 +373,7 @@ public function testSuccessMessage( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); @@ -477,11 +479,13 @@ public function testSuccessRedirect( $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ - ['password', null, $password], - ['password_confirmation', null, $password], - ['is_subscribed', false, true], - ]); + ->willReturnMap( + [ + ['password', null, $password], + ['password_confirmation', null, $password], + ['is_subscribed', false, true], + ] + ); $this->customerMock->expects($this->once()) ->method('setAddresses') @@ -502,16 +506,18 @@ public function testSuccessRedirect( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); $this->urlMock->expects($this->any()) ->method('getUrl') - ->willReturnMap([ - ['*/*/index', ['_secure' => true], $successUrl], - ['*/*/create', ['_secure' => true], $successUrl], - ]); + ->willReturnMap( + [ + ['*/*/index', ['_secure' => true], $successUrl], + ['*/*/create', ['_secure' => true], $successUrl], + ] + ); $this->redirectMock->expects($this->once()) ->method('success') ->with($this->equalTo($successUrl)) diff --git a/app/code/Magento/Customer/etc/frontend/di.xml b/app/code/Magento/Customer/etc/frontend/di.xml index c31742519e581..3b9675178c052 100644 --- a/app/code/Magento/Customer/etc/frontend/di.xml +++ b/app/code/Magento/Customer/etc/frontend/di.xml @@ -77,4 +77,34 @@ </argument> </arguments> </type> -</config> \ No newline at end of file + <type name="Magento\Framework\View\Element\Message\MessageConfigurationsPool"> + <arguments> + <argument name="configurationsMap" xsi:type="array"> + <item name="customerAlreadyExistsErrorMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/customerAlreadyExistsErrorMessage.phtml</item> + </item> + </item> + <item name="confirmAccountSuccessMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/confirmAccountSuccessMessage.phtml</item> + </item> + </item> + <item name="customerVatShippingAddressSuccessMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/customerVatShippingAddressSuccessMessage.phtml</item> + </item> + </item> + <item name="customerVatBillingAddressSuccessMessage" xsi:type="array"> + <item name="renderer" xsi:type="const">\Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE</item> + <item name="data" xsi:type="array"> + <item name="template" xsi:type="string">Magento_Customer::messages/customerVatBillingAddressSuccessMessage.phtml</item> + </item> + </item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/customerVatBillingAddressSuccessMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/customerVatBillingAddressSuccessMessage.phtml new file mode 100644 index 0000000000000..294412cbc706d --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/templates/messages/customerVatBillingAddressSuccessMessage.phtml @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> +<?= $block->escapeHtml(__('If you are a registered VAT customer, please <a href="%1">click here</a> to enter your billing address for proper VAT calculation.', $block->getData('url')), ['a']); diff --git a/app/code/Magento/Customer/view/frontend/templates/messages/customerVatShippingAddressSuccessMessage.phtml b/app/code/Magento/Customer/view/frontend/templates/messages/customerVatShippingAddressSuccessMessage.phtml new file mode 100644 index 0000000000000..72dbf3f5e03d4 --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/templates/messages/customerVatShippingAddressSuccessMessage.phtml @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** @var \Magento\Framework\View\Element\Template $block */ +?> +<?= $block->escapeHtml(__('If you are a registered VAT customer, please <a href="%1">click here</a> to enter your shipping address for proper VAT calculation.', $block->getData('url')), ['a']); From dd344523c67bf0af3d1f8e6a43af62a8e993bf26 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Sun, 25 Aug 2019 14:36:48 +0300 Subject: [PATCH 0439/2437] magento/magento2#: Replace deprecated addError, addSuccess, addException methods in Magento/Customer/Controller/Account/CreatePost.php --- app/code/Magento/Customer/Controller/Account/CreatePost.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 2145d7dba0049..57b670684411e 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -474,7 +474,7 @@ protected function getSuccessMessage() * @return MessageInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ - protected function getMessageManagerSuccessMessage(): MessageInterface + private function getMessageManagerSuccessMessage(): MessageInterface { if ($this->addressHelper->isVatValidationEnabled()) { if ($this->addressHelper->getTaxCalculationAddressType() == Address::TYPE_SHIPPING) { From 62111e3f2f1d7ec12e17f59dd959f69689403b68 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Mon, 26 Aug 2019 17:03:45 +0300 Subject: [PATCH 0440/2437] createShipmentEntityTest --- ...eatedShipmentInShipmentsTabActionGroup.xml | 18 +++++ ...sertShipmentInShipmentsGridActionGroup.xml | 27 +++++++ .../AdminAssertShipmentItemsActionGroup.xml | 22 ++++++ ...CreateShipmentFromOrderPageActionGroup.xml | 29 +++++++ .../AssertThereIsNoShipButtonActionGroup.xml | 15 ++++ .../Test/Mftf/Page/AdminShipmentsGridPage.xml | 14 ++++ .../Section/AdminShipmentItemsSection.xml | 1 + .../AdminShipmentPaymentShippingSection.xml | 2 +- .../Section/AdminShipmentsGridSection.xml | 19 +++++ .../AdminCreatePartialShipmentEntityTest.xml | 74 ++++++++++++++++++ .../Test/AdminCreateShipmentEntityTest.xml | 75 +++++++++++++++++++ .../TestCase/CreateShipmentEntityTest.xml | 3 +- 12 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml new file mode 100644 index 0000000000000..1a7d3355e4ee4 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="AdminAssertCreatedShipmentsInShipmentsTabActionGroup"> + <click stepKey="navigateToShipmentsTab" selector="{{AdminOrderDetailsOrderViewSection.shipments}}"/> + <waitForPageLoad stepKey="waitForTabLoad"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <assertNotEmpty actual="$grabShipmentId" stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml new file mode 100644 index 0000000000000..de293c24a9c5d --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertShipmentInShipmentsGrid"> + <arguments> + <argument name="shipmentId" type="string"/> + </arguments> + <!--Assert Shipment in Shipments Grid--> + <amOnPage url="{{AdminShipmentsGridPage.url}}" stepKey="onShipmentsGridPage"/> + <waitForPageLoad stepKey="waitForLoadingPage"/> + <conditionalClick selector="{{AdminShipmentsGridSection.clearFilters}}" dependentSelector="{{AdminShipmentsGridSection.clearFilters}}" visible="true" stepKey="clearFilter"/> + <waitForLoadingMaskToDisappear stepKey="waitForFilterLoad"/> + <click selector="{{AdminShipmentsGridSection.buttonFilters}}" stepKey="openFilterSearch"/> + <waitForLoadingMaskToDisappear stepKey="waitForFilterFields"/> + <fillField userInput="{{shipmentId}}" selector="{{AdminShipmentsGridSection.fieldShipment}}" stepKey="fillSearchByShipmentId"/> + <click selector="{{AdminShipmentsGridSection.applyFilter}}" stepKey="clickSearchButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchResult"/> + <see userInput="{{shipmentId}}" selector="{{AdminShipmentsGridSection.rowShipments}}" stepKey="seeShipmentId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml new file mode 100644 index 0000000000000..c4a0b4536fe20 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="AdminAssertShipmentItemsActionGroup"> + <arguments> + <argument name="product" defaultValue="" type="string"/> + <argument name="qty" defaultValue="" type="string"/> + </arguments> + <click selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="clickView"/> + <scrollTo selector="{{AdminShipmentItemsSection.itemName('1')}}" stepKey="scrollToShippedItems"/> + <see userInput="{{product}}" selector="{{AdminShipmentItemsSection.itemName('1')}}" stepKey="seeProductName"/> + <see userInput="{{qty}}" selector="{{AdminShipmentItemsSection.productQty}}" stepKey="seeQty"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..0e1358651c58a --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Create Shipment With Tracking Number--> + <actionGroup name="AdminCreateShipmentFromOrderPage"> + <arguments> + <argument name="Title" defaultValue="" type="string"/> + <argument name="Number" defaultValue="" type="string"/> + <argument name="Comment" defaultValue="" type="string"/> + <argument name="Qty" defaultValue="" type="string"/> + </arguments> + + <click stepKey="clickShipButton" selector="{{AdminOrderDetailsMainActionsSection.ship}}"/> + <click stepKey="clickAddTrackingNumber" selector="{{AdminShipmentPaymentShippingSection.AddTrackingNumber}}"/> + <fillField stepKey="fillTitle" userInput="{{Title}}" selector="{{AdminShipmentPaymentShippingSection.Title('1')}}"/> + <fillField stepKey="fillNumber" userInput="{{Number}}" selector="{{AdminShipmentPaymentShippingSection.Number('1')}}"/> + <fillField stepKey="fillQty" userInput="{{Qty}}" selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}"/> + <fillField stepKey="fillComment" userInput="{{Comment}}" selector="{{AdminShipmentTotalSection.CommentText}}"/> + <click stepKey="clickSubmitButton" selector="{{AdminShipmentMainActionsSection.submitShipment}}"/> + <see userInput="The shipment has been created." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml new file mode 100644 index 0000000000000..10521769c5070 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Create Shipment With Tracking Number--> + <actionGroup name="AssertThereIsNoShipButtonActionGroup"> + <dontSee stepKey="dontSeeShipButton" selector="{{AdminOrderDetailsMainActionsSection.ship}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml new file mode 100644 index 0000000000000..61aad55401248 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminShipmentsGridPage" url="sales/shipment/" area="admin" module="Magento_Sales"> + <section name="AdminShipmentsGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml index 0345c3f2949f4..3630de8978924 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml @@ -16,5 +16,6 @@ <element name="nameColumn" type="text" selector=".order-shipment-table .col-product .product-title"/> <element name="skuColumn" type="text" selector=".order-shipment-table .col-product .product-sku-block"/> <element name="itemQtyInvoiced" type="text" selector="(//*[@class='col-ordered-qty']//th[contains(text(), 'Invoiced')]/following-sibling::td)[{{var}}]" parameterized="true"/> + <element name="productQty" type="text" selector="td.col-qty"/> </section> </sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml index 48c7106c2d65e..eb94014d7a50c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml @@ -17,7 +17,7 @@ <element name="AddTrackingNumber" type="button" selector="#tracking_numbers_table tfoot [data-ui-id='shipment-tracking-add-button']"/> <element name="Carrier" type="select" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-carrier select" parameterized="true"/> <element name="Title" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-title input" parameterized="true"/> - <element name="Number" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}} .col-number input)" parameterized="true"/> + <element name="Number" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-number input" parameterized="true"/> <element name="Delete" type="button" selector="#tracking_numbers_table tr:nth-of-type({{row}} .col-delete button.action-delete)" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml new file mode 100644 index 0000000000000..84aed3052c736 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminShipmentsGridSection"> + <element name="shipmentId" type="text" selector="//*[@id='sales_order_view_tabs_order_shipments_content']//tbody/tr/td[2]/div"/> + <element name="clearFilters" type="button" selector="button.action-tertiary.action-clear"/> + <element name="buttonFilters" type="button" selector=".data-grid-filters-action-wrap > button"/> + <element name="fieldShipment" type="input" selector="input[name='increment_id']"/> + <element name="applyFilter" type="button" selector="button[data-action='grid-filter-apply']"/> + <element name="rowShipments" type="text" selector="div.data-grid-cell-content"/> + </section> +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml new file mode 100644 index 0000000000000..98fd20b3368c2 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreatePartialShipmentEntityTest"> + <annotations> + <stories value="Create Partial Shipment Entity"/> + <title value="Create Partial Shipment for Offline Payment Methods"/> + <description value="Admin Should be Able to Create Partial Shipments"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + </createData> + <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> + <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Create Order --> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productQty" value="2"/> + </actionGroup> + <!-- Select Free shipping --> + <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption"/> + <!--Click *Submit Order* button--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <!-- Create Partial Shipment --> + <actionGroup ref="AdminCreateShipmentFromOrderPage" stepKey="createNewShipment"> + <argument name="Qty" value="1"/> + <argument name="Title" value="Title"/> + <argument name="Number" value="199"/> + <argument name="Comment" value="comments for shipment"/> + </actionGroup> + <!-- Assert There is no "Ship Button" in Order Information --> + <actionGroup ref="AssertThereIsNoShipButtonActionGroup" stepKey="dontSeeShipButton"/> + <!-- Assert Created Shipment in Shipments Tab--> + <actionGroup ref="AdminAssertCreatedShipmentsInShipmentsTabActionGroup" stepKey="assertCreatedShipment"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <!-- Assert Shipment items --> + <actionGroup ref="AdminAssertShipmentItemsActionGroup" stepKey="assertShipmentItems"> + <argument name="product" value="$$createSimpleProduct.name$$"/> + <argument name="qty" value="1"/> + </actionGroup> + <!-- Assert Created Shipment in Shipments Grid--> + <actionGroup ref="AdminAssertShipmentInShipmentsGrid" stepKey="assertShipmentInGrid"> + <argument name="shipmentId" value="{$grabShipmentId}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml new file mode 100644 index 0000000000000..6dfccc3171758 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateShipmentEntityWithTrackingNumberTest"> + <annotations> + <stories value="Shipment Entity With Tracking Number"/> + <title value="Create Shipment for Offline Payment Methods"/> + <description value="Admin Should be Able to Create Shipments"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + </createData> + <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> + <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + + <!-- Create Order --> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Select Free shipping --> + <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption"/> + <!--Click *Submit Order* button--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <!-- Create Shipment --> + <actionGroup ref="AdminCreateShipmentFromOrderPage" stepKey="createNewShipment"> + <argument name="Title" value="Title"/> + <argument name="Number" value="199"/> + <argument name="Qty" value="1"/> + <argument name="Comment" value="comments for shipment"/> + </actionGroup> + + <!-- Assert There is no "Ship Button" in Order Information --> + <actionGroup ref="AssertThereIsNoShipButtonActionGroup" stepKey="dontSeeShipButton"/> + <!-- Assert Created Shipment in Shipments Tab--> + <actionGroup ref="AdminAssertCreatedShipmentsInShipmentsTabActionGroup" stepKey="assertCreatedShipment"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <!-- Assert Shipment items --> + <actionGroup ref="AdminAssertShipmentItemsActionGroup" stepKey="assertShipmentItems"> + <argument name="product" value="$$createSimpleProduct.name$$"/> + <argument name="qty" value="1"/> + </actionGroup> + <!-- Assert Created Shipment in Shipments Grid--> + <actionGroup ref="AdminAssertShipmentInShipmentsGrid" stepKey="assertShipmentInGrid"> + <argument name="shipmentId" value="{$grabShipmentId}"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml index 06acf95effdbf..032651c818b91 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Shipping\Test\TestCase\CreateShipmentEntityTest" summary="Create Shipment for Offline Payment Methods" ticketId="MAGETWO-28708"> <variation name="CreateShipmentEntityTestVariation1" summary="Shipment with tracking number"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> <data name="order/data/total_qty_ordered/0" xsi:type="string">1</data> @@ -38,6 +38,7 @@ <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentInShipmentsGrid" /> <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentItems" /> <constraint name="Magento\Shipping\Test\Constraint\AssertShipTotalQuantity" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 78fd8117df9a6fc72e51a14786d58eb5504417fb Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 26 Aug 2019 10:01:00 -0500 Subject: [PATCH 0441/2437] MC-19072: Braintree: Unable to fill "Credit Card Number" and "Expiration Date" fields on Checkout page if "CVV Verification" = No --- .../Braintree/view/frontend/web/template/payment/form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html index 9bcb5dad8b636..8da8927a3b247 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html @@ -23,7 +23,7 @@ <!-- ko template: getTemplate() --><!-- /ko --> <!--/ko--> </div> - <form id="co-transparent-form-braintree" class="form" data-bind="" method="post" action="#" novalidate="novalidate"> + <form id="co-transparent-form-braintree" class="form" data-bind="afterRender: initHostedFields" method="post" action="#" novalidate="novalidate"> <fieldset data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + getCode()}"> <legend class="legend"> <span><!-- ko i18n: 'Credit Card Information'--><!-- /ko --></span> @@ -87,7 +87,7 @@ <span><!-- ko i18n: 'Card Verification Number'--><!-- /ko --></span> </label> <div class="control _with-tooltip"> - <div data-bind="afterRender: initHostedFields, attr: {id: getCode() + '_cc_cid'}" class="hosted-control hosted-cid"></div> + <div data-bind="attr: {id: getCode() + '_cc_cid'}" class="hosted-control hosted-cid"></div> <div class="hosted-error"><!-- ko i18n: 'Please, enter valid Card Verification Number'--><!-- /ko --></div> <div class="field-tooltip toggle"> From 3cb1ab2dffd1b3290fd896704129f75a4f24dd54 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 26 Aug 2019 15:30:57 -0500 Subject: [PATCH 0442/2437] MC-18685: Remove custom layout updates from admin --- .../Page/CustomLayout/CustomLayoutManager.php | 62 +++++++++++++------ app/code/Magento/Cms/etc/di.xml | 5 -- .../Model/Page/CustomLayoutManagerTest.php | 19 +++--- .../Model/Page/CustomLayoutRepositoryTest.php | 22 ++++--- .../Framework/View/Model/Layout/Merge.php | 15 +++++ 5 files changed, 80 insertions(+), 43 deletions(-) diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php index 8bf902f009bc8..d11d86433152d 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -15,20 +15,15 @@ use Magento\Framework\App\Area; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; -use Magento\Framework\View\File; -use Magento\Framework\View\File\CollectorInterface; use Magento\Framework\View\Result\Page as PageLayout; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; /** * @inheritDoc */ class CustomLayoutManager implements CustomLayoutManagerInterface { - /** - * @var CollectorInterface - */ - private $fileCollector; - /** * @var FlyweightFactory */ @@ -45,21 +40,31 @@ class CustomLayoutManager implements CustomLayoutManagerInterface private $pageRepository; /** - * @param CollectorInterface $fileCollector + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** * @param FlyweightFactory $themeFactory * @param DesignInterface $design * @param PageRepositoryInterface $pageRepository + * @param LayoutProcessorFactory $layoutProcessorFactory */ public function __construct( - CollectorInterface $fileCollector, FlyweightFactory $themeFactory, DesignInterface $design, - PageRepositoryInterface $pageRepository + PageRepositoryInterface $pageRepository, + LayoutProcessorFactory $layoutProcessorFactory ) { - $this->fileCollector = $fileCollector; $this->themeFactory = $themeFactory; $this->design = $design; $this->pageRepository = $pageRepository; + $this->layoutProcessorFactory = $layoutProcessorFactory; } /** @@ -73,23 +78,42 @@ private function sanitizeIdentifier(PageInterface $page): string return str_replace('/', '_', $page->getIdentifier()); } + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + /** * @inheritDoc */ public function fetchAvailableFiles(PageInterface $page): array { $identifier = $this->sanitizeIdentifier($page); - $layoutFiles = $this->fileCollector->getFiles( - $this->themeFactory->create($this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND)), - 'cms_page_view_selectable_' .$identifier .'_*.xml' - ); + $handles = $this->getLayoutProcessor()->getAvailableHandles(); return array_filter( array_map( - function (File $file) use ($identifier) : ?string { + function (string $handle) use ($identifier) : ?string { preg_match( - '/selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', - $file->getName(), + '/^cms\_page\_view\_selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', + $handle, $selectable ); if (!empty($selectable[1])) { @@ -98,7 +122,7 @@ function (File $file) use ($identifier) : ?string { return null; }, - $layoutFiles + $handles ) ); } diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index ef95a004102ac..fdefa3fa0daee 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -236,10 +236,5 @@ <plugin name="cms" type="Magento\Cms\Model\Plugin\Product" sortOrder="100"/> </type> <preference for="Magento\Cms\Model\Page\CustomLayoutManagerInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager" /> - <type name="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager"> - <arguments> - <argument name="fileCollector" xsi:type="object">Magento\Framework\View\Layout\File\Collector\Aggregated\Proxy</argument> - </arguments> - </type> <preference for="Magento\Cms\Model\Page\CustomLayoutRepositoryInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository" /> </config> diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php index 7e405725a2d8b..966afa0febc1c 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -13,9 +13,9 @@ use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\Framework\View\File\CollectorInterface; -use Magento\Framework\View\File; use Magento\Framework\View\Result\PageFactory as PageResultFactory; +use Magento\Framework\View\Model\Layout\MergeFactory; +use Magento\Framework\View\Model\Layout\Merge; /** * Test the manager. @@ -50,17 +50,18 @@ protected function setUp() $objectManager = Bootstrap::getObjectManager(); $this->resultFactory = $objectManager->get(PageResultFactory::class); //Mocking available list of files for the page. - $files = [ - new File('cms_page_view_selectable_page100_select1.xml', 'test'), - new File('cms_page_view_selectable_page100_select2.xml', 'test') + $handles = [ + 'cms_page_view_selectable_page100_select1', + 'cms_page_view_selectable_page100_select2' ]; - $fileCollector = $this->getMockForAbstractClass(CollectorInterface::class); - $fileCollector->method('getFiles') - ->willReturn($files); + $processor = $this->getMockBuilder(Merge::class)->disableOriginalConstructor()->getMock(); + $processor->method('getAvailableHandles')->willReturn($handles); + $processorFactory = $this->getMockBuilder(MergeFactory::class)->disableOriginalConstructor()->getMock(); + $processorFactory->method('create')->willReturn($processor); $this->manager = $objectManager->create( CustomLayoutManagerInterface::class, - ['fileCollector' => $fileCollector] + ['layoutProcessorFactory' => $processorFactory] ); $this->repo = $objectManager->create( CustomLayoutRepositoryInterface::class, diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php index 4736774ea0f02..12b436fd32411 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php @@ -12,10 +12,10 @@ use Magento\Cms\Model\PageFactory; use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Model\Layout\Merge; +use Magento\Framework\View\Model\Layout\MergeFactory; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\Framework\View\File\CollectorInterface; -use Magento\Framework\View\File; /** * Test the repository. @@ -38,20 +38,22 @@ class CustomLayoutRepositoryTest extends TestCase protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + //Mocking available list of files for the page. - $files = [ - new File('cms_page_view_selectable_page100_select1.xml', 'test'), - new File('cms_page_view_selectable_page100_select2.xml', 'test') + $handles = [ + 'cms_page_view_selectable_page100_select1', + 'cms_page_view_selectable_page100_select2' ]; - $fileCollector = $this->getMockForAbstractClass(CollectorInterface::class); - $fileCollector->method('getFiles') - ->willReturn($files); - + $processor = $this->getMockBuilder(Merge::class)->disableOriginalConstructor()->getMock(); + $processor->method('getAvailableHandles')->willReturn($handles); + $processorFactory = $this->getMockBuilder(MergeFactory::class)->disableOriginalConstructor()->getMock(); + $processorFactory->method('create')->willReturn($processor); $manager = $objectManager->create( CustomLayoutManagerInterface::class, - ['fileCollector' => $fileCollector] + ['layoutProcessorFactory' => $processorFactory] ); $this->repo = $objectManager->create(CustomLayoutRepositoryInterface::class, ['manager' => $manager]); + $this->pageFactory = $objectManager->get(PageFactory::class); } diff --git a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php index d307935375f41..fe79976039a9c 100644 --- a/lib/internal/Magento/Framework/View/Model/Layout/Merge.php +++ b/lib/internal/Magento/Framework/View/Model/Layout/Merge.php @@ -382,6 +382,21 @@ public function getPageHandles() return $this->pageHandles; } + /** + * List of all available layout handles. + * + * @return string[] + */ + public function getAvailableHandles(): array + { + $handles = []; + $nodes = $this->getFileLayoutUpdatesXml()->xpath('/layouts/handle[@id]'); + foreach ($nodes as $node) { + $handles[] = (string)$node->attributes()->id; + } + return $handles; + } + /** * Retrieve all design abstractions that exist in the system. * From d110db27a979202df882c88c42fc2d0833683659 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@adobe.com> Date: Mon, 26 Aug 2019 16:05:24 -0500 Subject: [PATCH 0443/2437] MC-18532: Update Changelog based on delivered scope Update Changelog for 2.3.3-develop --- CHANGELOG.md | 417 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04fb46a825f62..4f760535cb91b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,420 @@ +2.3.3 +============= +* GitHub issues: + * [#533](https://github.com/magento/magento2/issues/533) -- By default Allow all access in .htaccess (fixed in [magento/graphql-ce#578](https://github.com/magento/graphql-ce/pull/578)) + * [#601](https://github.com/magento/magento2/issues/601) -- Admin tabs js error. (fixed in [magento/graphql-ce#632](https://github.com/magento/graphql-ce/pull/632)) + * [#631](https://github.com/magento/magento2/issues/631) -- Image Management / Editing (fixed in [magento/graphql-ce#634](https://github.com/magento/graphql-ce/pull/634)) + * [#20124](https://github.com/magento/magento2/issues/20124) -- Sort By label is hidden by Shop By Menu on listing page in iphone5 device (fixed in [magento/magento2#20135](https://github.com/magento/magento2/pull/20135)) + * [#21978](https://github.com/magento/magento2/issues/21978) -- Adding product image: File doesn't exist (fixed in [magento/magento2#22020](https://github.com/magento/magento2/pull/22020)) + * [#22045](https://github.com/magento/magento2/issues/22045) -- Instant Purchase on product page not working properly. (fixed in [magento/magento2#22260](https://github.com/magento/magento2/pull/22260)) + * [#22134](https://github.com/magento/magento2/issues/22134) -- Paypal buttons disable issue - Magento 2.3.1 (fixed in [magento/magento2#22260](https://github.com/magento/magento2/pull/22260)) + * [#22249](https://github.com/magento/magento2/issues/22249) -- Configurable Product Gallery Images Out of Order when More than 10 images (fixed in [magento/magento2#22287](https://github.com/magento/magento2/pull/22287)) + * [#22527](https://github.com/magento/magento2/issues/22527) -- Wishlist and compare icon align issue in product listing page (fixed in [magento/magento2#22532](https://github.com/magento/magento2/pull/22532)) + * [#628](https://github.com/magento/magento2/issues/628) -- Feature Request: Parent entities links in child entities grid. (fixed in [magento/graphql-ce#636](https://github.com/magento/graphql-ce/pull/636)) + * [#640](https://github.com/magento/magento2/issues/640) -- [Insight] Files should not be executable (fixed in [magento/graphql-ce#648](https://github.com/magento/graphql-ce/pull/648)) + * [#603](https://github.com/magento/magento2/issues/603) -- 'Continue' button is disabled even though 'I've read OSL licence' is checked (fixed in [magento/graphql-ce#653](https://github.com/magento/graphql-ce/pull/653)) + * [#22406](https://github.com/magento/magento2/issues/22406) -- Store view specific labels cut in left navigation menu (fixed in [magento/magento2#22423](https://github.com/magento/magento2/pull/22423)) + * [#19515](https://github.com/magento/magento2/issues/19515) -- Create new order from backend saves the credit card when it is told not to (fixed in [magento/magento2#19767](https://github.com/magento/magento2/pull/19767)) + * [#21473](https://github.com/magento/magento2/issues/21473) -- Form element validation is not triggered when validation rules change (fixed in [magento/magento2#21992](https://github.com/magento/magento2/pull/21992)) + * [#22641](https://github.com/magento/magento2/issues/22641) -- Typo Issue and Missing header title at Customer Sales order grid (fixed in [magento/magento2#22643](https://github.com/magento/magento2/pull/22643)) + * [#22647](https://github.com/magento/magento2/issues/22647) -- In customer account create page word not readable, should use '-' after break to new line In mobile view (fixed in [magento/magento2#22656](https://github.com/magento/magento2/pull/22656)) + * [#22395](https://github.com/magento/magento2/issues/22395) -- config:set -le and -lc short form options don't work (fixed in [magento/magento2#22720](https://github.com/magento/magento2/pull/22720)) + * [#198](https://github.com/magento/magento2/issues/198) -- Better Search Please!! NOW (fixed in [magento/graphql-ce#371](https://github.com/magento/graphql-ce/pull/371)) + * [#436](https://github.com/magento/magento2/issues/436) -- [Feature request]custom option attach an image (fixed in [magento/graphql-ce#445](https://github.com/magento/graphql-ce/pull/445)) + * [#309](https://github.com/magento/magento2/issues/309) -- Terrible UI in Backend (fixed in [magento/graphql-ce#504](https://github.com/magento/graphql-ce/pull/504)) + * [#535](https://github.com/magento/magento2/issues/535) -- A few bugs (fixed in [magento/graphql-ce#650](https://github.com/magento/graphql-ce/pull/650)) + * [#658](https://github.com/magento/magento2/issues/658) -- Inline translate malfunctioning (fixed in [magento/graphql-ce#665](https://github.com/magento/graphql-ce/pull/665) and [magento/graphql-ce#744](https://github.com/magento/graphql-ce/pull/744)) + * [#657](https://github.com/magento/magento2/issues/657) -- Feature Request: Grid paging options at the top and the bottom of the grid. (fixed in [magento/graphql-ce#666](https://github.com/magento/graphql-ce/pull/666)) + * [#12612](https://github.com/magento/magento2/issues/12612) -- Array to String conversion error on checkout page when changin country - how to debug (fixed in [magento/magento2#22558](https://github.com/magento/magento2/pull/22558)) + * [#22556](https://github.com/magento/magento2/issues/22556) -- VatValidator::validate returns error if region in quoteAddress is not set (fixed in [magento/magento2#22558](https://github.com/magento/magento2/pull/22558)) + * [#20843](https://github.com/magento/magento2/issues/20843) -- Uncaught TypeError: panel.addClass is not a function when Swatches are disabled (fixed in [magento/magento2#22560](https://github.com/magento/magento2/pull/22560)) + * [#22636](https://github.com/magento/magento2/issues/22636) -- arrow toggle not changing only showing to down It should be toggle as every where is working (fixed in [magento/magento2#22644](https://github.com/magento/magento2/pull/22644)) + * [#22640](https://github.com/magento/magento2/issues/22640) -- Add tax rule form checkbox design is not as per the magento admin panel checkbox design, It is showing default design (fixed in [magento/magento2#22655](https://github.com/magento/magento2/pull/22655)) + * [#20906](https://github.com/magento/magento2/issues/20906) -- Magento backend catalog "Cost" without currency symbol (fixed in [magento/magento2#22739](https://github.com/magento/magento2/pull/22739)) + * [#22771](https://github.com/magento/magento2/issues/22771) -- Magento 2.3.0 can't change text area field height admin form using Ui component (fixed in [magento/magento2#22779](https://github.com/magento/magento2/pull/22779)) + * [#22788](https://github.com/magento/magento2/issues/22788) -- New Shipment emails do not generate (fixed in [magento/magento2#22791](https://github.com/magento/magento2/pull/22791)) + * [#18651](https://github.com/magento/magento2/issues/18651) -- Tierprice can't save float percentage value (fixed in [magento/magento2#19584](https://github.com/magento/magento2/pull/19584)) + * [#21672](https://github.com/magento/magento2/issues/21672) -- Database Media Storage - Design Config fails to save transactional email logo correctly (fixed in [magento/magento2#21675](https://github.com/magento/magento2/pull/21675) and [magento/magento2#21674](https://github.com/magento/magento2/pull/21674)) + * [#22028](https://github.com/magento/magento2/issues/22028) -- Unable to update products via csv file, when products ids from file are from wide id range (fixed in [magento/magento2#22575](https://github.com/magento/magento2/pull/22575)) + * [#21558](https://github.com/magento/magento2/issues/21558) -- Navigation issue of review from product listing when click on review count (fixed in [magento/magento2#22794](https://github.com/magento/magento2/pull/22794)) + * [#22127](https://github.com/magento/magento2/issues/22127) -- Magento 2.3.0: getSize call on configurable collection leads to exception, if no product filters are applied (fixed in [magento/magento2#22186](https://github.com/magento/magento2/pull/22186)) + * [#22639](https://github.com/magento/magento2/issues/22639) -- Without select attribute click on add attribute it display all selected when add attribute again. (fixed in [magento/magento2#22724](https://github.com/magento/magento2/pull/22724)) + * [#22676](https://github.com/magento/magento2/issues/22676) -- Compare Products counter, and My Wish List counter vertical not aligned (fixed in [magento/magento2#22742](https://github.com/magento/magento2/pull/22742)) + * [#6659](https://github.com/magento/magento2/issues/6659) -- Disabled payment methods show in Customer Dashboard (fixed in [magento/magento2#22850](https://github.com/magento/magento2/pull/22850)) + * [#4628](https://github.com/magento/magento2/issues/4628) -- .lib-font-face mixin - Fixed font formats (fixed in [magento/magento2#22854](https://github.com/magento/magento2/pull/22854)) + * [#3795](https://github.com/magento/magento2/issues/3795) -- Validation messages missing from datepicker form elements (fixed in [magento/magento2#21397](https://github.com/magento/magento2/pull/21397)) + * [#22786](https://github.com/magento/magento2/issues/22786) -- The validation for UPS configurations triggers even if UPS is disabled for checkout (fixed in [magento/magento2#22787](https://github.com/magento/magento2/pull/22787)) + * [#22822](https://github.com/magento/magento2/issues/22822) -- [Shipping] The contact us link isn't showing on order tracking page (fixed in [magento/magento2#22823](https://github.com/magento/magento2/pull/22823)) + * [#21852](https://github.com/magento/magento2/issues/21852) -- Random Error while waiting for package deployed (fixed in [magento/magento2#22607](https://github.com/magento/magento2/pull/22607)) + * [#22563](https://github.com/magento/magento2/issues/22563) -- Parallelised execution of static content deploy is broken on 2.3-develop (fixed in [magento/magento2#22607](https://github.com/magento/magento2/pull/22607)) + * [#22736](https://github.com/magento/magento2/issues/22736) -- Cursor position not in right side of search keyword in search box when click on search again (Mobile issue) (fixed in [magento/magento2#22795](https://github.com/magento/magento2/pull/22795)) + * [#22875](https://github.com/magento/magento2/issues/22875) -- Billing Agreements page title need to be improved (fixed in [magento/magento2#22876](https://github.com/magento/magento2/pull/22876)) + * [#21214](https://github.com/magento/magento2/issues/21214) -- Luma theme Apply Discount Code section design improvement (fixed in [magento/magento2#21215](https://github.com/magento/magento2/pull/21215)) + * [#22143](https://github.com/magento/magento2/issues/22143) -- Varnish health check failing due to presence of id_prefix in env.php (fixed in [magento/magento2#22307](https://github.com/magento/magento2/pull/22307)) + * [#22317](https://github.com/magento/magento2/issues/22317) -- CodeSniffer should not mark correctly aligned DocBlock elements as code style violation. (fixed in [magento/magento2#22444](https://github.com/magento/magento2/pull/22444)) + * [#22396](https://github.com/magento/magento2/issues/22396) -- config:set fails with JSON values (fixed in [magento/magento2#22513](https://github.com/magento/magento2/pull/22513)) + * [#22506](https://github.com/magento/magento2/issues/22506) -- Search suggestion panel overlapping on advance reporting button (fixed in [magento/magento2#22520](https://github.com/magento/magento2/pull/22520)) + * [#22869](https://github.com/magento/magento2/issues/22869) -- REST: Updating a customer without store_id sets the store_id to default (fixed in [magento/magento2#22893](https://github.com/magento/magento2/pull/22893)) + * [#22924](https://github.com/magento/magento2/issues/22924) -- Store view label not in the middle of panel (fixed in [magento/magento2#22926](https://github.com/magento/magento2/pull/22926)) + * [#20186](https://github.com/magento/magento2/issues/20186) -- phpcs error on rule classes - must be of the type integer (fixed in [magento/magento2#22947](https://github.com/magento/magento2/pull/22947)) + * [#574](https://github.com/magento/magento2/issues/574) -- Maximum function nesting level of '100' reached (fixed in [magento/graphql-ce#694](https://github.com/magento/graphql-ce/pull/694)) + * [#686](https://github.com/magento/magento2/issues/686) -- Product save validation errors in the admin don't hide the overlay (fixed in [magento/graphql-ce#695](https://github.com/magento/graphql-ce/pull/695)) + * [#22380](https://github.com/magento/magento2/issues/22380) -- Checkout totals order in specific store (fixed in [magento/magento2#22387](https://github.com/magento/magento2/pull/22387)) + * [#18183](https://github.com/magento/magento2/issues/18183) -- Magento 2.2.6 coupon codes don't work anymore (fixed in [magento/magento2#22718](https://github.com/magento/magento2/pull/22718)) + * [#22899](https://github.com/magento/magento2/issues/22899) -- Incorrect return type at getListByCustomerId in PaymentTokenManagementInterface (fixed in [magento/magento2#22914](https://github.com/magento/magento2/pull/22914)) + * [#22686](https://github.com/magento/magento2/issues/22686) -- Shipment Create via API salesShipmentRepositoryV1 throw Fatal error in Admin Order -> Shipment -> View (fixed in [magento/magento2#22687](https://github.com/magento/magento2/pull/22687)) + * [#22767](https://github.com/magento/magento2/issues/22767) -- Not clear logic for loading CMS Pages with setStoreId function (fixed in [magento/magento2#22772](https://github.com/magento/magento2/pull/22772)) + * [#20788](https://github.com/magento/magento2/issues/20788) -- Listing page no equal spacing in product in list view (fixed in [magento/magento2#22931](https://github.com/magento/magento2/pull/22931)) + * [#23030](https://github.com/magento/magento2/issues/23030) -- Magento2 Swatch change Image does not slide to first Image (fixed in [magento/magento2#23033](https://github.com/magento/magento2/pull/23033)) + * [#23034](https://github.com/magento/magento2/issues/23034) -- Wrong behaviour of validation scroll (fixed in [magento/magento2#23035](https://github.com/magento/magento2/pull/23035)) + * [#12696](https://github.com/magento/magento2/issues/12696) -- Integration tests create stub modules in app/code (fixed in [magento/magento2#18459](https://github.com/magento/magento2/pull/18459)) + * [#13266](https://github.com/magento/magento2/issues/13266) -- Topmenu 'last' class not being set if the a parent is inactive (fixed in [magento/magento2#22071](https://github.com/magento/magento2/pull/22071)) + * [#22882](https://github.com/magento/magento2/issues/22882) -- Static content deploy - Don't shows error message, just stack trace (fixed in [magento/magento2#22884](https://github.com/magento/magento2/pull/22884)) + * [#23045](https://github.com/magento/magento2/issues/23045) -- Exceptions from data patches do not show root cause (fixed in [magento/magento2#23046](https://github.com/magento/magento2/pull/23046)) + * [#16446](https://github.com/magento/magento2/issues/16446) -- magento 2.2.2 text swatch switches product image even if attribute feature is disabled (fixed in [magento/magento2#19184](https://github.com/magento/magento2/pull/19184)) + * [#14492](https://github.com/magento/magento2/issues/14492) -- Creating Customer without password is directly confirmed (fixed in [magento/magento2#21394](https://github.com/magento/magento2/pull/21394)) + * [#21671](https://github.com/magento/magento2/issues/21671) -- Database Media Storage - Transaction emails logo not used when pub/media cleared (fixed in [magento/magento2#21674](https://github.com/magento/magento2/pull/21674)) + * [#22425](https://github.com/magento/magento2/issues/22425) -- wrong url redirect when edit product review from Customer view page (fixed in [magento/magento2#22426](https://github.com/magento/magento2/pull/22426)) + * [#22511](https://github.com/magento/magento2/issues/22511) -- Special From Date set to today's date when Use Default Date checked in Store scope (fixed in [magento/magento2#22521](https://github.com/magento/magento2/pull/22521)) + * [#23080](https://github.com/magento/magento2/issues/23080) -- Missing whitespace in mobile navigation for non-English websites (fixed in [magento/magento2#23081](https://github.com/magento/magento2/pull/23081)) + * [#19872](https://github.com/magento/magento2/issues/19872) -- Magento 2.3 category edit page "Select from gallery" button not working. (fixed in [magento/magento2#21131](https://github.com/magento/magento2/pull/21131)) + * [#22092](https://github.com/magento/magento2/issues/22092) -- Assigning Catalog Image from Gallery, then Saving Twice, Clears Image (fixed in [magento/magento2#21131](https://github.com/magento/magento2/pull/21131)) + * [#22087](https://github.com/magento/magento2/issues/22087) -- Products Ordered Report - Not grouped by product (fixed in [magento/magento2#22646](https://github.com/magento/magento2/pull/22646)) + * [#21546](https://github.com/magento/magento2/issues/21546) -- [2.3] Database Media Storage - New Product Images fail to be processed correctly (fixed in [magento/magento2#21605](https://github.com/magento/magento2/pull/21605)) + * [#21604](https://github.com/magento/magento2/issues/21604) -- Database Media Storage - Admin Product Edit page does not handle product images correctly in database storage mode (fixed in [magento/magento2#21605](https://github.com/magento/magento2/pull/21605)) + * [#4247](https://github.com/magento/magento2/issues/4247) -- getProductUrl does not allow to override the scope in backend context (fixed in [magento/magento2#21876](https://github.com/magento/magento2/pull/21876)) + * [#22940](https://github.com/magento/magento2/issues/22940) -- Reset feature does not clear the date (fixed in [magento/magento2#23007](https://github.com/magento/magento2/pull/23007)) + * [#23053](https://github.com/magento/magento2/issues/23053) -- Sendfriend works for products with visibility not visible individually (fixed in [magento/magento2#23118](https://github.com/magento/magento2/pull/23118)) + * [#675](https://github.com/magento/magento2/issues/675) -- Textarea element cols and rows (fixed in [magento/graphql-ce#677](https://github.com/magento/graphql-ce/pull/677)) + * [#682](https://github.com/magento/magento2/issues/682) -- \Magento\Framework\Pricing\PriceCurrencyInterface depends on Magento application code (fixed in [magento/graphql-ce#700](https://github.com/magento/graphql-ce/pull/700)) + * [#681](https://github.com/magento/magento2/issues/681) -- Magento\Framework\Xml\Parser class issues (fixed in [magento/graphql-ce#711](https://github.com/magento/graphql-ce/pull/711)) + * [#22484](https://github.com/magento/magento2/issues/22484) -- Customer address States are duplicated in backend (fixed in [magento/magento2#22637](https://github.com/magento/magento2/pull/22637)) + * [#23138](https://github.com/magento/magento2/issues/23138) -- Magento_Theme. Incorrect configuration file location (fixed in [magento/magento2#23140](https://github.com/magento/magento2/pull/23140)) + * [#22004](https://github.com/magento/magento2/issues/22004) -- ce231 - can't update attribute for all product (fixed in [magento/magento2#22704](https://github.com/magento/magento2/pull/22704)) + * [#22870](https://github.com/magento/magento2/issues/22870) -- ProductRepository fails to update an existing product with a changed SKU (fixed in [magento/magento2#22933](https://github.com/magento/magento2/pull/22933)) + * [#22808](https://github.com/magento/magento2/issues/22808) -- php bin/magento catalog:image:resize error if image is missing (fixed in [magento/magento2#23005](https://github.com/magento/magento2/pull/23005)) + * [#674](https://github.com/magento/magento2/issues/674) -- Widgets in content pages. (fixed in [magento/graphql-ce#709](https://github.com/magento/graphql-ce/pull/709)) + * [#683](https://github.com/magento/magento2/issues/683) -- CMS Router not routing correctly (fixed in [magento/graphql-ce#717](https://github.com/magento/graphql-ce/pull/717)) + * [#9113](https://github.com/magento/magento2/issues/9113) -- [Bug or Feature?] url_path attribute value is not populated for any product (fixed in [magento/graphql-ce#721](https://github.com/magento/graphql-ce/pull/721)) + * [#18337](https://github.com/magento/magento2/issues/18337) -- #search input is missing required attribute aria-expanded. (fixed in [magento/magento2#22942](https://github.com/magento/magento2/pull/22942)) + * [#23213](https://github.com/magento/magento2/issues/23213) -- Static content deploy showing percentage(%) two times in progress bar (fixed in [magento/magento2#23216](https://github.com/magento/magento2/pull/23216)) + * [#23238](https://github.com/magento/magento2/issues/23238) -- Apply coupon button act like remove coupon while create new order from admin (fixed in [magento/magento2#23250](https://github.com/magento/magento2/pull/23250)) + * [#4788](https://github.com/magento/magento2/issues/4788) -- Wrong sitemap product url (fixed in [magento/magento2#23129](https://github.com/magento/magento2/pull/23129)) + * [#22934](https://github.com/magento/magento2/issues/22934) -- Incorrect work of "Use Categories Path for Product URLs" in sitemap generation. (fixed in [magento/magento2#23129](https://github.com/magento/magento2/pull/23129)) + * [#23266](https://github.com/magento/magento2/issues/23266) -- Cannot filter admin user by ID (fixed in [magento/magento2#23267](https://github.com/magento/magento2/pull/23267)) + * [#23285](https://github.com/magento/magento2/issues/23285) -- Credit memo submit button(refund) stays disable after validation fails & unable to enable button (fixed in [magento/magento2#23286](https://github.com/magento/magento2/pull/23286)) + * [#486](https://github.com/magento/magento2/issues/486) -- Take inspiration from other frameworks (fixed in [magento/graphql-ce#714](https://github.com/magento/graphql-ce/pull/714)) + * [#716](https://github.com/magento/magento2/issues/716) -- Wrong mimetype returned by getMimeType from Magento library (fixed in [magento/graphql-ce#723](https://github.com/magento/graphql-ce/pull/723)) + * [#687](https://github.com/magento/magento2/issues/687) -- Improvement Idea: /var/cache/mage--X (fixed in [magento/graphql-ce#749](https://github.com/magento/graphql-ce/pull/749)) + * [#20038](https://github.com/magento/magento2/issues/20038) -- loading icon disappearing before background process completes for braintree payment (Admin order) (fixed in [magento/magento2#22675](https://github.com/magento/magento2/pull/22675)) + * [#23074](https://github.com/magento/magento2/issues/23074) -- Magento 2.3.1 - URL rewrite rules are not creating for product after update url key (fixed in [magento/magento2#23309](https://github.com/magento/magento2/pull/23309)) + * [#622](https://github.com/magento/magento2/issues/622) -- FIX Magento Search Please! (fixed in [magento/graphql-ce#626](https://github.com/magento/graphql-ce/pull/626)) + * [#732](https://github.com/magento/magento2/issues/732) -- Inconsistency between Select and Multiselect form elements. (fixed in [magento/graphql-ce#734](https://github.com/magento/graphql-ce/pull/734)) + * [#13227](https://github.com/magento/magento2/issues/13227) -- Knockout Recently Viewed contains wrong product url (with category path), also not correct url <meta property="og:url"> on product view page (fixed in [magento/magento2#22650](https://github.com/magento/magento2/pull/22650)) + * [#22638](https://github.com/magento/magento2/issues/22638) -- Asterisk(*) sign position does not consistent in admin (fixed in [magento/magento2#22800](https://github.com/magento/magento2/pull/22800)) + * [#22266](https://github.com/magento/magento2/issues/22266) -- Product Alert after login shows 404 page (fixed in [magento/magento2#23218](https://github.com/magento/magento2/pull/23218)) + * [#23230](https://github.com/magento/magento2/issues/23230) -- Sticky header floating under top when there is no buttons in the toolbar (fixed in [magento/magento2#23247](https://github.com/magento/magento2/pull/23247)) + * [#23333](https://github.com/magento/magento2/issues/23333) -- Incorrect payment method translation in order emails (fixed in [magento/magento2#23338](https://github.com/magento/magento2/pull/23338)) + * [#23346](https://github.com/magento/magento2/issues/23346) -- 'Test Connection' button is over-spanned (fixed in [magento/magento2#23367](https://github.com/magento/magento2/pull/23367)) + * [#21380](https://github.com/magento/magento2/issues/21380) -- Cron schedule is being duplicated (fixed in [magento/magento2#23312](https://github.com/magento/magento2/pull/23312)) + * [#21136](https://github.com/magento/magento2/issues/21136) -- Magento installation via metapackage: checkExtensions fails (fixed in [magento/magento2#22116](https://github.com/magento/magento2/pull/22116)) + * [#23233](https://github.com/magento/magento2/issues/23233) -- Alert widget doesn't trigger always method on showing the message (fixed in [magento/magento2#23234](https://github.com/magento/magento2/pull/23234)) + * [#21974](https://github.com/magento/magento2/issues/21974) -- Changes for PayPal affect core config fields with tooltip (fixed in [magento/magento2#23393](https://github.com/magento/magento2/pull/23393)) + * [#23377](https://github.com/magento/magento2/issues/23377) -- Mini cart loader not working first time magento2 (fixed in [magento/magento2#23394](https://github.com/magento/magento2/pull/23394)) + * [#22998](https://github.com/magento/magento2/issues/22998) -- POST on /orders fails when properties in the body are out of sequence (fixed in [magento/magento2#23048](https://github.com/magento/magento2/pull/23048)) + * [#23522](https://github.com/magento/magento2/issues/23522) -- UPS shipping booking and label generation gives error when shipper's street given more than 35 chars (fixed in [magento/magento2#23523](https://github.com/magento/magento2/pull/23523)) + * [#8298](https://github.com/magento/magento2/issues/8298) -- Mobile Menu Behavior at Incorrect Breakpoint (fixed in [magento/magento2#23528](https://github.com/magento/magento2/pull/23528)) + * [#22103](https://github.com/magento/magento2/issues/22103) -- Character Encoding in Plain Text Emails Fails since 2.2.8/2.3.0 due to emails no longer being sent as MIME (fixed in [magento/magento2#23535](https://github.com/magento/magento2/pull/23535)) + * [#23199](https://github.com/magento/magento2/issues/23199) -- NO sender in email header for magento 2 sales order and password change emails to customer (fixed in [magento/magento2#23535](https://github.com/magento/magento2/pull/23535)) + * [#23538](https://github.com/magento/magento2/issues/23538) -- wrong validation happen for max-words validation class (fixed in [magento/magento2#23541](https://github.com/magento/magento2/pull/23541)) + * [#21126](https://github.com/magento/magento2/issues/21126) -- Backend Import behavior design break (fixed in [magento/magento2#21128](https://github.com/magento/magento2/pull/21128)) + * [#23471](https://github.com/magento/magento2/issues/23471) -- Tooltip missing at store view lable in Cms page and Cms block (fixed in [magento/magento2#23474](https://github.com/magento/magento2/pull/23474)) + * [#23466](https://github.com/magento/magento2/issues/23466) -- Cart empty after update qty with -1 and change address. (fixed in [magento/magento2#23477](https://github.com/magento/magento2/pull/23477)) + * [#23467](https://github.com/magento/magento2/issues/23467) -- Phone and Zip not update if customer have no saved address (fixed in [magento/magento2#23494](https://github.com/magento/magento2/pull/23494)) + * [#23222](https://github.com/magento/magento2/issues/23222) -- setup:upgrade should return failure when app:config:import failed (fixed in [magento/magento2#23310](https://github.com/magento/magento2/pull/23310)) + * [#23354](https://github.com/magento/magento2/issues/23354) -- Data saving problem error showing when leave blank qty and update it (fixed in [magento/magento2#23360](https://github.com/magento/magento2/pull/23360)) + * [#23424](https://github.com/magento/magento2/issues/23424) -- Search by keyword didn't work properly with "0" value (fixed in [magento/magento2#23427](https://github.com/magento/magento2/pull/23427)) + * [#16234](https://github.com/magento/magento2/issues/16234) -- Unable to enter `+` character in widget content (fixed in [magento/magento2#23496](https://github.com/magento/magento2/pull/23496)) + * [#9798](https://github.com/magento/magento2/issues/9798) -- Problem adding attribute options to configurable product via REST Api (fixed in [magento/magento2#23529](https://github.com/magento/magento2/pull/23529)) + * [#6287](https://github.com/magento/magento2/issues/6287) -- Customer Admin Shopping Cart View Missing (fixed in [magento/magento2#20918](https://github.com/magento/magento2/pull/20918)) + * [#8258](https://github.com/magento/magento2/issues/8258) -- M2.1.3 : Form validation with identical field names does not work as expected. (fixed in [magento/magento2#15383](https://github.com/magento/magento2/pull/15383)) + * [#13561](https://github.com/magento/magento2/issues/13561) -- Magento 2 and SSL connection to MySQL (fixed in [magento/magento2#18075](https://github.com/magento/magento2/pull/18075)) + * [#22545](https://github.com/magento/magento2/issues/22545) -- Status downloadable product stays pending after succesfull payment (fixed in [magento/magento2#22658](https://github.com/magento/magento2/pull/22658)) + * [#23383](https://github.com/magento/magento2/issues/23383) -- Products which are not assigned to any store are automatically being force-assigned a store ID after being saved (fixed in [magento/magento2#23500](https://github.com/magento/magento2/pull/23500)) + * [#22950](https://github.com/magento/magento2/issues/22950) -- Spacing issue for Gift message section in my account (fixed in [magento/magento2#23226](https://github.com/magento/magento2/pull/23226)) + * [#23606](https://github.com/magento/magento2/issues/23606) -- Default value for report filters might result in errors (fixed in [magento/magento2#23607](https://github.com/magento/magento2/pull/23607)) + * [#736](https://github.com/magento/magento2/issues/736) -- Access to Zend Framework classes (fixed in [magento/graphql-ce#747](https://github.com/magento/graphql-ce/pull/747)) + * [#739](https://github.com/magento/magento2/issues/739) -- Command line install script no longer exists. (fixed in [magento/graphql-ce#753](https://github.com/magento/graphql-ce/pull/753)) + * [#23435](https://github.com/magento/magento2/issues/23435) -- Catalog Products Filter in 2.3.2 (fixed in [magento/magento2#23444](https://github.com/magento/magento2/pull/23444)) + * [#12817](https://github.com/magento/magento2/issues/12817) -- Coupon code with canceled order (fixed in [magento/magento2#20579](https://github.com/magento/magento2/pull/20579)) + * [#23386](https://github.com/magento/magento2/issues/23386) -- Copy Service does not works properly for Entities which extends Data Object and implements ExtensibleDataInterface (fixed in [magento/magento2#23387](https://github.com/magento/magento2/pull/23387)) + * [#23345](https://github.com/magento/magento2/issues/23345) -- Creditmemo getOrder() method loads order incorrectly (fixed in [magento/magento2#23358](https://github.com/magento/magento2/pull/23358)) + * [#22814](https://github.com/magento/magento2/issues/22814) -- Product stock alert - unsubscribe not working (fixed in [magento/magento2#23459](https://github.com/magento/magento2/pull/23459)) + * [#23594](https://github.com/magento/magento2/issues/23594) -- Database Media Storage : php bin/magento catalog:images:resize fails when image does not exist locally (fixed in [magento/magento2#23598](https://github.com/magento/magento2/pull/23598)) + * [#23595](https://github.com/magento/magento2/issues/23595) -- Database Media Storage : php bin/magento catalog:images:resize fails to generate cached images in database (fixed in [magento/magento2#23598](https://github.com/magento/magento2/pull/23598)) + * [#23596](https://github.com/magento/magento2/issues/23596) -- Database Media Storage : Add new product image, cached images not generated (fixed in [magento/magento2#23598](https://github.com/magento/magento2/pull/23598)) + * [#23643](https://github.com/magento/magento2/issues/23643) -- Mime parts of email are no more encoded with quoted printable (fixed in [magento/magento2#23649](https://github.com/magento/magento2/pull/23649)) + * [#23597](https://github.com/magento/magento2/issues/23597) -- Database Media Storage : Difficulty changing mode to database media storage due to poor "Use Default Value" checkbox behaviour (fixed in [magento/magento2#23710](https://github.com/magento/magento2/pull/23710)) + * [#23510](https://github.com/magento/magento2/issues/23510) -- Product customizable options of Area type render issue in Dashboard (fixed in [magento/magento2#23524](https://github.com/magento/magento2/pull/23524)) + * [#22890](https://github.com/magento/magento2/issues/22890) -- Disabled config can be overwritten via admin (fixed in [magento/magento2#22891](https://github.com/magento/magento2/pull/22891)) + * [#23054](https://github.com/magento/magento2/issues/23054) -- Cron job not running after crashed once (fixed in [magento/magento2#23125](https://github.com/magento/magento2/pull/23125)) + * [#23135](https://github.com/magento/magento2/issues/23135) -- Insert Variable popup missing template variables for new templates (fixed in [magento/magento2#23173](https://github.com/magento/magento2/pull/23173)) + * [#23211](https://github.com/magento/magento2/issues/23211) -- Zero Subtotal Checkout erroneously says the default value for "Automatically Invoice All Items" is "Yes" (fixed in [magento/magento2#23688](https://github.com/magento/magento2/pull/23688)) + * [#23624](https://github.com/magento/magento2/issues/23624) -- [Authorize.net accept.js] "Place Order" button not being disabled (fixed in [magento/magento2#23718](https://github.com/magento/magento2/pull/23718)) + * [#23717](https://github.com/magento/magento2/issues/23717) -- dependency injection fails for \Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface (fixed in [magento/magento2#23753](https://github.com/magento/magento2/pull/23753)) + * [#758](https://github.com/magento/magento2/issues/758) -- Coding standards: arrays (fixed in [magento/graphql-ce#759](https://github.com/magento/graphql-ce/pull/759)) + * [#14071](https://github.com/magento/magento2/issues/14071) -- Not able to change a position of last two related products in case of I've 20+ related products. (fixed in [magento/magento2#22984](https://github.com/magento/magento2/pull/22984)) + * [#22112](https://github.com/magento/magento2/issues/22112) -- Shipping address information is lost in billing step (fixed in [magento/magento2#23656](https://github.com/magento/magento2/pull/23656)) + * [#23654](https://github.com/magento/magento2/issues/23654) -- Frontend Label For Custom Order Status not Editable in Magento Admin in Single Store Mode (fixed in [magento/magento2#23681](https://github.com/magento/magento2/pull/23681)) + * [#23751](https://github.com/magento/magento2/issues/23751) -- Database Media Storage : PDF Logo file not database aware (fixed in [magento/magento2#23752](https://github.com/magento/magento2/pull/23752)) + * [#23678](https://github.com/magento/magento2/issues/23678) -- Can't see "Zero Subtotal Checkout" payment method settings if "Offline Payments" module is disabled (fixed in [magento/magento2#23679](https://github.com/magento/magento2/pull/23679)) + * [#23777](https://github.com/magento/magento2/issues/23777) -- "Discount Amount" field is validated after the page load without any action from user in Create New Catalog Rule form (fixed in [magento/magento2#23779](https://github.com/magento/magento2/pull/23779)) + * [#23789](https://github.com/magento/magento2/issues/23789) -- CommentLevelsSniff works incorrect with @magento_import statement. (fixed in [magento/magento2#23790](https://github.com/magento/magento2/pull/23790)) + * [#22702](https://github.com/magento/magento2/issues/22702) -- Toggle icon not working in create configuration Product creation Page (fixed in [magento/magento2#23803](https://github.com/magento/magento2/pull/23803)) + * [#167](https://github.com/magento/magento2/issues/167) -- Fatal error: Class 'Mage' not found (fixed in [magento/graphql-ce#351](https://github.com/magento/graphql-ce/pull/351)) + * [#438](https://github.com/magento/magento2/issues/438) -- [Feature request] Price slider (fixed in [magento/graphql-ce#699](https://github.com/magento/graphql-ce/pull/699)) + * [#702](https://github.com/magento/magento2/issues/702) -- Base table or view not found (fixed in [magento/graphql-ce#779](https://github.com/magento/graphql-ce/pull/779)) + * [#738](https://github.com/magento/magento2/issues/738) -- pub/setup missing in 0.1.0-alpha103 (fixed in [magento/graphql-ce#789](https://github.com/magento/graphql-ce/pull/789)) + * [#23405](https://github.com/magento/magento2/issues/23405) -- 2.3.2 installed and bin/magento setup:upgrade not working (fixed in [magento/magento2#23866](https://github.com/magento/magento2/pull/23866)) + * [#23900](https://github.com/magento/magento2/issues/23900) -- Report->Product->Downloads has wrong ACL (fixed in [magento/magento2#23901](https://github.com/magento/magento2/pull/23901)) + * [#23904](https://github.com/magento/magento2/issues/23904) -- No auto-focus after validation at "Create Configurations" button => User can not see the error message (fixed in [magento/magento2#23905](https://github.com/magento/magento2/pull/23905)) + * [#23916](https://github.com/magento/magento2/issues/23916) -- Missing Validation at some Payment Method Settings (fixed in [magento/magento2#23917](https://github.com/magento/magento2/pull/23917)) + * [#23932](https://github.com/magento/magento2/issues/23932) -- Decimal quantity is not displayed for wishlist items. (fixed in [magento/magento2#23933](https://github.com/magento/magento2/pull/23933)) +* GitHub pull requests: + * [magento/magento2#20135](https://github.com/magento/magento2/pull/20135) -- issue fixed #20124 Sort By label is hidden by Shop By Menu on listing… (by @cedarvinda) + * [magento/magento2#22020](https://github.com/magento/magento2/pull/22020) -- Non existing file, when adding image to gallery with move option. Fix for #21978 (by @dudzio12) + * [magento/magento2#22260](https://github.com/magento/magento2/pull/22260) -- Disabling "Display on Product Details Page" the button is shown anyway. (by @Nazar65) + * [magento/magento2#22287](https://github.com/magento/magento2/pull/22287) -- #222249 configurable product images wrong sorting fix (by @Wirson) + * [magento/magento2#22526](https://github.com/magento/magento2/pull/22526) -- code cleanup (http to https) (by @ravi-chandra3197) + * [magento/magento2#22532](https://github.com/magento/magento2/pull/22532) -- fixed issue 22527 wishlist and compare icon alignment (by @sanjaychouhan-webkul) + * [magento/magento2#22423](https://github.com/magento/magento2/pull/22423) -- Store view specific labels cut in left navigation menu #22406 (by @sudhanshu-bajaj) + * [magento/magento2#22561](https://github.com/magento/magento2/pull/22561) -- [Catalog|Eav] Revert change of PR magento/magento2#13302 not included into revert commit (by @Den4ik) + * [magento/magento2#22569](https://github.com/magento/magento2/pull/22569) -- 2.3 develop pr1 (by @abhinay111222) + * [magento/magento2#22594](https://github.com/magento/magento2/pull/22594) -- Fixed typo issue (by @AfreenScarlet) + * [magento/magento2#22599](https://github.com/magento/magento2/pull/22599) -- Correct spelling (by @ravi-chandra3197) + * [magento/magento2#22621](https://github.com/magento/magento2/pull/22621) -- Resolved Typo (by @UdgamN) + * [magento/magento2#19767](https://github.com/magento/magento2/pull/19767) -- Prevent display of token when save for later is not selected (by @pmclain) + * [magento/magento2#21744](https://github.com/magento/magento2/pull/21744) -- Custom option type select - Allow modify list of single selection option types (by @ihor-sviziev) + * [magento/magento2#21992](https://github.com/magento/magento2/pull/21992) -- #21473: Form element validation is not triggered when validation rules... (by @kisroman) + * [magento/magento2#22493](https://github.com/magento/magento2/pull/22493) -- Update credit-card-number-validator.js (by @justin-at-bounteous) + * [magento/magento2#22643](https://github.com/magento/magento2/pull/22643) -- Fixed typo issue and added missing header in customer sales order grid (by @vishal-7037) + * [magento/magento2#22656](https://github.com/magento/magento2/pull/22656) -- issues #22647 fixed, In customer account create page word not readable, should use '-' after break to new line In mobile view (by @cedarvinda) + * [magento/magento2#22720](https://github.com/magento/magento2/pull/22720) -- Fixed:#22395 (by @satyaprakashpatel) + * [magento/magento2#22558](https://github.com/magento/magento2/pull/22558) -- Additional condition in getRegion() method (by @Leone) + * [magento/magento2#22560](https://github.com/magento/magento2/pull/22560) -- Fix undefined methods 'addClass' and `removeClass` on a PrototypeJS Element (by @markvds) + * [magento/magento2#22606](https://github.com/magento/magento2/pull/22606) -- Fix Exception While Creating an Order in the Admin (by @justin-at-bounteous) + * [magento/magento2#22628](https://github.com/magento/magento2/pull/22628) -- Add a missting colon in the pdf page. (by @Hailong) + * [magento/magento2#22644](https://github.com/magento/magento2/pull/22644) -- Issue fixed #22636 arrow toggle not changing only showing to down It should be toggle as every where is working (by @cedarvinda) + * [magento/magento2#22664](https://github.com/magento/magento2/pull/22664) -- Fixed Typo Error (by @LuciferStrome) + * [magento/magento2#22729](https://github.com/magento/magento2/pull/22729) -- Fixed Typo Issue (by @jitendra-cedcoss) + * [magento/magento2#22734](https://github.com/magento/magento2/pull/22734) -- Ignores allure-results in git. (by @hostep) + * [magento/magento2#22758](https://github.com/magento/magento2/pull/22758) -- Correct spelling (by @ravi-chandra3197) + * [magento/magento2#22798](https://github.com/magento/magento2/pull/22798) -- Disable Travis builds - 2.3-develop (by @okorshenko) + * [magento/magento2#22126](https://github.com/magento/magento2/pull/22126) -- Remove unnecessary form on order success page (by @danielgoodwin97) + * [magento/magento2#22655](https://github.com/magento/magento2/pull/22655) -- Fixed Issue #22640 (by @Surabhi-Cedcoss) + * [magento/magento2#22657](https://github.com/magento/magento2/pull/22657) -- 404 not found form validation url when updating quantity in cart page (by @gulshanchitransh) + * [magento/magento2#22739](https://github.com/magento/magento2/pull/22739) -- Revert "Magento backend catalog cost without currency symbol" as Cost... (by @orlangur) + * [magento/magento2#22779](https://github.com/magento/magento2/pull/22779) -- #22771 Remove hardcoded height for admin textarea (by @serhiyzhovnir) + * [magento/magento2#22791](https://github.com/magento/magento2/pull/22791) -- Fixed issue #22788 (by @gauravagarwal1001) + * [magento/magento2#19584](https://github.com/magento/magento2/pull/19584) -- Tierprice can t save float percentage value 18651 (by @novikor) + * [magento/magento2#21675](https://github.com/magento/magento2/pull/21675) -- [2.3] Database Media Storage - Design Config Save functions to be Database Media Storage aware (by @gwharton) + * [magento/magento2#21917](https://github.com/magento/magento2/pull/21917) -- Prevent duplicate variation error during import of configurable products with numerical SKUs (by @alexander-aleman) + * [magento/magento2#22463](https://github.com/magento/magento2/pull/22463) -- Set timezone on DateTime object not in constructor (by @NathMorgan) + * [magento/magento2#22575](https://github.com/magento/magento2/pull/22575) -- Fix for update products via csv file (fix for 22028) (by @mtwegrzycki) + * [magento/magento2#22794](https://github.com/magento/magento2/pull/22794) -- fixed issue #21558 - Navigation issue of review from product listing (by @sanjaychouhan-webkul) + * [magento/magento2#22844](https://github.com/magento/magento2/pull/22844) -- Allow to specify a field to be checked in the response (by @diazwatson) + * [magento/magento2#22186](https://github.com/magento/magento2/pull/22186) -- 22127: check that products is set (by @davidverholen) + * [magento/magento2#22418](https://github.com/magento/magento2/pull/22418) -- Patch the prototype pollution vulnerability in jQuery < 3.4.0 (CVE-2019-11358) (by @DanielRuf) + * [magento/magento2#22724](https://github.com/magento/magento2/pull/22724) -- Fixed issue #22639: Without select attribute click on add attribute it display all selected when add attribute again. (by @maheshWebkul721) + * [magento/magento2#22742](https://github.com/magento/magento2/pull/22742) -- issue #22676 fixed - Compare Products counter, and My Wish List count... (by @sanjaychouhan-webkul) + * [magento/magento2#22850](https://github.com/magento/magento2/pull/22850) -- only show customer account sections if payment method is active (by @torhoehn) + * [magento/magento2#22854](https://github.com/magento/magento2/pull/22854) -- fix #4628 font-face mixin add fromat option (by @Karlasa) + * [magento/magento2#22868](https://github.com/magento/magento2/pull/22868) -- fix date calculation for report's years interval (by @polkan-msk) + * [magento/magento2#21397](https://github.com/magento/magento2/pull/21397) -- Fixed Validation messages missing from datepicker form elements (by @ravi-chandra3197) + * [magento/magento2#22694](https://github.com/magento/magento2/pull/22694) -- Fixed Issue #20234 (by @surbhi-ranosys) + * [magento/magento2#22787](https://github.com/magento/magento2/pull/22787) -- #22786 Add dependency for UPS required fields to avoid validation for these fields if UPS Shipping is not active (by @serhiyzhovnir) + * [magento/magento2#22823](https://github.com/magento/magento2/pull/22823) -- [Shipping] Adjusting the Contact Us Xpath (by @eduard13) + * [magento/magento2#22830](https://github.com/magento/magento2/pull/22830) -- Removing "if" block and making code more legible (by @matheusgontijo) + * [magento/magento2#22839](https://github.com/magento/magento2/pull/22839) -- FIX: Add missing Stories and Severity to Test cases (by @lbajsarowicz) + * [magento/magento2#22858](https://github.com/magento/magento2/pull/22858) -- Grammatical mistake in the comments (by @sudhanshu-bajaj) + * [magento/magento2#22889](https://github.com/magento/magento2/pull/22889) -- Replace hardcoded CarierCode from createShippingMethod() (by @wigman) + * [magento/magento2#22922](https://github.com/magento/magento2/pull/22922) -- [BUGFIX] Set correct cron instance for catalog_product_frontend_actio... (by @lewisvoncken) + * [magento/magento2#22607](https://github.com/magento/magento2/pull/22607) -- Implement Better Error Handling and Fix Waits on Null PIDs in Parallel SCD Execution (by @davidalger) + * [magento/magento2#22795](https://github.com/magento/magento2/pull/22795) -- fixed issue #22736 - Cursor position not in right side of search keyword in mobile (by @sanjaychouhan-webkul) + * [magento/magento2#22876](https://github.com/magento/magento2/pull/22876) -- Fixed issue #22875 Billing Agreements page title need to be improved (by @amitvishvakarma) + * [magento/magento2#21215](https://github.com/magento/magento2/pull/21215) -- fixed-Discount-Code-improvement-21214 (by @abrarpathan19) + * [magento/magento2#22307](https://github.com/magento/magento2/pull/22307) -- Varnish health check failing due to presence of id_prefix in env.php (by @Nazar65) + * [magento/magento2#22444](https://github.com/magento/magento2/pull/22444) -- magento/magento2#22317: PR#22321 fix. (by @p-bystritsky) + * [magento/magento2#22513](https://github.com/magento/magento2/pull/22513) -- Fixed #22396 config:set fails with JSON values (by @shikhamis11) + * [magento/magento2#22520](https://github.com/magento/magento2/pull/22520) -- Fixed #22506: Search suggestion panel overlapping on advance reporting button (by @webkul-ajaysaini) + * [magento/magento2#22760](https://github.com/magento/magento2/pull/22760) -- [Forwardport] Magento Catalog - fix custom option type text price conversion for mu... (by @ihor-sviziev) + * [magento/magento2#22893](https://github.com/magento/magento2/pull/22893) -- #22869 - defaulting customer storeId fix (by @Wirson) + * [magento/magento2#22926](https://github.com/magento/magento2/pull/22926) -- Store view label not in the middle of panel (by @speedy008) + * [magento/magento2#22947](https://github.com/magento/magento2/pull/22947) -- phpcs error on rule classes - must be of the type integer (by @Nazar65) + * [magento/magento2#22951](https://github.com/magento/magento2/pull/22951) -- Update the contributing.md to match the new beginners guide (by @dmanners) + * [magento/magento2#22387](https://github.com/magento/magento2/pull/22387) -- Checkout totals order in specific store (by @krnshah) + * [magento/magento2#22718](https://github.com/magento/magento2/pull/22718) -- Resolved issue coupon codes don't work anymore #18183 (by @this-adarsh) + * [magento/magento2#22914](https://github.com/magento/magento2/pull/22914) -- #22899 Fix the issue with Incorrect return type at getListByCustomerId in PaymentTokenManagementInterface (by @serhiyzhovnir) + * [magento/magento2#22687](https://github.com/magento/magento2/pull/22687) -- #22686 Shipment view fixed for Fatal error. (by @milindsingh) + * [magento/magento2#22772](https://github.com/magento/magento2/pull/22772) -- Fixed issue #22767: Not clear logic for loading CMS Pages with setStoreId function (by @maheshWebkul721) + * [magento/magento2#22931](https://github.com/magento/magento2/pull/22931) -- [Fixed-20788: Listing page no equal spacing in product in list view] (by @hitesh-wagento) + * [magento/magento2#22965](https://github.com/magento/magento2/pull/22965) -- Simplify if else catalog search full text data provider (by @sankalpshekhar) + * [magento/magento2#23011](https://github.com/magento/magento2/pull/23011) -- Fix typehint (by @amenk) + * [magento/magento2#22920](https://github.com/magento/magento2/pull/22920) -- Mview Indexers getList should return integer values of id's and not strings (by @mhodge13) + * [magento/magento2#23020](https://github.com/magento/magento2/pull/23020) -- Remove direct use of object manager (by @AnshuMishra17) + * [magento/magento2#23033](https://github.com/magento/magento2/pull/23033) -- Issue fix #23030: Swatch change Image does not slide to first Image (by @milindsingh) + * [magento/magento2#23035](https://github.com/magento/magento2/pull/23035) -- [Validator] Fix wrong behaviour of validation scroll (by @Den4ik) + * [magento/magento2#18459](https://github.com/magento/magento2/pull/18459) -- 12696 Delete all test modules after integration tests (by @avstudnitz) + * [magento/magento2#19897](https://github.com/magento/magento2/pull/19897) -- Re-enable PriceBox block caching (by @brucemead) + * [magento/magento2#21200](https://github.com/magento/magento2/pull/21200) -- [FEATURE] Don't load product collection in review observer (by @Den4ik) + * [magento/magento2#22071](https://github.com/magento/magento2/pull/22071) -- Make sure 'last' class is set on top menu (by @arnoudhgz) + * [magento/magento2#22821](https://github.com/magento/magento2/pull/22821) -- Customer Account Forgot Password page title fix (by @textarea) + * [magento/magento2#22884](https://github.com/magento/magento2/pull/22884) -- Show exception message during SCD failure (by @ihor-sviziev) + * [magento/magento2#22989](https://github.com/magento/magento2/pull/22989) -- Properly transliterate German Umlauts (by @amenk) + * [magento/magento2#23036](https://github.com/magento/magento2/pull/23036) -- [Framework] Reassign fields variable after converting to array (by @Den4ik) + * [magento/magento2#23040](https://github.com/magento/magento2/pull/23040) -- Don't create a new account-nav block - use existing instead. (by @vovayatsyuk) + * [magento/magento2#23046](https://github.com/magento/magento2/pull/23046) -- Add more descriptive exception when data patch fails to apply. (by @ashsmith) + * [magento/magento2#23067](https://github.com/magento/magento2/pull/23067) -- Create Security.md file to show on GitHub Security/Policy page (by @piotrekkaminski) + * [magento/magento2#14384](https://github.com/magento/magento2/pull/14384) -- Don't throw shipping method exception when creating quote with only virtual products in API (by @Maikel-Koek) + * [magento/magento2#19184](https://github.com/magento/magento2/pull/19184) -- Fixed magento text swatch switches product image even if attribute feature is disabled #16446 (by @ravi-chandra3197) + * [magento/magento2#21394](https://github.com/magento/magento2/pull/21394) -- [2.3]creating customer without password is directly confirmed 14492 (by @novikor) + * [magento/magento2#21674](https://github.com/magento/magento2/pull/21674) -- [2.3] Database Media Storage - Transactional Emails will now extract image from database in Database Media Storage mode (by @gwharton) + * [magento/magento2#22336](https://github.com/magento/magento2/pull/22336) -- fix clean_cache plugin flush mode (by @thomas-kl1) + * [magento/magento2#22426](https://github.com/magento/magento2/pull/22426) -- Fixed wrong url redirect when edit product review from Customer view page (by @ravi-chandra3197) + * [magento/magento2#22521](https://github.com/magento/magento2/pull/22521) -- Fixed 22511 (by @maheshWebkul721) + * [magento/magento2#22626](https://github.com/magento/magento2/pull/22626) -- resolved typo error (by @nehaguptacedcoss) + * [magento/magento2#22834](https://github.com/magento/magento2/pull/22834) -- #16445 - getRegionHtmlSelect does not have configuration - resolved (by @nikunjskd20) + * [magento/magento2#22937](https://github.com/magento/magento2/pull/22937) -- Mark Elasticsearch 6 support for synonyms (by @aapokiiso) + * [magento/magento2#23081](https://github.com/magento/magento2/pull/23081) -- Fix missing whitespace in mobile navigation for non-English websites (by @alexeya-ven) + * [magento/magento2#21131](https://github.com/magento/magento2/pull/21131) -- Fix Issue #19872 - checking if image is in media directory (by @Bartlomiejsz) + * [magento/magento2#22341](https://github.com/magento/magento2/pull/22341) -- Apply coupoun and scroll top to check. applied successfully or not (by @krnshah) + * [magento/magento2#22646](https://github.com/magento/magento2/pull/22646) -- Fixed Issue #22087 (by @Surabhi-Cedcoss) + * [magento/magento2#23025](https://github.com/magento/magento2/pull/23025) -- Re-enable XML as request and response types within the SwaggerUI (by @sweikenb) + * [magento/magento2#20848](https://github.com/magento/magento2/pull/20848) -- Partial docs fixes in Newsletter module (by @SikailoISM) + * [magento/magento2#21605](https://github.com/magento/magento2/pull/21605) -- [2.3] Database Media Storage - Admin Product Edit Page handles recreates images correctly when pub/media/catalog is cleared. (by @gwharton) + * [magento/magento2#21876](https://github.com/magento/magento2/pull/21876) -- $product->getUrlInStore() does not allow to override the scope in backend context (by @Nazar65) + * [magento/magento2#23007](https://github.com/magento/magento2/pull/23007) -- [Fixed] Reset feature does not clear the date (by @niravkrish) + * [magento/magento2#23118](https://github.com/magento/magento2/pull/23118) -- #23053 : sendfriend verifies product visibility instead of status (by @Wirson) + * [magento/magento2#18748](https://github.com/magento/magento2/pull/18748) -- Add a module manager to the Magento Framework API (by @navarr) + * [magento/magento2#22637](https://github.com/magento/magento2/pull/22637) -- Fixed #22484 Customer address States are duplicated in backend (by @shikhamis11) + * [magento/magento2#23140](https://github.com/magento/magento2/pull/23140) -- magento/magento2#23138: Magento_Theme. Incorrect configuration file location (by @atwixfirster) + * [magento/magento2#23179](https://github.com/magento/magento2/pull/23179) -- Fix for translation function (by @kkdg) + * [magento/magento2#22704](https://github.com/magento/magento2/pull/22704) -- Fixed #22004 can't update attribute for all product (by @shikhamis11) + * [magento/magento2#22933](https://github.com/magento/magento2/pull/22933) -- magento/magento2#22870: ProductRepository fails to update an existing product with a changed SKU. (by @p-bystritsky) + * [magento/magento2#23005](https://github.com/magento/magento2/pull/23005) -- Improve command catalog:images:resize (by @tdgroot) + * [magento/magento2#22942](https://github.com/magento/magento2/pull/22942) -- Fixed issue #18337 (by @geet07) + * [magento/magento2#23216](https://github.com/magento/magento2/pull/23216) -- Fixed #23213 Static content deploy showing percentage symbol two times in progress bar (by @amitvishvakarma) + * [magento/magento2#23244](https://github.com/magento/magento2/pull/23244) -- Update CONTRIBUTING.md (by @diazwatson) + * [magento/magento2#23248](https://github.com/magento/magento2/pull/23248) -- fix tooltip toggle selector typo (by @Karlasa) + * [magento/magento2#23250](https://github.com/magento/magento2/pull/23250) -- Fixed #23238 Apply button act like remove button while create new order from admin (by @gauravagarwal1001) + * [magento/magento2#22211](https://github.com/magento/magento2/pull/22211) -- Show converted value for validateForRefund error message (by @kassner) + * [magento/magento2#23129](https://github.com/magento/magento2/pull/23129) -- #22934 Improved sitemap product generation logic (by @sergiy-v) + * [magento/magento2#23201](https://github.com/magento/magento2/pull/23201) -- MFTF: Use AdminLoginActionGroup for AdminLoginTest - easiest use of ActionGroup (by @lbajsarowicz) + * [magento/magento2#23100](https://github.com/magento/magento2/pull/23100) -- Resolve issue with improper EAV attribute join statement (by @udovicic) + * [magento/magento2#23267](https://github.com/magento/magento2/pull/23267) -- Add filter index for ID column in adminhtml user grid (by @JeroenVanLeusden) + * [magento/magento2#23286](https://github.com/magento/magento2/pull/23286) -- Fixed Credit memo submit button(refund) stays disable after validation fails & unable to enable button issue. (by @nishantjariwala) + * [magento/magento2#23292](https://github.com/magento/magento2/pull/23292) -- revert Properly transliterate German Umlauts (by @Nazar65) + * [magento/magento2#23307](https://github.com/magento/magento2/pull/23307) -- [Ui] Allow to define listing configuration via ui component xml (by @Den4ik) + * [magento/magento2#23335](https://github.com/magento/magento2/pull/23335) -- Correct spelling (by @ravi-chandra3197) + * [magento/magento2#22675](https://github.com/magento/magento2/pull/22675) -- Fixed #20038 loading icon disappearing before background process completes for Braintree payment in Admin (by @kunal-rtpl) + * [magento/magento2#23174](https://github.com/magento/magento2/pull/23174) -- Move Quote related Plugins to correct module (by @sankalpshekhar) + * [magento/magento2#23309](https://github.com/magento/magento2/pull/23309) -- magento/magento2#23074: update correct product URL rewrites after changing category url key (by @sta1r) + * [magento/magento2#23347](https://github.com/magento/magento2/pull/23347) -- Fixes incorrect file reference in a comment in a .htaccess file. (by @hostep) + * [magento/magento2#22650](https://github.com/magento/magento2/pull/22650) -- Fixes issue #13227 (by @atishgoswami) + * [magento/magento2#22800](https://github.com/magento/magento2/pull/22800) -- fixed issue #22638 - Asterisk(*) sign position does not consistent in admin (by @sanjaychouhan-webkul) + * [magento/magento2#22910](https://github.com/magento/magento2/pull/22910) -- Do an empty check instead of isset check on image removed (by @arnoudhgz) + * [magento/magento2#23218](https://github.com/magento/magento2/pull/23218) -- Fixed #22266: 404 message for product alerts when not logged in (by @ArjenMiedema) + * [magento/magento2#23247](https://github.com/magento/magento2/pull/23247) -- Fixed 23230 : Sticky header floating under top when there is no buttons in the toolbar (by @konarshankar07) + * [magento/magento2#23338](https://github.com/magento/magento2/pull/23338) -- Fix issue with incorrect payment translation in sales emails (by @alexeya-ven) + * [magento/magento2#23366](https://github.com/magento/magento2/pull/23366) -- Correct spelling (by @ravi-chandra3197) + * [magento/magento2#23367](https://github.com/magento/magento2/pull/23367) -- #23346: 'Test Connection' button is over-spanned (by @konarshankar07) + * [magento/magento2#22671](https://github.com/magento/magento2/pull/22671) -- Change exportButton option cvs (by @ajeetsinghcedcoss) + * [magento/magento2#23240](https://github.com/magento/magento2/pull/23240) -- Refactor: Improve mview code readability (by @lbajsarowicz) + * [magento/magento2#23280](https://github.com/magento/magento2/pull/23280) -- Ensure page is loaded after order click actions (by @fooman) + * [magento/magento2#23306](https://github.com/magento/magento2/pull/23306) -- FS/23038 Decimal qty with Increment is with specific values are not adding in cart (by @sertlab) + * [magento/magento2#23312](https://github.com/magento/magento2/pull/23312) -- Added function to check against running/pending/successful cron tasks (by @chickenland) + * [magento/magento2#22116](https://github.com/magento/magento2/pull/22116) -- Fix magento root package identification for metapackage installation (by @oleksii-lisovyi) + * [magento/magento2#23234](https://github.com/magento/magento2/pull/23234) -- [Ui] Calling the always action on opening and closing the modal. (by @eduard13) + * [magento/magento2#23353](https://github.com/magento/magento2/pull/23353) -- Get review entity id by code instead hard-coded. (by @DaniloEmpire) + * [magento/magento2#23393](https://github.com/magento/magento2/pull/23393) -- Fixed issue #21974 (by @geet07) + * [magento/magento2#23394](https://github.com/magento/magento2/pull/23394) -- Fixed issue #23377 (by @geet07) + * [magento/magento2#23403](https://github.com/magento/magento2/pull/23403) -- Remove rogue closing tag from store-switcher template (by @sta1r) + * [magento/magento2#22987](https://github.com/magento/magento2/pull/22987) -- Fixed apply discount coupons for bundle product (by @NikolasSumrak) + * [magento/magento2#23048](https://github.com/magento/magento2/pull/23048) -- #22998 : failing order creation with api when no address email is provided (by @Wirson) + * [magento/magento2#23390](https://github.com/magento/magento2/pull/23390) -- Changed logic so that _scrollToTopIfVisible is called only if element is in viewport. Previously it was called only when the element was outside it. (by @oskarolaussen) + * [magento/magento2#23425](https://github.com/magento/magento2/pull/23425) -- The best practices for SEO meta sequence. (by @vaseemishak) + * [magento/magento2#23523](https://github.com/magento/magento2/pull/23523) -- Issue #23522 UPS shipping booking and label generation gives error when shipper's street given more than 35 chars (by @ankurvr) + * [magento/magento2#23528](https://github.com/magento/magento2/pull/23528) -- move breakpoint by -1px to make nav work correctly at viewport 768 (by @bobemoe) + * [magento/magento2#23532](https://github.com/magento/magento2/pull/23532) -- Correct array type hints in Visibility model (by @pmclain) + * [magento/magento2#23535](https://github.com/magento/magento2/pull/23535) -- [2.3] Plain Text Emails are now sent with correct MIME Encoding (by @gwharton) + * [magento/magento2#23541](https://github.com/magento/magento2/pull/23541) -- fix validation class for max-words (by @sunilit42) + * [magento/magento2#21128](https://github.com/magento/magento2/pull/21128) -- Fix issue 21126 : Import design break issue resolved (by @speedy008) + * [magento/magento2#22213](https://github.com/magento/magento2/pull/22213) -- Date column ui component locale date format (by @Karlasa) + * [magento/magento2#23457](https://github.com/magento/magento2/pull/23457) -- Update CartTotalRepository.php (by @UlyanaKiklevich) + * [magento/magento2#23474](https://github.com/magento/magento2/pull/23474) -- Fixed tooltip missing at store view lable in Cms page and Cms block (by @dipeshrangani) + * [magento/magento2#23477](https://github.com/magento/magento2/pull/23477) -- Added quantity validation on Shipping Multiple Address Page (by @nirmalraval18) + * [magento/magento2#23494](https://github.com/magento/magento2/pull/23494) -- Removed editor from phone and zipcode (by @kazim-krish) + * [magento/magento2#23310](https://github.com/magento/magento2/pull/23310) -- magento/magento-2#23222: setup:upgrade should return failure when app… (by @ProcessEight) + * [magento/magento2#23360](https://github.com/magento/magento2/pull/23360) -- #23354 : Data saving problem error showing when leave blank qty and update it (by @konarshankar07) + * [magento/magento2#23427](https://github.com/magento/magento2/pull/23427) -- 23424: fixed search with 0 (by @jeysmook) + * [magento/magento2#23496](https://github.com/magento/magento2/pull/23496) -- Resolved + character issue in custom widget (by @sarfarazbheda) + * [magento/magento2#23529](https://github.com/magento/magento2/pull/23529) -- Feature/9798 updating configurable product options based on produc id and sku (by @lpouwelse) + * [magento/magento2#20918](https://github.com/magento/magento2/pull/20918) -- Enabled 'Shopping Cart' tab for customer edit interface in admin (by @rav-redchamps) + * [magento/magento2#22624](https://github.com/magento/magento2/pull/22624) -- Resolve Typo (by @prashantsharmacedcoss) + * [magento/magento2#15383](https://github.com/magento/magento2/pull/15383) -- issue fixed #8258 - assign indices for all array inputs so that validation works properly (by @jayankaghosh) + * [magento/magento2#18075](https://github.com/magento/magento2/pull/18075) -- Feature/issue 13561 2.3 (by @bnymn) + * [magento/magento2#22658](https://github.com/magento/magento2/pull/22658) -- Fixed #22545 Status downloadable product stays pending after succesfu… (by @shikhamis11) + * [magento/magento2#23500](https://github.com/magento/magento2/pull/23500) -- Fixed issue #23383 (by @manishgoswamij) + * [magento/magento2#23226](https://github.com/magento/magento2/pull/23226) -- Spacing issue for Gift message section in my account (by @amjadm61) + * [magento/magento2#23272](https://github.com/magento/magento2/pull/23272) -- hide or show the select for regions instead of enabling/disabling in customer registration (by @UB3RL33T) + * [magento/magento2#23593](https://github.com/magento/magento2/pull/23593) -- A small fix to improve format customer acl xml. (by @mrkhoa99) + * [magento/magento2#23607](https://github.com/magento/magento2/pull/23607) -- Default filter for reports set to past month (by @rogyar) + * [magento/magento2#22138](https://github.com/magento/magento2/pull/22138) -- BeanShells are changed to correct using of variables (by @AnnaShepa) + * [magento/magento2#22733](https://github.com/magento/magento2/pull/22733) -- Adds module:config:status command which checks if the module config i… (by @hostep) + * [magento/magento2#23351](https://github.com/magento/magento2/pull/23351) -- Fix some framework coding issues (by @fooman) + * [magento/magento2#23444](https://github.com/magento/magento2/pull/23444) -- Fix missing attribute_id condition from filter (by @mattijv) + * [magento/magento2#20579](https://github.com/magento/magento2/pull/20579) -- magento/magento2#12817: [Forwardport] Coupon code with canceled order. (by @p-bystritsky) + * [magento/magento2#23387](https://github.com/magento/magento2/pull/23387) -- magento/magento2#23386: Copy Service does not work properly for Entities which extends Data Object and implements ExtensibleDataInterface. (by @swnsma) + * [magento/magento2#23358](https://github.com/magento/magento2/pull/23358) -- magento/magento2#23345: Creditmemo getOrder() method loads order incorrectly. (by @p-bystritsky) + * [magento/magento2#23459](https://github.com/magento/magento2/pull/23459) -- magento/magento2#22814: Product stock alert - unsubscribe not working (by @yuriichayka) + * [magento/magento2#23598](https://github.com/magento/magento2/pull/23598) -- [2.3] magento catalog:images:resize now Database Media Storage mode aware (by @gwharton) + * [magento/magento2#23291](https://github.com/magento/magento2/pull/23291) -- Optimized dev:urn-catalog:generate for PHPStorm (by @JeroenBoersma) + * [magento/magento2#23592](https://github.com/magento/magento2/pull/23592) -- [Unit] Fix broken unit tests (by @Den4ik) + * [magento/magento2#23649](https://github.com/magento/magento2/pull/23649) -- [2.3] Transfer Encoding of emails changed to QUOTED-PRINTABLE (by @gwharton) + * [magento/magento2#23652](https://github.com/magento/magento2/pull/23652) -- Add missing getClass() to image.phtml so it is more like image_with_borders.phtml (by @woutersamaey) + * [magento/magento2#23710](https://github.com/magento/magento2/pull/23710) -- [2.3] Improve Database Media Storage Configuration settings usability (by @gwharton) + * [magento/magento2#23735](https://github.com/magento/magento2/pull/23735) -- Fixed typo in deploy module README.md (by @arnolds) + * [magento/magento2#22717](https://github.com/magento/magento2/pull/22717) -- Getting 404 url while updating quantity on multiple address cart page (by @vikalps4) + * [magento/magento2#23166](https://github.com/magento/magento2/pull/23166) -- Fix 22085 (by @geet07) + * [magento/magento2#23524](https://github.com/magento/magento2/pull/23524) -- remove html tag from option html from order page (by @sunilit42) + * [magento/magento2#22891](https://github.com/magento/magento2/pull/22891) -- Check if setting is disabled on default scope (by @kassner) + * [magento/magento2#23099](https://github.com/magento/magento2/pull/23099) -- fix customer data race condition when bundling is enabled (by @davidverholen) + * [magento/magento2#23125](https://github.com/magento/magento2/pull/23125) -- Catch throwables in mview updating (by @QuentinFarizonAfrimarket) + * [magento/magento2#23173](https://github.com/magento/magento2/pull/23173) -- Fixed issue #23135: Insert Variable popup missing template variables for new templates (by @maheshWebkul721) + * [magento/magento2#23688](https://github.com/magento/magento2/pull/23688) -- Resolve "Automatically Invoice All Items" is "Yes" but no invoice is created (Zero Subtotal Checkout) (by @edenduong) + * [magento/magento2#23718](https://github.com/magento/magento2/pull/23718) -- Resolve [Authorize.net accept.js] "Place Order" button not being disabled when editing billing address (by @edenduong) + * [magento/magento2#23753](https://github.com/magento/magento2/pull/23753) -- Add Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface to di.xml in issue23717 (by @edenduong) + * [magento/magento2#22984](https://github.com/magento/magento2/pull/22984) -- magento/magento2#14071: Not able to change a position of last two rel... (by @m-a-x-i-m) + * [magento/magento2#23656](https://github.com/magento/magento2/pull/23656) -- Fixes issue 22112 (https://github.com/magento/magento2/issues/22112) ... (by @rsimmons07) + * [magento/magento2#23666](https://github.com/magento/magento2/pull/23666) -- magento/magento2#: Fix storeId param type in the EmailNotification::newAccount, EmailNotificationInterface::newAccount methods (by @atwixfirster) + * [magento/magento2#23681](https://github.com/magento/magento2/pull/23681) -- Resolve Frontend Label For Custom Order Status not Editable in Magento Admin in Single Store Mode (by @edenduong) + * [magento/magento2#23752](https://github.com/magento/magento2/pull/23752) -- [2.3] Database Media Storage : PDF Logo file now database aware (by @gwharton) + * [magento/magento2#23679](https://github.com/magento/magento2/pull/23679) -- Moved Zero Subtotal Checkout Payment Settings (by @textarea) + * [magento/magento2#23779](https://github.com/magento/magento2/pull/23779) -- Resolve "Discount Amount" field is validated after the page load without any action from user in Create New Catalog Rule form issue23777 (by @edenduong) + * [magento/magento2#23787](https://github.com/magento/magento2/pull/23787) -- Fix for PHP_CodeSniffer error after app:config:dump (by @dng-dev) + * [magento/magento2#23790](https://github.com/magento/magento2/pull/23790) -- magento/magento2#23789: CommentLevelsSniff works incorrect with @magento_import statement. (by @p-bystritsky) + * [magento/magento2#23794](https://github.com/magento/magento2/pull/23794) -- Remove duplicate declaration (by @gfernandes410) + * [magento/magento2#23803](https://github.com/magento/magento2/pull/23803) -- Resolve Toggle icon not working in create configuration Product creation Page issue 22702 (by @edenduong) + * [magento/magento2#23782](https://github.com/magento/magento2/pull/23782) -- Cleaning some code gaps (by @Stepa4man) + * [magento/magento2#23840](https://github.com/magento/magento2/pull/23840) -- Fix regular expression comment on function isNameValid() in ImageContentValidator.php (by @nimbus2300) + * [magento/magento2#23845](https://github.com/magento/magento2/pull/23845) -- Add custom added url key to decoded directive string in WYSIWYG editor (by @JeroenVanLeusden) + * [magento/magento2#23866](https://github.com/magento/magento2/pull/23866) -- additional check for correct version of sodium (by @matei) + * [magento/magento2#23901](https://github.com/magento/magento2/pull/23901) -- Resolve Report->Product->Downloads has wrong ACL issue 23900 (by @edenduong) + * [magento/magento2#23905](https://github.com/magento/magento2/pull/23905) -- Resolve No auto-focus after validation at "Create Configurations" button => User can not see the error message issue23904 (by @edenduong) + * [magento/magento2#23917](https://github.com/magento/magento2/pull/23917) -- Resolve Missing Validation at some Payment Method Settings issue 23916 (by @edenduong) + * [magento/magento2#23919](https://github.com/magento/magento2/pull/23919) -- class ApplyAttributesUpdate should use \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE instead of fixing "bundle" (by @edenduong) + * [magento/magento2#23933](https://github.com/magento/magento2/pull/23933) -- Fix display of decimal quantities for wishlist items (by @mfickers) + 2.3.2 ============= * GitHub issues: From af0558bad7cded65998aabfeefffdc5f53a9ba3a Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 26 Aug 2019 16:38:43 -0500 Subject: [PATCH 0444/2437] MC-18685: Remove custom layout updates from admin --- .../Product/Attribute/LayoutUpdateManager.php | 140 ++++++++++++++++++ .../Product/Attribute/Source/LayoutUpdate.php | 24 +++ .../Product/Form/Modifier/LayoutUpdate.php | 56 +++++++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 4 + 4 files changed, 224 insertions(+) create mode 100644 app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php create mode 100644 app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php new file mode 100644 index 0000000000000..c0c0c444c6b8b --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -0,0 +1,140 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\Area; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; +use Magento\Framework\View\Result\Page as PageLayout; + +/** + * Manage available layout updates for products. + */ +class LayoutUpdateManager +{ + + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + * @param LayoutProcessorFactory $layoutProcessorFactory + */ + public function __construct( + FlyweightFactory $themeFactory, + DesignInterface $design, + LayoutProcessorFactory $layoutProcessorFactory + ) { + $this->themeFactory = $themeFactory; + $this->design = $design; + $this->layoutProcessorFactory = $layoutProcessorFactory; + } + + /** + * Adopt product's SKU to be used as layout handle. + * + * @param ProductInterface $product + * @return string + */ + private function sanitizeSku(ProductInterface $product): string + { + return rawurlencode($product->getSku()); + } + + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + + /** + * Fetch list of available files/handles for the product. + * + * @param ProductInterface $product + * @return string[] + */ + public function fetchAvailableFiles(ProductInterface $product): array + { + $identifier = $this->sanitizeSku($product); + $handles = $this->getLayoutProcessor()->getAvailableHandles(); + + return array_filter( + array_map( + function (string $handle) use ($identifier) : ?string { + preg_match( + '/^catalog\_product\_view\_selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', + $handle, + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $handles + ) + ); + } + + /** + * Apply selected custom layout updates. + * + * If no update is selected none will apply. + * + * @param PageLayout $layout + * @param ProductInterface $product + * @return void + */ + public function applyUpdate(PageLayout $layout, ProductInterface $product): void + { + if ($attribute = $product->getCustomAttribute('custom_layout_update_file')) { + $layout->addPageLayoutHandles( + ['selectable' => $this->sanitizeIdentifier($product) . '_' . $attribute->getValue()] + ); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php index 214348890cf2a..d63e77286498b 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -8,6 +8,7 @@ namespace Magento\Catalog\Model\Product\Attribute\Source; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Framework\Api\CustomAttributesDataInterface; @@ -22,6 +23,19 @@ class LayoutUpdate extends AbstractSource implements SpecificSourceInterface */ private $optionsText; + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + /** * @inheritDoc */ @@ -52,6 +66,16 @@ public function getOptionText($value) public function getOptionsFor(CustomAttributesDataInterface $entity): array { $options = $this->getAllOptions(); + if ($entity->getCustomAttribute('custom_layout_update')) { + $existingValue = '__existing__'; + $existingLabel = 'Use existing'; + $options[] = ['label' => $existingLabel, 'value' => $existingValue]; + $this->optionsText[$existingValue] = $existingLabel; + } + foreach ($this->manager->fetchAvailableFiles($entity) as $handle) { + $options[] = ['label' => $handle, 'value' => $handle]; + $this->optionsText[$handle] = $handle; + } return $options; } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php new file mode 100644 index 0000000000000..92afe35c70bb5 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; + +/** + * Additional logic on how to display the layout update field. + */ +class LayoutUpdate implements ModifierInterface +{ + /** + * @var LocatorInterface + */ + private $locator; + + /** + * @param LocatorInterface $locator + */ + public function __construct(LocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * @inheritdoc + * @since 101.1.0 + */ + public function modifyData(array $data) + { + $product = $this->locator->getProduct(); + if ($oldLayout = $product->getCustomAttribute('custom_layout_update')) { + if ($oldLayout->getValue()) { + $data[$product->getId()][AbstractModifier::DATA_SOURCE_DEFAULT]['custom_layout_update_file'] + = '__existing__'; + } + } + + return $data; + } + + /** + * @inheritDoc + */ + public function modifyMeta(array $meta) + { + return $meta; + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c04cfb2dce00a..372b630c6aee9 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -158,6 +158,10 @@ <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice</item> <item name="sortOrder" xsi:type="number">150</item> </item> + <item name="custom_layout_update" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\LayoutUpdate</item> + <item name="sortOrder" xsi:type="number">160</item> + </item> </argument> </arguments> </virtualType> From 5d7082f2217912839f6684cfe1c8b7639d807c92 Mon Sep 17 00:00:00 2001 From: Kieu Phan <kphan@adobe.com> Date: Mon, 26 Aug 2019 16:39:46 -0500 Subject: [PATCH 0445/2437] MC-18532: Update Changelog based on delivered scope Update Changelog for 2.3.3-develop --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f760535cb91b..24e445b043bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -354,7 +354,7 @@ * [magento/magento2#23474](https://github.com/magento/magento2/pull/23474) -- Fixed tooltip missing at store view lable in Cms page and Cms block (by @dipeshrangani) * [magento/magento2#23477](https://github.com/magento/magento2/pull/23477) -- Added quantity validation on Shipping Multiple Address Page (by @nirmalraval18) * [magento/magento2#23494](https://github.com/magento/magento2/pull/23494) -- Removed editor from phone and zipcode (by @kazim-krish) - * [magento/magento2#23310](https://github.com/magento/magento2/pull/23310) -- magento/magento-2#23222: setup:upgrade should return failure when app… (by @ProcessEight) + * [magento/magento2#23310](https://github.com/magento/magento2/pull/23310) -- magento/magento-2#23222: setup:upgrade should return failure when app... (by @ProcessEight) * [magento/magento2#23360](https://github.com/magento/magento2/pull/23360) -- #23354 : Data saving problem error showing when leave blank qty and update it (by @konarshankar07) * [magento/magento2#23427](https://github.com/magento/magento2/pull/23427) -- 23424: fixed search with 0 (by @jeysmook) * [magento/magento2#23496](https://github.com/magento/magento2/pull/23496) -- Resolved + character issue in custom widget (by @sarfarazbheda) @@ -363,14 +363,14 @@ * [magento/magento2#22624](https://github.com/magento/magento2/pull/22624) -- Resolve Typo (by @prashantsharmacedcoss) * [magento/magento2#15383](https://github.com/magento/magento2/pull/15383) -- issue fixed #8258 - assign indices for all array inputs so that validation works properly (by @jayankaghosh) * [magento/magento2#18075](https://github.com/magento/magento2/pull/18075) -- Feature/issue 13561 2.3 (by @bnymn) - * [magento/magento2#22658](https://github.com/magento/magento2/pull/22658) -- Fixed #22545 Status downloadable product stays pending after succesfu… (by @shikhamis11) + * [magento/magento2#22658](https://github.com/magento/magento2/pull/22658) -- Fixed #22545 Status downloadable product stays pending after succesfu... (by @shikhamis11) * [magento/magento2#23500](https://github.com/magento/magento2/pull/23500) -- Fixed issue #23383 (by @manishgoswamij) * [magento/magento2#23226](https://github.com/magento/magento2/pull/23226) -- Spacing issue for Gift message section in my account (by @amjadm61) * [magento/magento2#23272](https://github.com/magento/magento2/pull/23272) -- hide or show the select for regions instead of enabling/disabling in customer registration (by @UB3RL33T) * [magento/magento2#23593](https://github.com/magento/magento2/pull/23593) -- A small fix to improve format customer acl xml. (by @mrkhoa99) * [magento/magento2#23607](https://github.com/magento/magento2/pull/23607) -- Default filter for reports set to past month (by @rogyar) * [magento/magento2#22138](https://github.com/magento/magento2/pull/22138) -- BeanShells are changed to correct using of variables (by @AnnaShepa) - * [magento/magento2#22733](https://github.com/magento/magento2/pull/22733) -- Adds module:config:status command which checks if the module config i… (by @hostep) + * [magento/magento2#22733](https://github.com/magento/magento2/pull/22733) -- Adds module:config:status command which checks if the module config i... (by @hostep) * [magento/magento2#23351](https://github.com/magento/magento2/pull/23351) -- Fix some framework coding issues (by @fooman) * [magento/magento2#23444](https://github.com/magento/magento2/pull/23444) -- Fix missing attribute_id condition from filter (by @mattijv) * [magento/magento2#20579](https://github.com/magento/magento2/pull/20579) -- magento/magento2#12817: [Forwardport] Coupon code with canceled order. (by @p-bystritsky) From 9377d04ac9ff7d4603759e0b4a944fa89eeef423 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 27 Aug 2019 13:50:10 +0400 Subject: [PATCH 0446/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6411 --- ...EditCustomerInformationFromActionGroup.xml | 7 ++ ...inOrderSelectShippingMethodActionGroup.xml | 23 ++++ .../AdminInvoicePaymentShippingSection.xml | 4 +- ...leRatesShippingMethodStatusActionGroup.xml | 7 ++ ...ustomStoreShippingMethodTableRatesTest.xml | 119 ++++++++++++++++++ .../tests/_data/test_tablerates.csv | 13 ++ 6 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml create mode 100644 dev/tests/acceptance/tests/_data/test_tablerates.csv diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml index 09033955ecc60..fa8931bc53141 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml @@ -30,4 +30,11 @@ <waitForPageLoad stepKey="wait"/> <scrollToTopOfPage stepKey="scrollToTop"/> </actionGroup> + <actionGroup name="AdminAssociateCustomerToCustomWebsiteActionGroup"> + <arguments> + <argument name="websiteName" type="string" defaultValue="secondWebsite"/> + </arguments> + <conditionalClick selector="{{AdminCustomerAccountInformationSection.accountInformationTab}}" dependentSelector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" visible="false" stepKey="goToAccountInformation"/> + <selectOption selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{websiteName}}" stepKey="selectWebSite"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..87f0c4d7e470e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderSelectShippingMethodActionGroup"> + <arguments> + <argument name="methodTitle" type="string" defaultValue="Flat Rate"/> + <argument name="methodName" type="string" defaultValue="Fixed"/> + </arguments> + <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" stepKey="waitForShippingMethodsOpen"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" stepKey="openShippingMethod"/> + <conditionalClick selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" dependentSelector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" visible="false" stepKey="openShippingMethodSecondTime"/> + <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" stepKey="waitForShippingMethod"/> + <click selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml index fda886a839802..a0b93c66f3a59 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml @@ -17,5 +17,7 @@ <element name="CreateShipment" type="checkbox" selector=".order-shipping-address input[name='invoice[do_shipment]']"/> <element name="getShippingMethodAndRates" type="button" selector="//span[text()='Get shipping methods and rates']" timeout="60"/> <element name="shippingMethod" type="button" selector="//label[contains(text(), 'Fixed')]" timeout="60"/> + <element name="fixedPriceShippingMethod" type="button" selector="//*[contains(text(), '{{methodTitle}}')]/parent::dl//label[contains(text(), '{{methodName}}')]" parameterized="true"/> + <element name="getShippingMethod" type="button" selector="#order-shipping-method-summary a"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml index e506ca3a7662f..2df78f3a0d1aa 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml @@ -17,4 +17,11 @@ <uncheckOption selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" stepKey="uncheckUseSystemValue"/> <selectOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" userInput="{{status}}" stepKey="changeTableRatesMethodStatus"/> </actionGroup> + <actionGroup name="AdminImportFileTableRatesShippingMethodActionGroup"> + <arguments> + <argument name="file" type="string" defaultValue="test_tablerates.csv"/> + </arguments> + <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> + <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="{{file}}" stepKey="attachFileForImport"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml new file mode 100644 index 0000000000000..bfb94c4293135 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderCustomStoreShippingMethodTableRatesTest"> + <annotations> + <features value="Shipping"/> + <stories value="Shipping method Table Rates settings gets from wrong store"/> + <title value="Create order on second store with shipping method Table Rates"/> + <description value="Create order on second store with shipping method Table Rates"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6411"/> + <useCaseId value="MAGETWO-91702"/> + <group value="shipping"/> + </annotations> + <before> + <!--Create product and customer--> + <comment userInput="Create product and customer" stepKey="commentCreateProductAndCustomer"/> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create website, store group and store view--> + <comment userInput="Create website, store group and store view" stepKey="commentCreateWebsiteStoreAndView"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="{{customWebsite.name}}"/> + <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> + <argument name="storeGroupCode" value="{{customStoreGroup.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"> + <argument name="StoreGroup" value="customStoreGroup"/> + <argument name="customStore" value="customStore"/> + </actionGroup> + <!--Enable Table Rate method and import csv file--> + <comment userInput="Enable Table Rate method and import csv file" stepKey="commentEnableTableRates"/> + <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethodForDefaultWebsite"> + <argument name="status" value="0"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfigForDefaultWebsite"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchCustomWebsite"> + <argument name="website" value="customWebsite"/> + </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethod"> + <argument name="status" value="1"/> + </actionGroup> + <actionGroup ref="AdminImportFileTableRatesShippingMethodActionGroup" stepKey="importCSVFile"> + <argument name="file" value="test_tablerates.csv"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!--Delete created data--> + <comment userInput="Delete created data" stepKey="commetnDeleteCreatedData"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteSecondCreatedCustomer"> + <argument name="customerEmail" value="$$createCustomer.id$$"/> + </actionGroup> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Assign product to custom website--> + <comment userInput="Assign product to custom website" stepKey="commentAssignProductToWebsite"/> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="unassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> + <argument name="website" value="{{_defaultWebsite.name}}"/> + </actionGroup> + <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct"> + <argument name="website" value="{{customWebsite.name}}"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!--Assign customer to custom website--> + <comment userInput="Assign customer to custom website" stepKey="commentAssignCustomerToWebsite"/> + <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> + <argument name="customerId" value="$$createCustomer.id$$"/> + </actionGroup> + <actionGroup ref="AdminAssociateCustomerToCustomWebsiteActionGroup" stepKey="associateCustomerToWebsite"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + <actionGroup ref="AdminSaveCustomerAndAssertSuccessMessage" stepKey="saveAndCheckSuccessMessage"/> + <!--Create order--> + <comment userInput="Create order" stepKey="commentCreateOrder"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + <argument name="storeView" value="customStore"/> + </actionGroup> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <argument name="customer" value="$$createCustomer$$"/> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + <!--Choose Best Way shipping Method--> + <comment userInput="Choose Best Way shipping Method" stepKey="commentChooseShippingMethod"/> + <actionGroup ref="AdminOrderSelectShippingMethodActionGroup" stepKey="chooseBestWayMethod"> + <argument name="methodTitle" value="Best Way"/> + <argument name="methodName" value="Table Rate"/> + </actionGroup> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/test_tablerates.csv b/dev/tests/acceptance/tests/_data/test_tablerates.csv new file mode 100644 index 0000000000000..d5a59ae6bccf2 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/test_tablerates.csv @@ -0,0 +1,13 @@ +Country,Region/State,"Zip/Postal Code","Order Subtotal (and above)","Shipping Price" +USA,*,*,0.0000,7.9900 +USA,*,*,7.0000,6.9900 +USA,*,*,13.0000,5.9900 +USA,*,*,25.9900,4.9900 +USA,AK,*,0.0000,8.9900 +USA,AK,*,7.0000,7.9900 +USA,AK,*,13.0000,6.9900 +USA,AK,*,25.9900,5.9900 +USA,HI,*,0.0000,8.9900 +USA,HI,*,7.0000,7.9900 +USA,HI,*,13.0000,6.9900 +USA,HI,*,25.9900,5.9900 From ffa865f1edcc7ffe4041ac84575ac7df32c043e6 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 27 Aug 2019 14:34:06 +0400 Subject: [PATCH 0447/2437] MC-18820: Increase test coverage for Admin functional area - Automation test for MC-6310 --- .../Mftf/Section/LocaleOptionsSection.xml | 1 + .../Test/Mftf/Data/CatalogSearchData.xml | 9 ++- .../Mftf/Metadata/catalog_search-meta.xml | 11 ++++ .../AdminElasticConnectionTestActionGroup.xml | 22 +++++++ ...AdminCatalogSearchConfigurationSection.xml | 15 +++++ ...frontElasticSearchForChineseLocaleTest.xml | 63 +++++++++++++++++++ 6 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml index a460aaebf1051..bd2a345c99660 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml @@ -12,5 +12,6 @@ <element name="sectionHeader" type="text" selector="#general_locale-head"/> <element name="timezone" type="select" selector="#general_locale_timezone"/> <element name="useDefault" type="checkbox" selector="#general_locale_timezone_inherit"/> + <element name="defaultLocale" type="checkbox" selector="#general_locale_code_inherit"/> </section> </sections> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml index 6868456079110..7e86ade93ad44 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml @@ -23,5 +23,10 @@ <entity name="SetMinQueryLengthToOne" type="number"> <data key="value">1</data> </entity> - -</entities> \ No newline at end of file + <entity name="SetCatalogSearchEngineToDefault" type="catalog_search_engine_default"> + <requiredEntity type="enable">DefaultCatalogSearchEngine</requiredEntity> + </entity> + <entity name="DefaultCatalogSearchEngine" type="enable"> + <data key="inherit">true</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml index 7405377249aa4..ce869f81a23df 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml @@ -29,4 +29,15 @@ </object> </object> </operation> + <operation name="CatalogSearchEngineDefault" dataType="catalog_search_engine_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="catalog_search_engine_default"> + <object key="search" dataType="catalog_search_engine_default"> + <object key="fields" dataType="catalog_search_engine_default"> + <object key="engine" dataType="enable"> + <field key="inherit">boolean</field> + </object> + </object> + </object> + </object> + </operation> </operations> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml new file mode 100644 index 0000000000000..e40bf3691e8db --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminElasticConnectionTestActionGroup"> + <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="openAdminCatalogSearchConfigPage"/> + <waitForPageLoad stepKey="waitPageToLoad"/> + <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" visible="false" stepKey="expandCatalogSearchTab"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="waitForConnectionButton"/> + <click selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="clickOnTestConnectionButton"/> + <waitForPageLoad stepKey="waitForConnectionEstablishment"/> + <see selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" userInput="Successful! Test again?" stepKey="checkThatConnectionExists"/> + <scrollTo selector="{{AdminConfigCatalogCategoryPermissionsSection.catalogPermissionsTab}}" stepKey="scrollToCatalogPermissionsTab"/> + <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="closeCatalogSearchTab"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml new file mode 100644 index 0000000000000..a6f35606ed79b --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogSearchConfigurationSection"> + <element name="elastic6ConnectionWizard" type="button" selector="#catalog_search_elasticsearch6_test_connect_wizard"/> + <element name="connectionStatus" type="text" selector="#catalog_search_elasticsearch6_test_connect_wizard_result"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml new file mode 100644 index 0000000000000..1acdaa8ce2b33 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontElasticSearchForChineseLocaleTest"> + <annotations> + <features value="Elasticsearch"/> + <stories value="Elasticsearch for Chinese produce error"/> + <title value="Elastic search for Chinese locale"/> + <description value="Elastic search for Chinese locale"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6310"/> + <useCaseId value="MAGETWO-91625"/> + <group value="elasticsearch"/> + </annotations> + <before> + <!-- Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront --> + <comment userInput="Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront" stepKey="doInitialSetups"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <magentoCLI command="config:set --scope=websites --scope-code=base general/locale/code zh_Hans_CN" stepKey="setLocaleToChina"/> + <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="setSearchEngineToElastic6"/> + <actionGroup ref="AdminElasticConnectionTestActionGroup" stepKey="checkConnection"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + </before> + <after> + <!-- Delete created data and reset initial configuration --> + <comment userInput="Delete created data and reset initial configuration" stepKey="deleteCreatedDataAndResetConfig"/> + <amOnPage url="{{GeneralConfigurationPage.url}}" stepKey="goToConfigurationGeneralPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchWebsiteActionGroup"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <conditionalClick selector="{{LocaleOptionsSection.sectionHeader}}" dependentSelector="{{LocaleOptionsSection.timezone}}" visible="false" stepKey="openLocaleSection"/> + <checkOption selector="{{LocaleOptionsSection.defaultLocale}}" stepKey="setDefaultLocaleValue"/> + <click selector="{{LocaleOptionsSection.sectionHeader}}" stepKey="closeTab"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveConfigButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeSuccess"/> + <createData entity="SetCatalogSearchEngineToDefault" stepKey="setSearchEngineToDefault"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <!-- Search for product by name --> + <comment userInput="Search for product by name" stepKey="searchForProduct"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName"> + <argument name="phrase" value="$$createProduct.name$$"/> + </actionGroup> + <!-- Check if searched product is displayed --> + <comment userInput="Check if searched product is displayed" stepKey="checkThatProductIsDisplayed"/> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInCategoryPage"/> + </test> +</tests> From 54d6fa00a60a03049fc2db82193addd0850f5f71 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Tue, 27 Aug 2019 09:50:54 -0500 Subject: [PATCH 0448/2437] MC-19633: Disabled Products Do Not Appear in Search Results of Link Attribute - update to show all products regardless of status --- .../Catalog/Model/ProductLink/Search.php | 1 - .../Adminhtml/Product/SearchTest.php | 23 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductLink/Search.php b/app/code/Magento/Catalog/Model/ProductLink/Search.php index 681c01bb1281b..ad7f3370ab3fe 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Search.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Search.php @@ -60,7 +60,6 @@ public function prepareCollection( ): \Magento\Catalog\Model\ResourceModel\Product\Collection { $productCollection = $this->productCollectionFactory->create(); $productCollection->addAttributeToSelect(ProductInterface::NAME); - $productCollection->setVisibility($this->catalogVisibility->getVisibleInCatalogIds()); $productCollection->setPage($pageNum, $limit); $this->filter->addFilter($productCollection, 'fulltext', ['fulltext' => $searchKey]); $productCollection->setPage($pageNum, $limit); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index 8a33543e93439..0704d59a1431c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -38,7 +38,8 @@ public function testExecuteNonExistingSearchKey() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('{"options":[],"total":0}', $responseBody); + $jsonResponse = json_decode($responseBody, true); + $this->assertEmpty($jsonResponse['options']); } /** @@ -57,6 +58,24 @@ public function testExecuteNotVisibleIndividuallyProducts() : void ->setPostValue('limit', 50); $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); - $this->assertContains('{"options":[],"total":0}', $responseBody); + $jsonResponse = json_decode($responseBody, true); + $this->assertEquals(1, $jsonResponse['total']); + $this->assertCount(1, $jsonResponse['options']); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiple_mixed_products.php + */ + public function testExecuteEnabledAndDisabledProducts() : void + { + $this->getRequest() + ->setPostValue('searchKey', 'simple') + ->setPostValue('page', 1) + ->setPostValue('limit', 50); + $this->dispatch('backend/catalog/product/search'); + $responseBody = $this->getResponse()->getBody(); + $jsonResponse = json_decode($responseBody, true); + $this->assertEquals(7, $jsonResponse['total']); + $this->assertCount(7, $jsonResponse['options']); } } From 2cee2dcaefb851350ed10a18450cc1f010aa3922 Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Tue, 27 Aug 2019 11:10:19 -0500 Subject: [PATCH 0449/2437] MC-18995: Page Builder & PWA Studio Compatibility - Fix Product Widget list rendering through GraphQL --- .../CmsGraphQl/Plugin/DesignLoader.php | 68 +++++++++++++++++++ .../Magento/CmsGraphQl/etc/graphql/di.xml | 3 + 2 files changed, 71 insertions(+) create mode 100644 app/code/Magento/CmsGraphQl/Plugin/DesignLoader.php diff --git a/app/code/Magento/CmsGraphQl/Plugin/DesignLoader.php b/app/code/Magento/CmsGraphQl/Plugin/DesignLoader.php new file mode 100644 index 0000000000000..d1df15c7026df --- /dev/null +++ b/app/code/Magento/CmsGraphQl/Plugin/DesignLoader.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CmsGraphQl\Plugin; + +use Magento\Catalog\Model\Product; +use Magento\Framework\Message\MessageInterface; + +/** + * Load necessary design files for GraphQL + */ +class DesignLoader +{ + /** + * @var \Magento\Framework\View\DesignLoader + */ + protected $designLoader; + + /** + * @var \Magento\Framework\Message\ManagerInterface + */ + protected $messageManager; + + /** + * @param \Magento\Framework\View\DesignLoader $designLoader + * @param \Magento\Framework\Message\ManagerInterface $messageManager + */ + public function __construct( + \Magento\Framework\View\DesignLoader $designLoader, + \Magento\Framework\Message\ManagerInterface $messageManager + ) { + $this->designLoader = $designLoader; + $this->messageManager = $messageManager; + } + + /** + * Before create load the design files + * + * @param \Magento\Catalog\Block\Product\ImageFactory $subject + * @param Product $product + * @param string $imageId + * @param array|null $attributes + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeCreate( + \Magento\Catalog\Block\Product\ImageFactory $subject, + Product $product, + string $imageId, + array $attributes = null + ) { + try { + $this->designLoader->load(); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + if ($e->getPrevious() instanceof \Magento\Framework\Config\Dom\ValidationException) { + /** @var MessageInterface $message */ + $message = $this->messageManager + ->createMessage(MessageInterface::TYPE_ERROR) + ->setText($e->getMessage()); + $this->messageManager->addUniqueMessages([$message]); + } + } + } +} \ No newline at end of file diff --git a/app/code/Magento/CmsGraphQl/etc/graphql/di.xml b/app/code/Magento/CmsGraphQl/etc/graphql/di.xml index 78c1071d8e07c..1be6d819a0dc9 100644 --- a/app/code/Magento/CmsGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CmsGraphQl/etc/graphql/di.xml @@ -18,4 +18,7 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Block\Product\ImageFactory"> + <plugin name="designLoader" type="Magento\CmsGraphQl\Plugin\DesignLoader" /> + </type> </config> From 60dc36c70b9de9a03bf3554c1f01be9e11bd2818 Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Tue, 27 Aug 2019 16:48:47 -0500 Subject: [PATCH 0450/2437] MC-19633: Disabled Products Do Not Appear in Search Results of Link Attribute - fix test --- .../Catalog/Controller/Adminhtml/Product/SearchTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php index 0704d59a1431c..cfa8b6022963e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/SearchTest.php @@ -75,7 +75,7 @@ public function testExecuteEnabledAndDisabledProducts() : void $this->dispatch('backend/catalog/product/search'); $responseBody = $this->getResponse()->getBody(); $jsonResponse = json_decode($responseBody, true); - $this->assertEquals(7, $jsonResponse['total']); - $this->assertCount(7, $jsonResponse['options']); + $this->assertEquals(6, $jsonResponse['total']); + $this->assertCount(6, $jsonResponse['options']); } } From 9765c088c8654d0b3e909d1786e9bee68f2284d5 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Tue, 27 Aug 2019 21:19:16 -0500 Subject: [PATCH 0451/2437] Convert DeleteProductsFromWishlistOnFrontendTest to MFTF --- ...orefrontCustomerWishlistProductSection.xml | 1 + ...teBundleDynamicProductFromWishlistTest.xml | 95 +++++++++++ ...leteBundleFixedProductFromWishlistTest.xml | 87 ++++++++++ ...eteConfigurableProductFromWishlistTest.xml | 149 ++++++++++++++++++ ...leteProductsFromWishlistOnFrontendTest.xml | 3 + 5 files changed, 335 insertions(+) create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index 0b6c2f1191c40..73a82e8976fc7 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -24,5 +24,6 @@ <element name="productSuccessShareMessage" type="text" selector="div.message-success"/> <element name="pager" type="block" selector=".toolbar .pager"/> <element name="wishlistEmpty" type="block" selector=".form-wishlist-items .message.info.empty"/> + <element name="removeProduct" type="button" selector=".products-grid a.btn-remove" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml new file mode 100644 index 0000000000000..88621b241db89 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontDeleteBundleDynamicProductFromWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Dynamic Bundle Product from Wishlist on Frontend"/> + <description value="Delete Dynamic Bundle Product from Wishlist on Frontend"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-28874"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">100.00</field> + </createData> + <!--Create Bundle product--> + <createData entity="BundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">True</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="goToProductPageViaID" stepKey="goToProduct"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> + <selectOption userInput="Separately" selector="{{AdminProductFormBundleSection.shipmentType}}" stepKey="selectSeparately"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Navigate to catalog page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createBundleProduct$$"/> + </actionGroup> + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item". --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmpty" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml new file mode 100644 index 0000000000000..cb8b5b1de859f --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontDeleteBundleFixedProductFromWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Fixed Bundle Product from Wishlist on Frontend"/> + <description value="Delete Fixed Bundle Product from Wishlist on Frontend"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-28874"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">100.00</field> + </createData> + <!-- Create bundle product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <field key="price_type">0</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <field key="price_type">0</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Navigate to catalog page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createBundleProduct$$"/> + </actionGroup> + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item". --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmpty" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml new file mode 100644 index 0000000000000..f23f09129cb63 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddConfigurableProductWithoutConfigureToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Configurable Product from Wishlist on Frontend"/> + <description value="Delete Configurable Product from Wishlist on Frontend"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-28874"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the first option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the second option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the third option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create Configurable product --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a simple product and give it the attribute with the first option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <field key="price">10.00</field> + </createData> + + <!--Create a simple product and give it the attribute with the second option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <field key="price">20.00</field> + </createData> + + <!--Create a simple product and give it the attribute with the Third option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + <field key="price">30.00</field> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + + <!-- Add the second simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- Add the third simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- 2. Navigate to catalog page --> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- 3. Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item". --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmpty" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml index d6b930d999537..26593636d3fcb 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml @@ -39,6 +39,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation4"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> @@ -46,6 +47,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> @@ -53,6 +55,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">configurableProduct::default</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> From 20567da3428c0ee720c3c5804d6d36d0b6ead089 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Tue, 27 Aug 2019 21:43:54 -0500 Subject: [PATCH 0452/2437] Convert MoveProductFromShoppingCartToWishlistTest to MFTF --- .../Section/CheckoutCartProductSection.xml | 1 + ...orefrontCustomerWishlistProductSection.xml | 3 + ...eProductFromShoppingCartToWishlistTest.xml | 164 ++++++++++++++++++ ...eProductFromShoppingCartToWishlistTest.xml | 110 ++++++++++++ ...eProductFromShoppingCartToWishlistTest.xml | 101 +++++++++++ ...lProductFromShoppingCartToWishlistTest.xml | 71 ++++++++ ...eProductFromShoppingCartToWishlistTest.xml | 5 +- 7 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml create mode 100644 app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index 3ab3fa5857b78..f028fae1f1f21 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -32,6 +32,7 @@ selector="//table[@id='shopping-cart-table']//tbody//tr[contains(@class,'item-actions')]//a[contains(@class,'action-delete')]"/> <element name="removeProductByName" type="text" selector="//*[contains(text(), '{{productName}}')]/ancestor::tbody//a[@class='action action-delete']" parameterized="true" timeout="30"/> <element name="productName" type="text" selector="//tbody[@class='cart item']//strong[@class='product-item-name']"/> + <element name="moveToWishlistByProductName" type="button" selector="//a[contains(text(), '{{productName}}')]/ancestor::tbody/tr//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="nthItemOption" type="block" selector=".item:nth-of-type({{numElement}}) .item-options" parameterized="true"/> <element name="nthEditButton" type="block" selector=".item:nth-of-type({{numElement}}) .action-edit" parameterized="true"/> <element name="nthBundleOptionName" type="text" selector=".product-item-details .item-options:nth-of-type({{numOption}}) dt" parameterized="true"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index 0b6c2f1191c40..dba0acfc29e8e 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -24,5 +24,8 @@ <element name="productSuccessShareMessage" type="text" selector="div.message-success"/> <element name="pager" type="block" selector=".toolbar .pager"/> <element name="wishlistEmpty" type="block" selector=".form-wishlist-items .message.info.empty"/> + <element name="productSeeDetailsByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]" parameterized="true"/> + <element name="productSeeDetailsLabelByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]//dt[@class='label']" parameterized="true"/> + <element name="productSeeDetailsValueByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]//dd[@class='values']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..317f937def3f1 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Configurable Product from Shopping Cart to Wishlist"/> + <description value="Move Configurable Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29545"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create an attribute with three options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the first option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the second option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Get the third option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create Configurable product --> + <createData entity="BaseConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create a simple product and give it the attribute with the first option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <field key="price">10.00</field> + </createData> + + <!--Create a simple product and give it the attribute with the second option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <field key="price">20.00</field> + </createData> + + <!--Create a simple product and give it the attribute with the Third option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + <field key="price">30.00</field> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + + <!-- Add the first simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + + <!-- Add the second simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <!-- Add the third simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="product" value="$$createConfigProduct$$"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption1"/> + <scrollTo selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" y="-200" stepKey="scroll"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createConfigProduct.name$$)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="$$createConfigProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + + <!-- Assert product is present in wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> + <waitForPageLoad stepKey="waitForWishlistPage"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> + <see userInput="$20.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + + <!-- Assert product details in Wishlist --> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createConfigProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> + <see userInput="$$createConfigProductAttribute.default_value$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createConfigProduct.name$$)}}" stepKey="seeAttribute"/> + <see userInput="$$getConfigAttributeOption2.label$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createConfigProduct.name$$)}}" stepKey="seeOption"/> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..dcd69a61e596f --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> + <description value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29545"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">20.00</field> + </createData> + <!--Create Bundle product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="goToProductPageViaID" stepKey="goToProduct"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> + <selectOption userInput="Separately" selector="{{AdminProductFormBundleSection.shipmentType}}" stepKey="selectSeparately"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeButton"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts($$createBundleOption1_1.title$$)}}" userInput="$$simpleProduct1.sku$$ +$100.00" stepKey="selectOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity($$createBundleOption1_1.title$$)}}" userInput="1" stepKey="fillQuantity00"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createBundleProduct.name$$)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="$$createBundleProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + + <!-- Assert product is present in wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> + <waitForPageLoad stepKey="waitForWishlistPage"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createBundleProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> + <see userInput="$100.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + + <!-- Assert product details in Wishlist --> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createBundleProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> + <see userInput="$$createBundleOption1_1.title$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createBundleProduct.name$$)}}" stepKey="seeBundleOption"/> + <see userInput="$$simpleProduct1.sku$$ $100.00" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createBundleProduct.name$$)}}" stepKey="seeProduct"/> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..4d99b05e9aa6a --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> + <description value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29545"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <!-- Create bundle product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <field key="price_type">0</field> + <field key="price">100</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <field key="price_type">0</field> + <field key="price">100</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeButton"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts($$createBundleOption1_1.title$$)}}" userInput="$$simpleProduct1.sku$$ +$100.00" stepKey="selectOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity($$createBundleOption1_1.title$$)}}" userInput="1" stepKey="fillQuantity00"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createBundleProduct.name$$)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="$$createBundleProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + + <!-- Assert product is present in wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> + <waitForPageLoad stepKey="waitForWishlistPage"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createBundleProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> + <see userInput="$101.23" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + + <!-- Assert product details in Wishlist --> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createBundleProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> + <see userInput="$$createBundleOption1_1.title$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createBundleProduct.name$$)}}" stepKey="seeBundleOption"/> + <see userInput="$$simpleProduct1.sku$$ $100.00" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createBundleProduct.name$$)}}" stepKey="seeProduct"/> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..baaae80f7d081 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveVirtualProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Virtual Product from Shopping Cart to Wishlist"/> + <description value="Move Virtual Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-29545"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultVirtualProduct" stepKey="createProduct"> + <field key="price">40</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Virtual Product page --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="OpenStoreFrontProductPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + + <!-- Add Virtual product to the cart and Assert add product to cart success message--> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createProduct.name$$)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="$$createProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + + <!-- Assert product is present in wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> + <waitForPageLoad stepKey="waitForWishlistPage"/> + + <actionGroup ref="StorefrontCustomerCheckProductInWishlist" stepKey="assertProductIsPresentInWishlist"> + <argument name="productVar" value="$$createProduct$$"/> + </actionGroup> + + <!-- Assert cart is empty --> + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml index 95e6a854ed266..aa3b646161a17 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml @@ -15,6 +15,7 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertCartIsEmpty" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">catalogProductVirtual::default</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -29,7 +30,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation4"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test, mftf_migrated:yes</data> <data name="product/0" xsi:type="string">configurableProduct::default</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -37,6 +38,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -44,6 +46,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> From 18dabb18c584a56942660a525370fac3db2a7260 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Tue, 27 Aug 2019 21:57:50 -0500 Subject: [PATCH 0453/2437] Convert ExpireSessionTest to MFTF --- .../Test/Mftf/Data/CookieConfigData.xml | 16 +++++++ .../Mftf/Section/AdminLoginFormSection.xml | 1 + .../Mftf/Test/AdminExpireAdminSessionTest.xml | 36 ++++++++++++++ .../Test/AdminExpireCustomerSessionTest.xml | 47 +++++++++++++++++++ .../Test/TestCase/ExpireSessionTest.xml | 2 + 5 files changed, 102 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml create mode 100644 app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml index 52a6c27a37ea8..a844e962202f8 100644 --- a/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml +++ b/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml @@ -20,4 +20,20 @@ <data key="scope_code">base</data> <data key="value">''</data> </entity> + <entity name="ChangeWebCookieLifetimeConfigData"> + <data key="path">web/cookie/cookie_lifetime</data> + <data key="value">60</data> + </entity> + <entity name="DefaultWebCookieLifetimeConfigData"> + <data key="path">web/cookie/cookie_lifetime</data> + <data key="value">3600</data> + </entity> + <entity name="ChangeAdminSecuritySessionLifetimeConfigData"> + <data key="path">admin/security/session_lifetime</data> + <data key="value">60</data> + </entity> + <entity name="DefaultAdminSecuritySessionLifetimeConfigData"> + <data key="path">admin/security/session_lifetime</data> + <data key="value">7200</data> + </entity> </entities> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml index bd65dea89abc2..0ad63c0f0d23a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml @@ -13,5 +13,6 @@ <element name="password" type="input" selector="#login"/> <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> <element name="forgotPasswordLink" type="button" selector=".action-forgotpassword" timeout="10"/> + <element name="loginBlock" type="block" selector=".adminhtml-auth-login"/> </section> </sections> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml new file mode 100644 index 0000000000000..1ed8cc9e9aa6d --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExpireAdminSessionTest"> + <annotations> + <stories value="Admin Session Expire"/> + <title value="Expire Admin Session"/> + <description value="Expire Admin Session"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-47723"/> + <group value="backend"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <!-- 4. Restore default configuration settings. --> + <magentoCLI command="config:set {{DefaultAdminSecuritySessionLifetimeConfigData.path}} {{DefaultAdminSecuritySessionLifetimeConfigData.value}}" stepKey="setDefaultSessionLifetime"/> + </after> + <!-- 1. Apply configuration settings. --> + <magentoCLI command="config:set {{ChangeAdminSecuritySessionLifetimeConfigData.path}} {{ChangeAdminSecuritySessionLifetimeConfigData.value}}" stepKey="changeCookieLifetime"/> + + <!-- 2. Wait for session to expire. --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <wait time="60" stepKey="waitForSessionLifetime"/> + <reloadPage stepKey="reloadPage"/> + + <!-- 3. Perform asserts. --> + <seeElement selector="{{AdminLoginFormSection.loginBlock}}" stepKey="assertAdminLoginPageIsAvailable"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml new file mode 100644 index 0000000000000..9e3301e4a26a3 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExpireCustomerSessionTest"> + <annotations> + <stories value="Admin Session Expire"/> + <title value="Check that session expires according with time settings applied in configuration"/> + <description value="Check that session expires according with time settings applied in configuration"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-47722"/> + <group value="backend"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <!-- 6. Restore default configuration settings. --> + <magentoCLI command="config:set {{DefaultWebCookieLifetimeConfigData.path}} {{DefaultWebCookieLifetimeConfigData.value}}" stepKey="setDefaultCookieLifetime"/> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- 1. Login to Admin. --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- 2. Create customer if needed. --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!-- 3. Apply configuration settings. --> + <magentoCLI command="config:set {{ChangeWebCookieLifetimeConfigData.path}} {{ChangeWebCookieLifetimeConfigData.value}}" stepKey="changeCookieLifetime"/> + + <!-- 4. Wait for session to expire. --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + <wait time="60" stepKey="waitForCookieLifetime"/> + <reloadPage stepKey="reloadPage"/> + + <!-- 5. Perform asserts. --> + <seeElement selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" stepKey="assertAuthorizationLinkIsVisibleOnStoreFront"/> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml index e67ceb99f2eef..195d1330ae78a 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml @@ -8,12 +8,14 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\ExpireSessionTest" summary="Admin Session Expire" ticketId="MAGETWO-47723"> <variation name="ExpireSessionTestVariation1" summary="Check that session expires according with time settings applied in configuration" ticketId="MAGETWO-47722"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="configData" xsi:type="string">default_cookie_lifetime_60_seconds</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="sessionLifetimeInSeconds" xsi:type="number">60</data> <constraint name="Magento\Cms\Test\Constraint\AssertAuthorizationLinkIsVisibleOnStoreFront" /> </variation> <variation name="ExpireAdminSession" summary="Expire Admin Session" ticketId="MAGETWO-47723"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="configData" xsi:type="string">admin_session_lifetime_60_seconds</data> <data name="sessionLifetimeInSeconds" xsi:type="number">60</data> <constraint name="Magento\Backend\Test\Constraint\AssertAdminLoginPageIsAvailable" /> From ff7c3c9b8d9b1732cd5b69142a856ad27c0b839e Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 28 Aug 2019 13:27:16 +0300 Subject: [PATCH 0454/2437] Convert DeleteCategoryUrlRewriteEntityTest to MFTF --- ...dminDeleteCategoryUrlRewriteEntityTest.xml | 75 +++++++++++++++++++ .../DeleteCategoryUrlRewriteEntityTest.xml | 2 + 2 files changed, 77 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityTest.xml new file mode 100644 index 0000000000000..d0e976a1f9e3d --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCategoryUrlRewriteEntityTest"> + <annotations> + <stories value="Delete category URL rewrite"/> + <title value="Delete category URL rewrite"/> + <description value="Login as admin and delete category Url Rewrite"/> + <group value="urlRewrite"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create the Category Url Rewrite--> + <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewrite"> + <argument name="category" value="$$category.name$$"/> + <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="-"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!--Delete the Category Url Rewrite--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="-"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="checkUrlOnFrontend"> + <argument name="requestPath" value="-"/> + </actionGroup> + + <!--Create the Category Url Rewrite--> + <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewriteSecondTime"> + <argument name="category" value="$$category.name$$"/> + <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="newrequestpath.html"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!--Delete the Category Url Rewrite--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewriteSecondTime"> + <argument name="requestPath" value="newrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="checkUrlOnFrontendSecondTime"> + <argument name="requestPath" value="newrequestpath.html"/> + </actionGroup> + + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml index 56440e8a8492b..42f71b8d01f76 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\UrlRewrite\Test\TestCase\DeleteCategoryUrlRewriteEntityTest" summary="Delete Category URL Rewrites" ticketId="MAGETWO-25086"> <variation name="DeleteCategoryUrlRewriteEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/category/view/id/%category::default%</data> <data name="urlRewrite/data/redirect_type" xsi:type="string">No</data> <data name="urlRewrite/data/request_path" xsi:type="string">-</data> @@ -15,6 +16,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCategoryUrlRewriteEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/category/view/id/%category::default%</data> <data name="urlRewrite/data/redirect_type" xsi:type="string">No</data> <data name="urlRewrite/data/request_path" xsi:type="string">example%isolation%.html</data> From f208cf5d0db47c13a70fe4e3cf7e3dd3763c6424 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 28 Aug 2019 16:32:48 +0300 Subject: [PATCH 0455/2437] Working on the test --- ...dminAddUrlRewriteForCmsPageActionGroup.xml | 40 ++++++++ .../Section/AdminUrlRewriteEditSection.xml | 1 + ...AdminDeleteCmsPageUrlRewriteEntityTest.xml | 99 +++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml new file mode 100644 index 0000000000000..f46bd7f5e0386 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddUrlRewriteForCmsPageActionGroup"> + <annotations> + <description>Goes to the Admin Add URL Rewrite edit page. Fills in the provided URL details. Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="cmsPageUrlKey" type="string"/> + <argument name="customUrlRewriteValue" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + + <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustomUrlRewrite"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCsmPage"/> + <waitForPageLoad stepKey="waitForCategoryEditSectionToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.cmsPage('cmsPageUrlKey')}}" stepKey="selectCmsPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminUrlRewriteEditSection.store}}" stepKey="clickOnStore"/> + <click selector="{{AdminUrlRewriteEditSection.storeValue('storeValue')}}" stepKey="clickOnStoreValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> + <click selector="{{AdminUrlRewriteEditSection.redirectType}}" stepKey="selectRedirectType"/> + <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml index 52939607f5377..5a55562e99334 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml @@ -19,6 +19,7 @@ <element name="redirectTypeValue" type="select" selector="//select[@id='redirect_type']//option[contains(., '{{Var}}')]" parameterized="true"/> <element name="description" type="input" selector="#description"/> <element name="categoryInTree" type="text" selector="//li[contains(@class,'active-category jstree-open')]/a[contains(., '{{categoryName}}')]" parameterized="true"/> + <element name="cmsPage" selector="//td[contains(text(), '{{cmsPageUrlKey}}')]" type="button" parameterized="true"/> <element name="saveButton" type="button" selector="#save" timeout="30"/> <element name="deleteButton" type="button" selector="#delete" timeout="30"/> <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']" timeout="30"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml new file mode 100644 index 0000000000000..705786afa83bb --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteEntityTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite"/> + <title value="Delete CMS Page URL rewrite"/> + <description value="Log in to admin and delete CMS Page URL rewrite"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create URL Rewrite for CMS Page with No redirects --> + <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewrite"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="newrequestpath"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="cms_default_no_redirect"/> + </actionGroup> + + <!-- Create URL Rewrite for CMS Page with temporary redirect --> + <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewriteTemporary"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="temporaryrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="cms_default_temporary_redirect"/> + </actionGroup> + + <!-- Create URL Rewrite for CMS Page with permanent redirect --> + <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewritePermanent"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="permanentrequestpath.html"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="cms_default_permanent_redirect"/> + </actionGroup> + + <!--Delete the URL Rewrite for CMS Page with No redirects--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + + <!--Delete the URL Rewrite for CMS Page with with temporary redirect--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteTemporaryUrlRewrite"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFoundSecondTime"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + + <!--Delete the URL Rewrite for CMS Page with permanent redirect--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deletePermanentUrlRewrite"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageThirdTime"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFoundThirdTime"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + + </test> +</tests> From bfd31af036aa66fff3a4fcbe9c1f5ce4f66ec6f3 Mon Sep 17 00:00:00 2001 From: Max Lesechko <mlesechko@magento.com> Date: Wed, 28 Aug 2019 10:45:22 -0500 Subject: [PATCH 0456/2437] MC-19684: Some minute values cannot be set for Analytics data collection --- .../AdminConfigurationTimeToSendDataTest.xml | 6 +-- .../Block/Agreement/Edit/AgreementsForm.xml | 2 +- .../Customer/Test/Block/Form/CustomerForm.php | 2 +- .../Block/Adminhtml/Queue/Edit/QueueForm.xml | 2 +- .../Block/Adminhtml/Sales/Coupons/Filter.xml | 2 +- .../Block/Adminhtml/Sales/TaxRule/Filter.xml | 2 +- .../Adminhtml/Rating/Edit/RatingForm.xml | 2 +- .../Block/Adminhtml/Report/Filter/Form.xml | 2 +- lib/web/mage/validation.js | 45 ------------------- lib/web/mage/validation/validation.js | 22 ++++----- 10 files changed, 18 insertions(+), 69 deletions(-) diff --git a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml index 58e62500b8203..8ebd8cb594bee 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Test/AdminConfigurationTimeToSendDataTest.xml @@ -25,9 +25,9 @@ <amOnPage url="{{AdminConfigGeneralAnalyticsPage.url}}" stepKey="amOnAdminConfig"/> <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingService}}" userInput="Enable" stepKey="selectAdvancedReportingServiceEnabled"/> <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingIndustry}}" userInput="Apps and Games" stepKey="selectAdvancedReportingIndustry"/> - <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingHour}}" userInput="11" stepKey="selectAdvancedReportingHour"/> - <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingMinute}}" userInput="11" stepKey="selectAdvancedReportingMinute"/> - <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingSeconds}}" userInput="00" stepKey="selectAdvancedReportingSeconds"/> + <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingHour}}" userInput="23" stepKey="selectAdvancedReportingHour"/> + <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingMinute}}" userInput="59" stepKey="selectAdvancedReportingMinute"/> + <selectOption selector="{{AdminConfigAdvancedReportingSection.advancedReportingSeconds}}" userInput="59" stepKey="selectAdvancedReportingSeconds"/> <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveConfigButton"/> <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeSuccess"/> </test> diff --git a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml index 95d99f9fa76cd..f98f9ca7cfe24 100644 --- a/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml +++ b/dev/tests/functional/tests/app/Magento/CheckoutAgreements/Test/Block/Adminhtml/Block/Agreement/Edit/AgreementsForm.xml @@ -18,7 +18,7 @@ <input>select</input> </mode> <stores> - <selector>[name="stores[0]"]</selector> + <selector>[name="stores[]"]</selector> <input>multiselectgrouplist</input> </stores> <checkbox_text /> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php index 61166339475b7..dc1e901a3feae 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/Block/Form/CustomerForm.php @@ -29,7 +29,7 @@ class CustomerForm extends Form * * @var string */ - protected $customerAttribute = "[orig-name='%s[]']"; + protected $customerAttribute = "[name='%s[]']"; /** * Validation text message for a field. diff --git a/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml b/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml index 4d2acc76c8703..c1970955013e8 100644 --- a/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml +++ b/dev/tests/functional/tests/app/Magento/Newsletter/Test/Block/Adminhtml/Queue/Edit/QueueForm.xml @@ -11,7 +11,7 @@ <selector>input[name='start_at']</selector> </queue_start_at> <stores> - <selector>select[name="stores[0]"]</selector> + <selector>select[name="stores[]"]</selector> <input>multiselectgrouplist</input> </stores> <newsletter_subject> diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml index 51809448e4edb..d66c3b702f076 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/Coupons/Filter.xml @@ -29,7 +29,7 @@ <input>select</input> </price_rule_type> <order_statuses> - <selector>[name="order_statuses[0]"]</selector> + <selector>[name="order_statuses[]"]</selector> <input>multiselect</input> </order_statuses> <rules_list> diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml index 5820de6772e1c..08e783e1329a4 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Sales/TaxRule/Filter.xml @@ -23,7 +23,7 @@ <input>select</input> </show_order_statuses> <order_statuses> - <selector>[name="order_statuses[0]"]</selector> + <selector>[name="order_statuses[]"]</selector> <input>multiselect</input> </order_statuses> <show_empty_rows> diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml index 504ce64bf2a73..3e1a1c727c668 100644 --- a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml +++ b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Rating/Edit/RatingForm.xml @@ -12,7 +12,7 @@ <strategy>css selector</strategy> <fields> <stores> - <selector>[name="stores[0]"]</selector> + <selector>[name="stores[]"]</selector> <input>multiselectgrouplist</input> </stores> <is_active> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml index 294f64966bde9..d868798eba79d 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Report/Filter/Form.xml @@ -26,7 +26,7 @@ <input>select</input> </show_order_statuses> <order_statuses> - <selector>[name="order_statuses[0]"]</selector> + <selector>[name="order_statuses[]"]</selector> <input>multiselect</input> </order_statuses> <show_actual_columns> diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index b284f0002bc66..55921c054e61a 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -1925,7 +1925,6 @@ * @protected */ _create: function () { - this._prepareArrayInputs(); this.validate = this.element.validate(this.options); // ARIA (adding aria-required attribute) @@ -1938,50 +1937,6 @@ this._listenFormValidate(); }, - /** - * Validation creation. - * - * @protected - */ - _prepareArrayInputs: function () { - /* Store original names for array inputs */ - var originalElements = [], - originalSubmitHandler = this.options.submitHandler; - - /* For all array inputs, assign index so that validation is proper */ - this.element.find('[name$="[]"]').each(function (key, input) { - var originalName, name; - - input = $(input); - originalName = input.attr('name'); - name = originalName.replace('[]', '[' + key + ']'); - $(input).attr('name', name); - $(input).attr('orig-name', originalName); - originalElements.push({ - element: $(input), - name: originalName - }); - }); - - if (originalElements.length) { - /** - * Before submitting the actual form, remove the previously assigned indices - * @param {Object} form - */ - this.options.submitHandler = function (form) { - originalElements.forEach(function (element) { - element.element.attr('name', element.name); - element.element.removeAttr('orig-name'); - }); - - console.error(this.submit); - - /* Call the originalSubmitHandler if it's a function */ - typeof originalSubmitHandler === 'function' ? originalSubmitHandler(form) : form.submit(); - }; - } - }, - /** * Validation listening. * diff --git a/lib/web/mage/validation/validation.js b/lib/web/mage/validation/validation.js index 69cb984b0d82d..74b00e34e9160 100644 --- a/lib/web/mage/validation/validation.js +++ b/lib/web/mage/validation/validation.js @@ -49,23 +49,17 @@ 'validate-one-checkbox-required-by-name': [ function (value, element, params) { var checkedCount = 0, - selector, - container, - origNameSelector, - nameSelector; + container; if (element.type === 'checkbox') { - /* If orig-name attribute is present, use it for validation. Else use name */ - origNameSelector = '[orig-name="' + element.getAttribute('orig-name') + '"]'; - nameSelector = '[name="' + element.name + '"]'; - selector = element.getAttribute('orig-name') ? origNameSelector : nameSelector; - $(selector).each(function () { - if ($(this).is(':checked')) { - checkedCount += 1; - - return false; + $('[name="' + element.name + '"]').each( + function () { + if ($(this).is(':checked')) { + checkedCount += 1; + return false; + } } - }); + ); } container = '#' + params; From 4cfb89e852acd29a676f8bff8c9d614db0c29a83 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 28 Aug 2019 10:51:07 -0500 Subject: [PATCH 0457/2437] MC-18685: Remove custom layout updates from admin --- .../Magento/Catalog/Helper/Product/View.php | 19 ++- .../Attribute/Backend/Customlayoutupdate.php | 20 +++ .../Attribute/Backend/LayoutUpdate.php | 69 +++++++++- .../Attribute/LayoutUpdateManager.php | 128 ++++++++++++++++++ .../Attribute/Source/LayoutUpdate.php | 2 +- .../Catalog/Model/Category/DataProvider.php | 5 + .../Attribute/Backend/LayoutUpdate.php | 67 ++++++++- .../Product/Attribute/LayoutUpdateManager.php | 2 +- .../Product/Attribute/Source/LayoutUpdate.php | 2 +- ...oduct_view_selectable_1_testupdateprod.xml | 25 ++++ .../templates/product/view/rev1.phtml | 8 ++ 11 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php create mode 100644 app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml create mode 100644 app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 74f40a18971d5..98b1cc01c11a7 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Helper\Product; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; +use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Result\Page as ResultPage; /** @@ -66,6 +68,11 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper */ private $string; + /** + * @var LayoutUpdateManager + */ + private $layoutUpdateManager; + /** * Constructor * @@ -78,6 +85,7 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator * @param array $messageGroups * @param \Magento\Framework\Stdlib\StringUtils|null $string + * @param LayoutUpdateManager|null $layoutUpdateManager */ public function __construct( \Magento\Framework\App\Helper\Context $context, @@ -88,7 +96,8 @@ public function __construct( \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, array $messageGroups = [], - \Magento\Framework\Stdlib\StringUtils $string = null + \Magento\Framework\Stdlib\StringUtils $string = null, + ?LayoutUpdateManager $layoutUpdateManager = null ) { $this->_catalogSession = $catalogSession; $this->_catalogDesign = $catalogDesign; @@ -97,8 +106,9 @@ public function __construct( $this->messageGroups = $messageGroups; $this->messageManager = $messageManager; $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; - $this->string = $string ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Stdlib\StringUtils::class); + $this->string = $string ?: ObjectManager::getInstance()->get(\Magento\Framework\Stdlib\StringUtils::class); + $this->layoutUpdateManager = $layoutUpdateManager + ?? ObjectManager::getInstance()->get(LayoutUpdateManager::class); parent::__construct($context); } @@ -204,6 +214,9 @@ public function initProductLayout(ResultPage $resultPage, $product, $params = nu } } + //Apply selected layout update + $this->layoutUpdateManager->applyUpdate($resultPage, $product); + $currentCategory = $this->_coreRegistry->registry('current_category'); $controllerClass = $this->_request->getFullActionName(); if ($controllerClass != 'catalog-product-view') { diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index a994446881189..2dc31e480055c 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\Attribute\Backend; +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Category; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; use Magento\Eav\Model\Entity\Attribute\Exception; @@ -63,4 +66,21 @@ public function validate($object) } return true; } + + /** + * @inheritDoc + * @param AbstractModel $object + * @throws LocalizedException + */ + public function beforeSave($object) + { + $attributeName = $this->getAttribute()->getName(); + if ($object->getData($attributeName) + && $object->getOrigData($attributeName) !== $object->getData($attributeName) + ) { + throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); + } + + return parent::beforeSave($object); + } } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index c34bddbe11d33..3447c91043b8e 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -10,12 +10,65 @@ use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; +use Magento\Framework\Exception\LocalizedException; /** - * Allows to select a layout file to merge when rendering a category's page. + * Allows to select a layout file to merge when rendering the category's page. */ class LayoutUpdate extends AbstractBackend { + private const VALUE_USE_UPDATE_XML = '__existing__'; + + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * Extracts the attributes value from given entity. + * + * @throws LocalizedException + * @param Category $category + * @return string|null + */ + private function extractValue(Category $category): ?string + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $value = $category->getData($attrCode); + if ($value + && $value !== self::VALUE_USE_UPDATE_XML + && !in_array($value, $this->manager->fetchAvailableFiles($category), true) + ) { + throw new LocalizedException(__('Selected layout update is not available')); + } + if (!$value) { + $value = null; + } + + return $value; + } + + /** + * Set value for the object. + * + * @param string|null $value + * @param Category $object + */ + private function setValue(?string $value, Category $object): void + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $object->setData($attrCode, $value); + } + /** * @inheritDoc * @param Category $object @@ -23,7 +76,9 @@ class LayoutUpdate extends AbstractBackend public function validate($object) { $valid = parent::validate($object); - + if ($valid) { + $this->extractValue($object); + } return $valid; } @@ -31,10 +86,18 @@ public function validate($object) /** * @inheritDoc * @param Category $object + * @throws LocalizedException */ public function beforeSave($object) { - parent::beforeSave($object); + $value = $this->extractValue($object); + if ($value !== self::VALUE_USE_UPDATE_XML) { + $object->setCustomAttribute('custom_layout_update', null); + $object->setData('custom_layout_update', null); + } else { + $value = null; + } + $this->setValue($value, $object); return $this; } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php new file mode 100644 index 0000000000000..6bcf0ea0f442e --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Framework\App\Area; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; +use Magento\Framework\View\Result\Page as PageLayout; + +/** + * Manage available layout updates for categories. + */ +class LayoutUpdateManager +{ + + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + * @param LayoutProcessorFactory $layoutProcessorFactory + */ + public function __construct( + FlyweightFactory $themeFactory, + DesignInterface $design, + LayoutProcessorFactory $layoutProcessorFactory + ) { + $this->themeFactory = $themeFactory; + $this->design = $design; + $this->layoutProcessorFactory = $layoutProcessorFactory; + } + + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + + /** + * Fetch list of available files/handles for the category. + * + * @param CategoryInterface $category + * @return string[] + */ + public function fetchAvailableFiles(CategoryInterface $category): array + { + $handles = $this->getLayoutProcessor()->getAvailableHandles(); + + return array_filter( + array_map( + function (string $handle) use ($category) : ?string { + preg_match( + '/^catalog\_category\_view\_selectable\_' .$category->getId() .'\_([a-z0-9]+)/i', + $handle, + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $handles + ) + ); + } + + /** + * Apply selected custom layout updates. + * + * If no update is selected none will apply. + * + * @param PageLayout $layout + * @param CategoryInterface $category + * @return void + */ + public function applyUpdate(PageLayout $layout, CategoryInterface $category): void + { + if ($attribute = $category->getCustomAttribute('custom_layout_update_file')) { + $layout->addPageLayoutHandles( + ['selectable' => $category->getId() . '_' . $attribute->getValue()] + ); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php index 2030e05b74925..f8281983df777 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -22,7 +22,7 @@ class LayoutUpdate extends AbstractSource implements SpecificSourceInterface */ public function getAllOptions() { - $options = [['label' => 'Use default', 'value' => '']]; + $options = [['label' => 'No update', 'value' => '']]; return $options; } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index c96b2aae36059..719e49449730f 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -535,6 +535,11 @@ private function convertValues($category, $categoryData) $categoryData[$attributeCode][0]['type'] = $mime; } } + if ($attributeCode === 'custom_layout_update_file') { + if (!empty($categoryData['custom_layout_update'])) { + $categoryData['custom_layout_update_file'] = '__existing__'; + } + } } return $categoryData; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index d8c9b7e2ae59f..6841900b30033 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -10,12 +10,65 @@ use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; +use Magento\Framework\Exception\LocalizedException; /** * Allows to select a layout file to merge when rendering the product's page. */ class LayoutUpdate extends AbstractBackend { + private const VALUE_USE_UPDATE_XML = '__existing__'; + + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * Extracts the attributes value from given entity. + * + * @throws LocalizedException + * @param Product $product + * @return string|null + */ + private function extractValue(Product $product): ?string + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $value = $product->getData($attrCode); + if ($value + && $value !== self::VALUE_USE_UPDATE_XML + && !in_array($value, $this->manager->fetchAvailableFiles($product), true) + ) { + throw new LocalizedException(__('Selected layout update is not available')); + } + if (!$value) { + $value = null; + } + + return $value; + } + + /** + * Set value for the object. + * + * @param string|null $value + * @param Product $object + */ + private function setValue(?string $value, Product $object): void + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $object->setData($attrCode, $value); + } + /** * @inheritDoc * @param Product $object @@ -23,7 +76,9 @@ class LayoutUpdate extends AbstractBackend public function validate($object) { $valid = parent::validate($object); - + if ($valid) { + $this->extractValue($object); + } return $valid; } @@ -31,10 +86,18 @@ public function validate($object) /** * @inheritDoc * @param Product $object + * @throws LocalizedException */ public function beforeSave($object) { - parent::beforeSave($object); + $value = $this->extractValue($object); + if ($value !== self::VALUE_USE_UPDATE_XML) { + $object->setCustomAttribute('custom_layout_update', null); + $object->setData('custom_layout_update', null); + } else { + $value = null; + } + $this->setValue($value, $object); return $this; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php index c0c0c444c6b8b..9fcd2c2c4c4d3 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -133,7 +133,7 @@ public function applyUpdate(PageLayout $layout, ProductInterface $product): void { if ($attribute = $product->getCustomAttribute('custom_layout_update_file')) { $layout->addPageLayoutHandles( - ['selectable' => $this->sanitizeIdentifier($product) . '_' . $attribute->getValue()] + ['selectable' => $this->sanitizeSku($product) . '_' . $attribute->getValue()] ); } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php index d63e77286498b..d793e110ac25e 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -42,7 +42,7 @@ public function __construct(LayoutUpdateManager $manager) public function getAllOptions() { $default = ''; - $defaultText = 'Use default'; + $defaultText = 'No update'; $this->optionsText[$default] = $defaultText; return [['label' => $defaultText, 'value' => $default]]; diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml new file mode 100644 index 0000000000000..538ba470927f2 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <head><title>TEST DDD + + + + + + TEST 123 + + + + + TESTY!!! + + + + + diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml new file mode 100644 index 0000000000000..ba933509b96a9 --- /dev/null +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml @@ -0,0 +1,8 @@ + + +
test my dust
From d617c3ca6541d1528bf26c26738b909d55424def Mon Sep 17 00:00:00 2001 From: Oleksii Lisovyi Date: Wed, 28 Aug 2019 18:51:39 +0300 Subject: [PATCH 0458/2437] Sales - fix displaying available allowed countries while editing order address in Admin panel --- .../Block/Adminhtml/Order/Address/Form.php | 8 +++++ .../Adminhtml/Order/Create/Form/Address.php | 31 +++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php index 12e59e63f6f7c..1efa149b390ef 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php @@ -136,4 +136,12 @@ public function getFormValues() { return $this->_getAddress()->getData(); } + + /** + * @inheritDoc + */ + protected function getAddressStoreId() + { + return $this->_getAddress()->getOrder()->getStoreId(); + } } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index e4b9dd4c63b93..3fe943c1b194c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -271,21 +271,24 @@ protected function _prepareForm() $this->_form->setValues($this->getFormValues()); - if ($this->_form->getElement('country_id')->getValue()) { - $countryId = $this->_form->getElement('country_id')->getValue(); - $this->_form->getElement('country_id')->setValue(null); - foreach ($this->_form->getElement('country_id')->getValues() as $country) { + $countryElement = $this->_form->getElement('country_id'); + + $this->processCountryOptions($countryElement); + + if ($countryElement->getValue()) { + $countryId = $countryElement->getValue(); + $countryElement->setValue(null); + foreach ($countryElement->getValues() as $country) { if ($country['value'] == $countryId) { - $this->_form->getElement('country_id')->setValue($countryId); + $countryElement->setValue($countryId); } } } - if ($this->_form->getElement('country_id')->getValue() === null) { - $this->_form->getElement('country_id')->setValue( + if ($countryElement->getValue() === null) { + $countryElement->setValue( $this->directoryHelper->getDefaultCountry($this->getStore()) ); } - $this->processCountryOptions($this->_form->getElement('country_id')); // Set custom renderer for VAT field if needed $vatIdElement = $this->_form->getElement('vat_id'); if ($vatIdElement && $this->getDisplayVatValidationButton() !== false) { @@ -309,7 +312,7 @@ protected function _prepareForm() */ private function processCountryOptions(\Magento\Framework\Data\Form\Element\AbstractElement $countryElement) { - $storeId = $this->getBackendQuoteSession()->getStoreId(); + $storeId = $this->getAddressStoreId(); $options = $this->getCountriesCollection() ->loadByStore($storeId) ->toOptionArray(); @@ -388,4 +391,14 @@ public function getAddressAsString(\Magento\Customer\Api\Data\AddressInterface $ return $this->escapeHtml($result); } + + /** + * Return address store id. + * + * @return int + */ + protected function getAddressStoreId() + { + return $this->getBackendQuoteSession()->getStoreId(); + } } From eb566be48767ab20e0ba0ee913d68011348eb28d Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun Date: Wed, 28 Aug 2019 10:52:21 -0500 Subject: [PATCH 0459/2437] MC-18685: Remove custom layout updates from admin --- ...oduct_view_selectable_1_testupdateprod.xml | 25 ------------------- .../templates/product/view/rev1.phtml | 8 ------ 2 files changed, 33 deletions(-) delete mode 100644 app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml delete mode 100644 app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml deleted file mode 100644 index 538ba470927f2..0000000000000 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view_selectable_1_testupdateprod.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - TEST DDD - - - - - - TEST 123 - - - - - TESTY!!! - - - - - diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml deleted file mode 100644 index ba933509b96a9..0000000000000 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/rev1.phtml +++ /dev/null @@ -1,8 +0,0 @@ - - -
test my dust
From eba1d8dfff69a3c4eef6fa63e593b5f384adda27 Mon Sep 17 00:00:00 2001 From: Max Lesechko Date: Wed, 28 Aug 2019 11:41:53 -0500 Subject: [PATCH 0460/2437] MC-19684: Some minute values cannot be set for Analytics data collection --- lib/web/mage/validation/validation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/validation/validation.js b/lib/web/mage/validation/validation.js index 74b00e34e9160..0f2c4c06b119b 100644 --- a/lib/web/mage/validation/validation.js +++ b/lib/web/mage/validation/validation.js @@ -56,6 +56,7 @@ function () { if ($(this).is(':checked')) { checkedCount += 1; + return false; } } From 4674e41ea82a9e51bfb8b56c76a3533446d1c310 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma Date: Wed, 28 Aug 2019 21:12:41 +0300 Subject: [PATCH 0461/2437] Convert DeleteCmsPageUrlRewriteEntityTest to MFTF --- .../Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml index 8262d88bdff1a..ea9d49d662bdd 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml @@ -8,20 +8,20 @@ - severity:S2 + severity:S2,mftf_migrated:yes cms_default_no_redirect - severity:S2 + severity:S2,mftf_migrated:yes cms_default_permanent_redirect - severity:S2 + severity:S2,mftf_migrated:yes cms_default_temporary_redirect From eca0a6aeed6237d625e0f30f7aad7e2ddc0a23bc Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun Date: Wed, 28 Aug 2019 13:35:48 -0500 Subject: [PATCH 0462/2437] MC-18685: Remove custom layout updates from admin --- .../Catalog/Controller/Category/View.php | 17 +++++- .../Magento/Catalog/Helper/Product/View.php | 6 +-- .../Attribute/LayoutUpdateManager.php | 12 +++-- .../Attribute/Source/LayoutUpdate.php | 53 +++++++++++++++++-- .../Catalog/Model/Category/DataProvider.php | 13 ++++- app/code/Magento/Catalog/Model/Design.php | 39 +++++++++++--- .../Product/Attribute/LayoutUpdateManager.php | 13 +++-- .../Catalog/Model/ResourceModel/Category.php | 2 + .../Data/UpdateCustomLayoutAttributes.php | 14 +++++ .../adminhtml/ui_component/category_form.xml | 9 ++++ 10 files changed, 155 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index da3d99a8d2745..dff39008b539a 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -28,6 +28,7 @@ use Magento\Framework\View\Result\PageFactory; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; /** * View a category on storefront. Needs to be accessible by POST because of the store switching. @@ -94,6 +95,11 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter */ private $toolbarMemorizer; + /** + * @var LayoutUpdateManager + */ + private $customLayoutManager; + /** * Constructor * @@ -108,6 +114,7 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository * @param ToolbarMemorizer|null $toolbarMemorizer + * @param LayoutUpdateManager|null $layoutUpdateManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -121,7 +128,8 @@ public function __construct( ForwardFactory $resultForwardFactory, Resolver $layerResolver, CategoryRepositoryInterface $categoryRepository, - ToolbarMemorizer $toolbarMemorizer = null + ToolbarMemorizer $toolbarMemorizer = null, + ?LayoutUpdateManager $layoutUpdateManager = null ) { parent::__construct($context); $this->_storeManager = $storeManager; @@ -134,6 +142,8 @@ public function __construct( $this->layerResolver = $layerResolver; $this->categoryRepository = $categoryRepository; $this->toolbarMemorizer = $toolbarMemorizer ?: $context->getObjectManager()->get(ToolbarMemorizer::class); + $this->customLayoutManager = $layoutUpdateManager + ?? $context->getObjectManager()->get(LayoutUpdateManager::class); } /** @@ -258,5 +268,10 @@ private function applyLayoutUpdates( $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false); } } + + //Selected files + if ($settings->getPageLayoutHandles()) { + $page->addPageLayoutHandles($settings->getPageLayoutHandles()); + } } } diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 98b1cc01c11a7..26776500438e5 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -213,9 +213,9 @@ public function initProductLayout(ResultPage $resultPage, $product, $params = nu } } } - - //Apply selected layout update - $this->layoutUpdateManager->applyUpdate($resultPage, $product); + if ($settings->getPageLayoutHandles()) { + $resultPage->addPageLayoutHandles($settings->getPageLayoutHandles()); + } $currentCategory = $this->_coreRegistry->registry('current_category'); $controllerClass = $this->_request->getFullActionName(); diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php index 6bcf0ea0f442e..32d6068446a65 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Framework\App\Area; +use Magento\Framework\DataObject; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; @@ -109,20 +110,23 @@ function (string $handle) use ($category) : ?string { } /** - * Apply selected custom layout updates. + * Extract selected custom layout settings. * * If no update is selected none will apply. * - * @param PageLayout $layout * @param CategoryInterface $category + * @param DataObject $intoSettings * @return void */ - public function applyUpdate(PageLayout $layout, CategoryInterface $category): void + public function extractCustomSettings(CategoryInterface $category, DataObject $intoSettings): void { if ($attribute = $category->getCustomAttribute('custom_layout_update_file')) { - $layout->addPageLayoutHandles( + $handles = $intoSettings->getPageLayoutHandles() ?? []; + $handles = array_merge_recursive( + $handles, ['selectable' => $category->getId() . '_' . $attribute->getValue()] ); + $intoSettings->setPageLayoutHandles($handles); } } } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php index f8281983df777..2375a5d36a0a8 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -8,6 +8,8 @@ namespace Magento\Catalog\Model\Category\Attribute\Source; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Framework\Api\CustomAttributesDataInterface; @@ -17,21 +19,66 @@ */ class LayoutUpdate extends AbstractSource implements SpecificSourceInterface { + /** + * @var string[] + */ + private $optionsText; + + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + /** * @inheritDoc */ public function getAllOptions() { - $options = [['label' => 'No update', 'value' => '']]; + $default = ''; + $defaultText = 'No update'; + $this->optionsText[$default] = $defaultText; - return $options; + return [['label' => $defaultText, 'value' => $default]]; + } + + /** + * @inheritDoc + */ + public function getOptionText($value) + { + if (is_scalar($value) && array_key_exists($value, $this->optionsText)) { + return $this->optionsText[$value]; + } + + return false; } /** * @inheritDoc + * @param CategoryInterface $entity */ public function getOptionsFor(CustomAttributesDataInterface $entity): array { - return $this->getAllOptions(); + $options = $this->getAllOptions(); + if ($entity->getCustomAttribute('custom_layout_update')) { + $existingValue = '__existing__'; + $existingLabel = 'Use existing'; + $options[] = ['label' => $existingLabel, 'value' => $existingValue]; + $this->optionsText[$existingValue] = $existingLabel; + } + foreach ($this->manager->fetchAvailableFiles($entity) as $handle) { + $options[] = ['label' => $handle, 'value' => $handle]; + $this->optionsText[$handle] = $handle; + } + + return $options; } } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 719e49449730f..cff85d6060e34 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -15,6 +15,7 @@ use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Eav\Model\Entity\Type; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; @@ -363,7 +364,16 @@ public function getAttributesMeta(Type $entityType) : $value; } if ($attribute->usesSource()) { - $meta[$code]['options'] = $attribute->getSource()->getAllOptions(); + $source = $attribute->getSource(); + if ($source instanceof SpecificSourceInterface) { + $options = $source->getOptionsFor($this->getCurrentCategory()); + } else { + $options = $attribute->getSource()->getAllOptions(); + } + foreach ($options as &$option) { + $option['__disableTmpl'] = true; + } + $meta[$code]['options'] = $options; } } @@ -609,6 +619,7 @@ protected function getFieldsMap() 'custom_design', 'page_layout', 'custom_layout_update', + 'custom_layout_update_file' ], 'schedule_design_update' => [ 'custom_design_from', diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php index 853bbeac8eb38..854f8f5648926 100644 --- a/app/code/Magento/Catalog/Model/Design.php +++ b/app/code/Magento/Catalog/Model/Design.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager as CategoryLayoutManager; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager as ProductLayoutManager; +use Magento\Framework\App\ObjectManager; use \Magento\Framework\TranslateInterface; /** @@ -38,6 +41,16 @@ class Design extends \Magento\Framework\Model\AbstractModel */ private $translator; + /** + * @var CategoryLayoutManager + */ + private $categoryLayoutUpdates; + + /** + * @var ProductLayoutManager + */ + private $productLayoutUpdates; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -47,6 +60,8 @@ class Design extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param TranslateInterface|null $translator + * @param CategoryLayoutManager|null $categoryLayoutManager + * @param ProductLayoutManager|null $productLayoutManager */ public function __construct( \Magento\Framework\Model\Context $context, @@ -56,12 +71,17 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - TranslateInterface $translator = null + TranslateInterface $translator = null, + ?CategoryLayoutManager $categoryLayoutManager = null, + ?ProductLayoutManager $productLayoutManager = null ) { $this->_localeDate = $localeDate; $this->_design = $design; - $this->translator = $translator ?: - \Magento\Framework\App\ObjectManager::getInstance()->get(TranslateInterface::class); + $this->translator = $translator ?? ObjectManager::getInstance()->get(TranslateInterface::class); + $this->categoryLayoutUpdates = $categoryLayoutManager + ?? ObjectManager::getInstance()->get(CategoryLayoutManager::class); + $this->productLayoutUpdates = $productLayoutManager + ?? ObjectManager::getInstance()->get(ProductLayoutManager::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -81,12 +101,12 @@ public function applyCustomDesign($design) /** * Get custom layout settings * - * @param \Magento\Catalog\Model\Category|\Magento\Catalog\Model\Product $object + * @param Category|Product $object * @return \Magento\Framework\DataObject */ public function getDesignSettings($object) { - if ($object instanceof \Magento\Catalog\Model\Product) { + if ($object instanceof Product) { $currentCategory = $object->getCategory(); } else { $currentCategory = $object; @@ -97,7 +117,7 @@ public function getDesignSettings($object) $category = $currentCategory->getParentDesignCategory($currentCategory); } - if ($object instanceof \Magento\Catalog\Model\Product) { + if ($object instanceof Product) { if ($category && $category->getCustomApplyToProducts()) { return $this->_mergeSettings($this->_extractSettings($category), $this->_extractSettings($object)); } else { @@ -111,7 +131,7 @@ public function getDesignSettings($object) /** * Extract custom layout settings from category or product object * - * @param \Magento\Catalog\Model\Category|\Magento\Catalog\Model\Product $object + * @param Category|Product $object * @return \Magento\Framework\DataObject */ protected function _extractSettings($object) @@ -140,6 +160,11 @@ protected function _extractSettings($object) )->setLayoutUpdates( (array)$object->getCustomLayoutUpdate() ); + if ($object instanceof Category) { + $this->categoryLayoutUpdates->extractCustomSettings($object, $settings); + } elseif ($object instanceof Product) { + $this->productLayoutUpdates->extractCustomSettings($object, $settings); + } } return $settings; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php index 9fcd2c2c4c4d3..bcf655b25299c 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -8,8 +8,10 @@ namespace Magento\Catalog\Model\Product\Attribute; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\Area; +use Magento\Framework\DataObject; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; @@ -121,20 +123,23 @@ function (string $handle) use ($identifier) : ?string { } /** - * Apply selected custom layout updates. + * Extract selected custom layout settings. * * If no update is selected none will apply. * - * @param PageLayout $layout * @param ProductInterface $product + * @param DataObject $intoSettings * @return void */ - public function applyUpdate(PageLayout $layout, ProductInterface $product): void + public function extractCustomSettings(ProductInterface $product, DataObject $intoSettings): void { if ($attribute = $product->getCustomAttribute('custom_layout_update_file')) { - $layout->addPageLayoutHandles( + $handles = $intoSettings->getPageLayoutHandles() ?? []; + $handles = array_merge_recursive( + $handles, ['selectable' => $this->sanitizeSku($product) . '_' . $attribute->getValue()] ); + $intoSettings->setPageLayoutHandles($handles); } } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 786cec391c460..bc414bf07a115 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -758,6 +758,8 @@ public function getParentDesignCategory($category) 'custom_layout_update' )->addAttributeToSelect( 'custom_apply_to_products' + )->addAttributeToSelect( + 'custom_layout_update_file' )->addFieldToFilter( 'entity_id', ['in' => $pathIds] diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php index 4809316d8ff0e..386d9b246782d 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php @@ -103,5 +103,19 @@ public function apply() 'is_filterable_in_grid' => false ] ); + + $eavSetup->updateAttribute( + Product::ENTITY, + 'custom_layout_update', + 'visible', + false + ); + + $eavSetup->updateAttribute( + Category::ENTITY, + 'custom_layout_update', + 'visible', + false + ); } } diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 992093c4a6658..c3a0457bb848a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -489,6 +489,15 @@ + + + string + + + ${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled + + + From b7eef40961df7b2cc7fbcbaac50a4785349026ae Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun Date: Wed, 28 Aug 2019 13:59:52 -0500 Subject: [PATCH 0463/2437] MC-18685: Remove custom layout updates from admin --- .../Adminhtml/Category/Authorization.php | 82 +++++++++++++++++++ .../Controller/Adminhtml/Category/Save.php | 37 +++++---- app/code/Magento/Catalog/Model/Category.php | 68 +-------------- .../Observer/CategoryDesignAuthorization.php | 44 ++++++++++ .../Catalog/Plugin/CategoryAuthorization.php | 48 +++++++++++ app/code/Magento/Catalog/etc/events.xml | 3 + .../Magento/Catalog/etc/webapi_rest/di.xml | 3 + .../Magento/Catalog/etc/webapi_soap/di.xml | 3 + 8 files changed, 203 insertions(+), 85 deletions(-) create mode 100644 app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php create mode 100644 app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php create mode 100644 app/code/Magento/Catalog/Plugin/CategoryAuthorization.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php new file mode 100644 index 0000000000000..a062189628789 --- /dev/null +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php @@ -0,0 +1,82 @@ +authorization = $authorization; + $this->categoryFactory = $factory; + } + + /** + * Authorize saving of a category. + * + * @throws AuthorizationException + * @throws NoSuchEntityException When a category with invalid ID given. + * @param CategoryInterface|Category $category + * @return void + */ + public function authorizeSavingOf(CategoryInterface $category): void + { + if (!$this->authorization->isAllowed('Magento_Catalog::edit_category_design')) { + $notAllowed = false; + if (!$category->getId()) { + foreach (array_keys($category->getDesignAttributes()) as $attribute) { + if ($category->getData($attribute)) { + $notAllowed = true; + break; + } + } + } else { + /** @var Category $savedCategory */ + $savedCategory = $this->categoryFactory->create(); + $savedCategory->load($category->getId()); + if ($savedCategory->getName()) { + throw NoSuchEntityException::singleField('id', $category->getId()); + } + foreach (array_keys($category->getDesignAttributes()) as $attribute) { + if ($category->getData($attribute) != $savedCategory->getData($attribute)) { + $notAllowed = true; + break; + } + } + } + + if ($notAllowed) { + throw new AuthorizationException(__('Not allowed to edit the category\'s design attributes')); + } + } + } +} diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 77518fd9bf5cc..485e8e69e4bff 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -173,29 +173,30 @@ public function execute() $products = json_decode($categoryPostData['category_products'], true); $category->setPostedProducts($products); } - $this->_eventManager->dispatch( - 'catalog_category_prepare_save', - ['category' => $category, 'request' => $this->getRequest()] - ); - /** - * Check "Use Default Value" checkboxes values - */ - if (isset($categoryPostData['use_default']) && !empty($categoryPostData['use_default'])) { - foreach ($categoryPostData['use_default'] as $attributeCode => $attributeValue) { - if ($attributeValue) { - $category->setData($attributeCode, null); + try { + $this->_eventManager->dispatch( + 'catalog_category_prepare_save', + ['category' => $category, 'request' => $this->getRequest()] + ); + + /** + * Check "Use Default Value" checkboxes values + */ + if (isset($categoryPostData['use_default']) && !empty($categoryPostData['use_default'])) { + foreach ($categoryPostData['use_default'] as $attributeCode => $attributeValue) { + if ($attributeValue) { + $category->setData($attributeCode, null); + } } } - } - /** - * Proceed with $_POST['use_config'] - * set into category model for processing through validation - */ - $category->setData('use_post_data_config', $useConfig); + /** + * Proceed with $_POST['use_config'] + * set into category model for processing through validation + */ + $category->setData('use_post_data_config', $useConfig); - try { $categoryResource = $category->getResource(); if ($category->hasCustomDesignTo()) { $categoryResource->getAttribute('custom_design_from')->setMaxValue($category->getCustomDesignTo()); diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index 773ef9d536e01..d3c25b74acc41 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -5,13 +5,10 @@ */ namespace Magento\Catalog\Model; -use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\Api\AttributeValueFactory; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\AuthorizationInterface; use Magento\Framework\Convert\ConvertArray; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Profiler; @@ -131,6 +128,7 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements 'page_layout', 'custom_layout_update', 'custom_apply_to_products', + 'custom_layout_update_file' ]; /** @@ -214,16 +212,6 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements */ protected $metadataService; - /** - * @var UserContextInterface - */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; - /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -933,60 +921,6 @@ public function beforeDelete() return parent::beforeDelete(); } - /** - * Get user context. - * - * @return UserContextInterface - */ - private function getUserContext(): UserContextInterface - { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); - } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); - } - - return $this->authorization; - } - - /** - * @inheritDoc - */ - public function beforeSave() - { - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Catalog::edit_category_design') - ) { - foreach ($this->_designAttributes as $attributeCode) { - $this->setData($attributeCode, $value = $this->getOrigData($attributeCode)); - if (!empty($this->_data[self::CUSTOM_ATTRIBUTES]) - && array_key_exists($attributeCode, $this->_data[self::CUSTOM_ATTRIBUTES])) { - //In case custom attribute were used to update the entity. - $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]->setValue($value); - } - } - } - - return parent::beforeSave(); - } - /** * Retrieve anchors above * diff --git a/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php new file mode 100644 index 0000000000000..b6486bd9256b1 --- /dev/null +++ b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php @@ -0,0 +1,44 @@ +authorization = $authorization; + } + + /** + * @inheritDoc + * @throws AuthorizationException + */ + public function execute(Observer $observer) + { + /** @var CategoryInterface $category */ + $category = $observer->getEvent()->getData('category'); + $this->authorization->authorizeSavingOf($category); + } +} diff --git a/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php new file mode 100644 index 0000000000000..1e4f2ddc38b82 --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php @@ -0,0 +1,48 @@ +authorization = $authorization; + } + + /** + * Authorize saving of a category. + * + * @param CategoryRepositoryInterface $subject + * @param CategoryInterface $category + * @throws LocalizedException + * @return array + */ + public function beforeSave(CategoryRepositoryInterface $subject, CategoryInterface $category): array + { + $this->authorization->authorizeSavingOf($category); + + return [$category]; + } +} diff --git a/app/code/Magento/Catalog/etc/events.xml b/app/code/Magento/Catalog/etc/events.xml index 5bcdc88369064..6bc2311dddd27 100644 --- a/app/code/Magento/Catalog/etc/events.xml +++ b/app/code/Magento/Catalog/etc/events.xml @@ -63,4 +63,7 @@ + + + diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index 4a7d2c7481576..bfbc05b12079d 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -25,4 +25,7 @@ + + + diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index 4a7d2c7481576..bfbc05b12079d 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -25,4 +25,7 @@ + + + From 892cfc9285df75e42531c527636c1ce33ca23c40 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun Date: Wed, 28 Aug 2019 16:10:57 -0500 Subject: [PATCH 0464/2437] MC-18685: Remove custom layout updates from admin --- .../Catalog/Controller/Adminhtml/Category.php | 8 +- .../Adminhtml/Category/Authorization.php | 13 +- .../Controller/Adminhtml/Category/Save.php | 10 +- .../Adminhtml/Product/Authorization.php | 2 +- app/code/Magento/Catalog/Model/Category.php | 2 +- .../Catalog/Api/CategoryRepositoryTest.php | 162 +++++++++++++++++- .../Controller/Adminhtml/CategoryTest.php | 91 +++++++++- 7 files changed, 269 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php index 1e0cb9f197a51..61fd714d518ba 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php @@ -71,8 +71,12 @@ protected function _initCategory($getRootInstead = false) } } - $this->_objectManager->get(\Magento\Framework\Registry::class)->register('category', $category); - $this->_objectManager->get(\Magento\Framework\Registry::class)->register('current_category', $category); + /** @var \Magento\Framework\Registry $registry */ + $registry = $this->_objectManager->get(\Magento\Framework\Registry::class); + $registry->unregister('category'); + $registry->unregister('current_category'); + $registry->register('category', $category); + $registry->register('current_category', $category); $this->_objectManager->get(\Magento\Cms\Model\Wysiwyg\Config::class) ->setStoreId($storeId); return $category; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php index a062189628789..023839a16ee89 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\CategoryFactory; +use Magento\Eav\Api\Data\AttributeInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; use Magento\Framework\Exception\NoSuchEntityException; @@ -52,8 +53,14 @@ public function authorizeSavingOf(CategoryInterface $category): void { if (!$this->authorization->isAllowed('Magento_Catalog::edit_category_design')) { $notAllowed = false; + $designAttributeCodes = array_map( + function (AttributeInterface $attribute) { + return $attribute->getAttributeCode(); + }, + $category->getDesignAttributes() + ); if (!$category->getId()) { - foreach (array_keys($category->getDesignAttributes()) as $attribute) { + foreach ($designAttributeCodes as $attribute) { if ($category->getData($attribute)) { $notAllowed = true; break; @@ -63,10 +70,10 @@ public function authorizeSavingOf(CategoryInterface $category): void /** @var Category $savedCategory */ $savedCategory = $this->categoryFactory->create(); $savedCategory->load($category->getId()); - if ($savedCategory->getName()) { + if (!$savedCategory->getName()) { throw NoSuchEntityException::singleField('id', $category->getId()); } - foreach (array_keys($category->getDesignAttributes()) as $attribute) { + foreach ($designAttributeCodes as $attribute) { if ($category->getData($attribute) != $savedCategory->getData($attribute)) { $notAllowed = true; break; diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 485e8e69e4bff..95ef5e1279e1e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -175,11 +175,6 @@ public function execute() } try { - $this->_eventManager->dispatch( - 'catalog_category_prepare_save', - ['category' => $category, 'request' => $this->getRequest()] - ); - /** * Check "Use Default Value" checkboxes values */ @@ -191,6 +186,11 @@ public function execute() } } + $this->_eventManager->dispatch( + 'catalog_category_prepare_save', + ['category' => $category, 'request' => $this->getRequest()] + ); + /** * Proceed with $_POST['use_config'] * set into category model for processing through validation diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php index d49c499930022..d1ee659ce8175 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php @@ -66,7 +66,7 @@ public function authorizeSavingOf(ProductInterface $product): void /** @var Product $savedProduct */ $savedProduct = $this->productFactory->create(); $savedProduct->load($product->getId()); - if ($savedProduct->getSku()) { + if (!$savedProduct->getSku()) { throw NoSuchEntityException::singleField('id', $product->getId()); } if ($product->getData('custom_design') != $savedProduct->getData('custom_design') diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index d3c25b74acc41..3e8df99dcda84 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -749,7 +749,7 @@ public function getCustomDesignDate() /** * Retrieve design attributes array * - * @return array + * @return \Magento\Eav\Api\Data\AttributeInterface[] */ public function getDesignAttributes() { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 332e509d550ac..7e010d7631eed 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -6,10 +6,15 @@ */ namespace Magento\Catalog\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; class CategoryRepositoryTest extends WebapiAbstract { @@ -18,6 +23,33 @@ class CategoryRepositoryTest extends WebapiAbstract private $modelId = 333; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/category_backend.php */ @@ -57,8 +89,10 @@ public function testInfoNoSuchEntityException() } /** + * Load category data. + * * @param int $id - * @return string + * @return array */ protected function getInfoCategory($id) { @@ -209,10 +243,11 @@ protected function getSimpleCategoryData($categoryData = []) /** * Create category process * - * @param $category - * @return int + * @param array $category + * @param string|null $token + * @return array */ - protected function createCategory($category) + protected function createCategory(array $category, ?string $token = null) { $serviceInfo = [ 'rest' => [ @@ -225,6 +260,9 @@ protected function createCategory($category) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['category' => $category]; return $this->_webApiCall($serviceInfo, $requestData); } @@ -251,7 +289,15 @@ protected function deleteCategory($id) return $this->_webApiCall($serviceInfo, ['categoryId' => $id]); } - protected function updateCategory($id, $data) + /** + * Update given category via web API. + * + * @param int $id + * @param array $data + * @param string|null $token + * @return array + */ + protected function updateCategory($id, $data, ?string $token = null) { $serviceInfo = [ @@ -265,6 +311,7 @@ protected function updateCategory($id, $data) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { $data['id'] = $id; @@ -272,7 +319,110 @@ protected function updateCategory($id, $data) } else { $data['id'] = $id; return $this->_webApiCall($serviceInfo, ['id' => $id, 'category' => $data]); - return $this->_webApiCall($serviceInfo, ['category' => $data]); } } + + /** + * Test design settings authorization + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving categories but not their design settings. + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load('test_custom_role', 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::categories']); + $rules->saveRel(); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $categoryData = $this->getSimpleCategoryData(); + $categoryData['custom_attributes'][] = ['attribute_code' => 'custom_layout_update_file', 'value' => 'test']; + + //Creating new category with design settings. + $exceptionMessage = null; + try { + $this->createCategory($categoryData, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have the permissions. + $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); + + //Updating the user role to allow access to design properties. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + $rules->saveRel(); + //Making the same request with design settings. + $categoryData = $this->getSimpleCategoryData(); + foreach ($categoryData['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] === 'custom_design') { + $attribute['value'] = 'test'; + break; + } + } + $result = $this->createCategory($categoryData, $token); + $this->assertArrayHasKey('id', $result); + //Category must be saved. + $categorySaved = $this->getInfoCategory($result['id']); + $savedCustomDesign = null; + foreach ($categorySaved['custom_attributes'] as $customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $savedCustomDesign = $customAttribute['value']; + break; + } + } + $this->assertEquals('test', $savedCustomDesign); + $categoryData = $categorySaved; + + //Updating our role to remove design properties access. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::categories']); + $rules->saveRel(); + //Updating the category but with the same design properties values. + $result = $this->updateCategory($categoryData['id'], $categoryData, $token); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + + //Changing a design property. + foreach ($categoryData['custom_attributes'] as &$customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $customAttribute['value'] = 'test2'; + } + } + $exceptionMessage = null; + try { + $this->updateCategory($categoryData['id'], $categoryData, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have permissions to do that. + $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 1001d58ee8a67..a46657acf8fff 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -5,10 +5,14 @@ */ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\Store; use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Catalog\Model\CategoryFactory as CategoryModelFactory; /** * @magentoAppArea adminhtml @@ -19,6 +23,15 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro * @var \Magento\Catalog\Model\ResourceModel\Product */ protected $productResource; + /** + * @var Builder + */ + private $aclBuilder; + + /** + * @var CategoryModelFactory + */ + private $categoryFactory; /** * @inheritDoc @@ -33,6 +46,8 @@ protected function setUp() $this->productResource = Bootstrap::getObjectManager()->get( Product::class ); + $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryModelFactory::class); } /** @@ -61,7 +76,7 @@ public function testSaveAction($inputData, $defaultAttributes, $attributesSaved if ($isSuccess) { $this->assertSessionMessages( $this->equalTo(['You saved the category.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + MessageInterface::TYPE_SUCCESS ); } @@ -555,4 +570,78 @@ private function getCategoryProductsCount(): int $this->productResource->getConnection()->fetchAll($oldCategoryProducts) ); } + + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + $requestData = [ + 'id' => '2', + 'entity_id' => '2', + 'path' => '1/2', + 'name' => 'Custom Name', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ]; + $uri = 'backend/catalog/category/save'; + + //Trying to update the category's design settings without proper permissions. + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['Not allowed to edit the category\'s design attributes']; + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); + $requestData['custom_layout_update_file'] = 'test-file'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $requestData['custom_layout_update_file'] = null; + $requestData['custom_design'] = 'test-theme'; + $this->aclBuilder->getAcl()->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + $this->getRequest()->setDispatched(false); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->assertNotEmpty($category->getCustomDesign()); + $this->assertEquals('test-theme', $category->getCustomDesign()); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } } From f29efd3e44ecbf424d9e3126f3b968b286abff5e Mon Sep 17 00:00:00 2001 From: RomanKis Date: Thu, 29 Aug 2019 13:29:25 +0300 Subject: [PATCH 0465/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../SaveQuoteAddressToCustomerAddressBook.php | 95 +++++++++++++++++++ .../Model/Cart/SetBillingAddressOnCart.php | 18 +++- .../Model/Cart/SetShippingAddressesOnCart.php | 20 +++- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 4 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php new file mode 100644 index 0000000000000..e9aa4ba745b9f --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php @@ -0,0 +1,95 @@ +addressFactory = $addressFactory; + $this->addressRepository = $addressRepository; + $this->regionFactory = $regionFactory; + } + + /** + * + * @param QuoteAddress $quoteAddress + * @param int $customerId + * + * @return void + * @throws GraphQlInputException + */ + public function execute(QuoteAddress $quoteAddress, int $customerId): void + { + try { + /** @var AddressInterface $customerAddress */ + $customerAddress = $this->addressFactory->create(); + $customerAddress->setFirstname($quoteAddress->getFirstname()) + ->setLastname($quoteAddress->getLastname()) + ->setMiddlename($quoteAddress->getMiddlename()) + ->setPrefix($quoteAddress->getPrefix()) + ->setSuffix($quoteAddress->getSuffix()) + ->setVatId($quoteAddress->getVatId()) + ->setCountryId($quoteAddress->getCountryId()) + ->setCompany($quoteAddress->getCompany()) + ->setRegionId($quoteAddress->getRegionId()) + ->setFax($quoteAddress->getFax()) + ->setCity($quoteAddress->getCity()) + ->setPostcode($quoteAddress->getPostcode()) + ->setStreet($quoteAddress->getStreet()) + ->setTelephone($quoteAddress->getTelephone()) + ->setCustomerId($customerId); + + /** @var RegionInterface $region */ + $region = $this->regionFactory->create(); + $region->setRegionCode($quoteAddress->getRegionCode()) + ->setRegion($quoteAddress->getRegion()) + ->setRegionId($quoteAddress->getRegionId()); + $customerAddress->setRegion($region); + + $this->addressRepository->save($customerAddress); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 673debefd0874..22dff2d5550ba 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -13,6 +13,7 @@ use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote\Address; +use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Set billing address for a specified shopping cart @@ -29,16 +30,24 @@ class SetBillingAddressOnCart */ private $assignBillingAddressToCart; + /** + * @var SaveQuoteAddressToCustomerAddressBook + */ + private $saveQuoteAddressToCustomerAddressBook; + /** * @param QuoteAddressFactory $quoteAddressFactory * @param AssignBillingAddressToCart $assignBillingAddressToCart + * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - AssignBillingAddressToCart $assignBillingAddressToCart + AssignBillingAddressToCart $assignBillingAddressToCart, + SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->assignBillingAddressToCart = $assignBillingAddressToCart; + $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -101,6 +110,12 @@ private function createBillingAddress( ): Address { if (null === $customerAddressId) { $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); + + $customerId = $context->getUserId(); + // need to save address only for registered user and if save_in_address_book = true + if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { + $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); + } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); @@ -111,6 +126,7 @@ private function createBillingAddress( (int)$context->getUserId() ); } + return $billingAddress; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 77719bed5b16f..c3d147bb4727b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -11,6 +11,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Set single shipping address for a specified shopping cart @@ -27,16 +28,24 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface */ private $assignShippingAddressToCart; + /** + * @var SaveQuoteAddressToCustomerAddressBook + */ + private $saveQuoteAddressToCustomerAddressBook; + /** * @param QuoteAddressFactory $quoteAddressFactory * @param AssignShippingAddressToCart $assignShippingAddressToCart + * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ public function __construct( QuoteAddressFactory $quoteAddressFactory, - AssignShippingAddressToCart $assignShippingAddressToCart + AssignShippingAddressToCart $assignShippingAddressToCart, + SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->assignShippingAddressToCart = $assignShippingAddressToCart; + $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -65,8 +74,15 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } + $customerId = $context->getUserId(); + if (null === $customerAddressId) { $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); + + // need to save address only for registered user and if save_in_address_book = true + if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { + $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); + } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); @@ -74,7 +90,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress( (int)$customerAddressId, - $context->getUserId() + $customerId ); } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 6d7f4daf40ded..b9199a0774c27 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -108,7 +108,7 @@ input CartAddressInput { postcode: String country_code: String! telephone: String! - save_in_address_book: Boolean! + save_in_address_book: Boolean } input SetShippingMethodsOnCartInput { From 666a333d19152b86069f2a0e667fc61631141f3d Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Thu, 29 Aug 2019 17:44:27 +0530 Subject: [PATCH 0466/2437] Correct spelling --- .../Magento/Sales/Model/ResourceModel/Status/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php index 23d835db603df..393494858a3bb 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php @@ -1,6 +1,6 @@ Date: Thu, 29 Aug 2019 17:46:44 +0530 Subject: [PATCH 0467/2437] update Correct spelling --- .../Magento/Sales/Model/ResourceModel/Status/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php index 393494858a3bb..f429a62fc3f03 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php @@ -1,6 +1,6 @@ Date: Thu, 29 Aug 2019 15:25:58 +0300 Subject: [PATCH 0468/2437] Sales - fix displaying available allowed countries while editing order address in Admin panel - fixed unit test --- .../Test/Unit/Block/Adminhtml/Order/Address/FormTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Address/FormTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Address/FormTest.php index 94148cc515382..2b08daf02134e 100644 --- a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Address/FormTest.php +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Address/FormTest.php @@ -95,6 +95,11 @@ protected function setUp() '_orderCreate' => $this->orderCreate ] ); + + // Do not display VAT validation button on edit order address form + // Emulate fix done in controller + /** @see \Magento\Sales\Controller\Adminhtml\Order\Address::execute */ + $this->addressBlock->setDisplayVatValidationButton(false); } public function testGetForm() From 459c596a74c209b001b1afcfcde8717f46132fec Mon Sep 17 00:00:00 2001 From: Tan Sezer Date: Thu, 29 Aug 2019 16:46:38 +0200 Subject: [PATCH 0469/2437] add eav_attribute_option join for ordering by sort_order column for attributes --- .../Model/Entity/Attribute/Source/Table.php | 8 +++++- .../ResourceModel/Entity/Attribute/Option.php | 27 +++++++++++++++++++ .../Entity/Attribute/Source/TableTest.php | 3 +++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index f9aa1a9ed3ba1..908f29069c429 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -213,7 +213,13 @@ public function addValueSortToCollection($collection, $dir = \Magento\Framework\ $valueExpr ); - $collection->getSelect()->order("{$attribute->getAttributeCode()} {$dir}"); + $this->_attrOptionFactory->create()->addOptionToCollection( + $collection, + $attribute, + $valueExpr + ); + + $collection->getSelect()->order("{$attribute->getAttributeCode()}_order {$dir}"); return $this; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php index 79c277dcb6a82..6dc51247fb3f3 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php @@ -61,6 +61,33 @@ public function addOptionValueToCollection($collection, $attribute, $valueExpr) return $this; } + /** + * Add Join with option for collection select + * + * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection + * @param \Magento\Eav\Model\Entity\Attribute $attribute + * @param \Zend_Db_Expr $valueExpr + * @return $this + */ + public function addOptionToCollection($collection, $attribute, $valueExpr) + { + $connection = $this->getConnection(); + $attributeCode = $attribute->getAttributeCode(); + $optionTable1 = $attributeCode . '_option_t1'; + $tableJoinCond1 = "{$optionTable1}.option_id={$valueExpr}"; + $valueExpr = $connection->getIfNullSql( + "{$optionTable1}.sort_order" + ); + + $collection->getSelect()->joinLeft( + [$optionTable1 => $this->getTable('eav_attribute_option')], + $tableJoinCond1, + ["{$attributeCode}_order" => $valueExpr] + ); + + return $this; + } + /** * Retrieve Select for update Flat data * diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php index b68446d22f910..49e7be7ecf594 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php @@ -314,6 +314,9 @@ public function testAddValueSortToCollection() $attrOption->expects($this->once())->method('addOptionValueToCollection') ->with($collection, $this->abstractAttributeMock, $expr) ->willReturnSelf(); + $attrOption->expects($this->once())->method('addOptionToCollection') + ->with($collection, $this->abstractAttributeMock, $expr) + ->willReturnSelf(); $select->expects($this->once())->method('order')->with("{$attributeCode} {$dir}"); $this->assertEquals($this->model, $this->model->addValueSortToCollection($collection, $dir)); From a047aca104cb76d743bb318887f6ff7530175d5d Mon Sep 17 00:00:00 2001 From: Tan Sezer Date: Thu, 29 Aug 2019 17:02:43 +0200 Subject: [PATCH 0470/2437] add a line --- app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index 908f29069c429..ef7b4a69808bc 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -211,9 +211,7 @@ public function addValueSortToCollection($collection, $dir = \Magento\Framework\ $collection, $attribute, $valueExpr - ); - - $this->_attrOptionFactory->create()->addOptionToCollection( + )->addOptionToCollection( $collection, $attribute, $valueExpr From 1a7bbc4b1d23a5bbaa555893f3a1406121b3ec28 Mon Sep 17 00:00:00 2001 From: RomanKis Date: Fri, 30 Aug 2019 11:59:42 +0300 Subject: [PATCH 0471/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Quote/Customer/CheckoutEndToEndTest.php | 2 - .../Customer/SetBillingAddressOnCartTest.php | 160 ++++++++++++++++- .../Customer/SetShippingAddressOnCartTest.php | 165 +++++++++++++++++- .../Guest/AllowGuestCheckoutOptionTest.php | 2 - .../Quote/Guest/CheckoutEndToEndTest.php | 2 - .../Guest/SetBillingAddressOnCartTest.php | 7 - .../Guest/SetShippingAddressOnCartTest.php | 6 - 7 files changed, 313 insertions(+), 31 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php index 5a4cc88d69623..578c7faa3c6e6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CheckoutEndToEndTest.php @@ -259,7 +259,6 @@ private function setBillingAddress(string $cartId): void telephone: "88776655" region: "TX" country_code: "US" - save_in_address_book: false } } } @@ -298,7 +297,6 @@ private function setShippingAddress(string $cartId): array postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 011930e723273..9a9c50c140571 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -7,6 +7,9 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; @@ -45,6 +48,19 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var AddressRepositoryInterface + */ + private $customerAddressRepository; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -53,6 +69,9 @@ protected function setUp() $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->customerAddressRepository = $objectManager->get(AddressRepositoryInterface::class); + $this->searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + $this->customerRepository = $objectManager->get(CustomerRepositoryInterface::class); } /** @@ -81,7 +100,6 @@ public function testSetNewBillingAddress() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -140,7 +158,6 @@ public function testSetNewBillingAddressWithUseForShippingParameter() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } use_for_shipping: true } @@ -301,7 +318,6 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -383,7 +399,6 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } use_for_shipping: true } @@ -620,7 +635,6 @@ public function testSetNewBillingAddressWithRedundantStreetLine() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -663,7 +677,6 @@ public function testSetBillingAddressWithLowerCaseCountry() postcode: "887766" country_code: "us" telephone: "88776655" - save_in_address_book: false } } } @@ -696,6 +709,141 @@ public function testSetBillingAddressWithLowerCaseCountry() $this->assertNewAddressFields($billingAddressResponse); } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewBillingAddressWithSaveInAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<graphQlMutation($query, [], '', $this->getHeaderMap()); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(1, $addresses); + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertArrayHasKey('billing_address', $cartResponse); + $billingAddressResponse = $cartResponse['billing_address']; + $this->assertNewAddressFields($billingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewBillingAddressWithNotSaveInAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<graphQlMutation($query, [], '', $this->getHeaderMap()); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(0, $addresses); + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertArrayHasKey('billing_address', $cartResponse); + $billingAddressResponse = $cartResponse['billing_address']; + $this->assertNewAddressFields($billingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * Verify the all the whitelisted fields for a New Address Object * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 15ee125955062..72b6a4aea6730 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -7,6 +7,9 @@ namespace Magento\GraphQl\Quote\Customer; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; @@ -45,6 +48,21 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var AddressRepositoryInterface + */ + private $customerAddressRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); @@ -53,6 +71,9 @@ protected function setUp() $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->customerAddressRepository = $objectManager->get(AddressRepositoryInterface::class); + $this->searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + $this->customerRepository = $objectManager->get(CustomerRepositoryInterface::class); } /** @@ -82,7 +103,6 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -146,7 +166,6 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -274,7 +293,6 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -456,7 +474,6 @@ public function testSetMultipleNewShippingAddresses() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } }, { @@ -470,7 +487,6 @@ public function testSetMultipleNewShippingAddresses() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -514,7 +530,6 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -655,6 +670,144 @@ public function testSetShippingAddressWithLowerCaseCountry() $this->assertEquals('CA', $address['region']['code']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewShippingAddressWithSaveInAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<graphQlMutation($query, [], '', $this->getHeaderMap()); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(1, $addresses); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewShippingAddressWithNotSaveInAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<graphQlMutation($query, [], '', $this->getHeaderMap()); + $customer = $this->customerRepository->get('customer@example.com'); + $searchCriteria = $this->searchCriteriaBuilder->addFilter('parent_id', $customer->getId())->create(); + $addresses = $this->customerAddressRepository->getList($searchCriteria)->getItems(); + + self::assertCount(0, $addresses); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); + + foreach ($addresses as $address) { + $this->customerAddressRepository->delete($address); + } + } + /** * Verify the all the whitelisted fields for a New Address Object * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php index 16f291be91078..23128e55af32f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php @@ -108,7 +108,6 @@ public function testSetBillingAddressToGuestCustomerCart() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -221,7 +220,6 @@ public function testSetNewShippingAddressOnCartWithGuestCheckoutDisabled() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CheckoutEndToEndTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CheckoutEndToEndTest.php index ed5aa9303d875..04ecde833de85 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CheckoutEndToEndTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CheckoutEndToEndTest.php @@ -219,7 +219,6 @@ private function setBillingAddress(string $cartId): void telephone: "88776655" region: "TX" country_code: "US" - save_in_address_book: false } } } @@ -258,7 +257,6 @@ private function setShippingAddress(string $cartId): array postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 730e65b4ba8aa..5a3d45005c911 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -52,7 +52,6 @@ public function testSetNewBillingAddress() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -110,7 +109,6 @@ public function testSetNewBillingAddressWithUseForShippingParameter() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } use_for_shipping: true } @@ -186,7 +184,6 @@ public function testSetBillingAddressToCustomerCart() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -263,7 +260,6 @@ public function testSetBillingAddressOnNonExistentCart() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -391,7 +387,6 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } use_for_shipping: true } @@ -437,7 +432,6 @@ public function testSetNewBillingAddressRedundantStreetLine() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } @@ -480,7 +474,6 @@ public function testSetBillingAddressWithLowerCaseCountry() postcode: "887766" country_code: "us" telephone: "88776655" - save_in_address_book: false } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 537c8f09a0a98..262aac45a968b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -53,7 +53,6 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -116,7 +115,6 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -268,7 +266,6 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -337,7 +334,6 @@ public function testSetMultipleNewShippingAddresses() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } }, { @@ -351,7 +347,6 @@ public function testSetMultipleNewShippingAddresses() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } ] @@ -391,7 +386,6 @@ public function testSetShippingAddressOnNonExistentCart() postcode: "887766" country_code: "US" telephone: "88776655" - save_in_address_book: false } } } From 952d12e423f2a02c0ead3a7fd8cd7588a15d0760 Mon Sep 17 00:00:00 2001 From: Tan Sezer Date: Fri, 30 Aug 2019 11:24:37 +0200 Subject: [PATCH 0472/2437] fix unit test for ordering of attribute --- .../Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php index 49e7be7ecf594..2997874f6ea34 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php @@ -317,7 +317,7 @@ public function testAddValueSortToCollection() $attrOption->expects($this->once())->method('addOptionToCollection') ->with($collection, $this->abstractAttributeMock, $expr) ->willReturnSelf(); - $select->expects($this->once())->method('order')->with("{$attributeCode} {$dir}"); + $select->expects($this->once())->method('order')->with("{$attributeCode}_order {$dir}"); $this->assertEquals($this->model, $this->model->addValueSortToCollection($collection, $dir)); } From 08e5d5c1941d60c8cc4d2d7ffe9be2e7abb359a5 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun Date: Fri, 30 Aug 2019 09:16:41 -0500 Subject: [PATCH 0473/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/LayoutUpdateManager.php | 8 +- .../Product/Attribute/LayoutUpdateManager.php | 6 +- .../Data/UpdateCustomLayoutAttributes.php | 4 +- .../integration/etc/di/preferences/ce.php | 2 + .../Model/CategoryLayoutUpdateManager.php | 50 ++++++++++++ .../Controller/Adminhtml/CategoryTest.php | 78 ++++++++++++++++++- .../Catalog/Controller/CategoryTest.php | 34 ++++++++ 7 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php index 32d6068446a65..69c5f961cb96f 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php @@ -88,6 +88,10 @@ private function getLayoutProcessor(): LayoutProcessor */ public function fetchAvailableFiles(CategoryInterface $category): array { + if (!$category->getId()) { + return []; + } + $handles = $this->getLayoutProcessor()->getAvailableHandles(); return array_filter( @@ -120,7 +124,9 @@ function (string $handle) use ($category) : ?string { */ public function extractCustomSettings(CategoryInterface $category, DataObject $intoSettings): void { - if ($attribute = $category->getCustomAttribute('custom_layout_update_file')) { + if ($category->getId() + && $attribute = $category->getCustomAttribute('custom_layout_update_file') + ) { $handles = $intoSettings->getPageLayoutHandles() ?? []; $handles = array_merge_recursive( $handles, diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php index bcf655b25299c..1e0acdc989cdb 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -100,6 +100,10 @@ private function getLayoutProcessor(): LayoutProcessor */ public function fetchAvailableFiles(ProductInterface $product): array { + if (!$product->getSku()) { + return []; + } + $identifier = $this->sanitizeSku($product); $handles = $this->getLayoutProcessor()->getAvailableHandles(); @@ -133,7 +137,7 @@ function (string $handle) use ($identifier) : ?string { */ public function extractCustomSettings(ProductInterface $product, DataObject $intoSettings): void { - if ($attribute = $product->getCustomAttribute('custom_layout_update_file')) { + if ($product->getSku() && $attribute = $product->getCustomAttribute('custom_layout_update_file')) { $handles = $intoSettings->getPageLayoutHandles() ?? []; $handles = array_merge_recursive( $handles, diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php index 386d9b246782d..81fd35ccd3178 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php @@ -107,14 +107,14 @@ public function apply() $eavSetup->updateAttribute( Product::ENTITY, 'custom_layout_update', - 'visible', + 'is_visible', false ); $eavSetup->updateAttribute( Category::ENTITY, 'custom_layout_update', - 'visible', + 'is_visible', false ); } diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index b8264ea977750..87ac54f4591eb 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -31,4 +31,6 @@ \Magento\TestFramework\Lock\Backend\DummyLocker::class, \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class, \Magento\Framework\HTTP\AsyncClientInterface::class => \Magento\TestFramework\HTTP\AsyncClientInterfaceMock::class, + \Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager::class => + \Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager::class ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php new file mode 100644 index 0000000000000..48ff3a6496722 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php @@ -0,0 +1,50 @@ +fakeFiles[$forCategoryId]); + } else { + $this->fakeFiles[$forCategoryId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(CategoryInterface $category): array + { + if (array_key_exists($category->getId(), $this->fakeFiles)) { + return $this->fakeFiles[$category->getId()]; + } + + return parent::fetchAvailableFiles($category); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index a46657acf8fff..94a37327fcc39 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -8,6 +8,7 @@ use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\Store; use Magento\Catalog\Model\ResourceModel\Product; @@ -222,7 +223,7 @@ public function saveActionDataProvider() 'custom_design_from' => 1, 'custom_design_to' => 1, 'page_layout' => 1, - 'custom_layout_update' => 1, + 'custom_layout_update' => null, ], ], [ @@ -268,7 +269,6 @@ public function saveActionDataProvider() 'custom_design_from' => '5/21/2015', 'custom_design_to' => '5/29/2015', 'page_layout' => '', - 'custom_layout_update' => '', 'use_config' => [ 'available_sort_by' => 1, 'default_sort_by' => 1, @@ -290,7 +290,6 @@ public function saveActionDataProvider() 'description' => true, 'meta_keywords' => true, 'meta_description' => true, - 'custom_layout_update' => true, 'custom_design_from' => true, 'custom_design_to' => true, 'filter_price_range' => false @@ -310,7 +309,6 @@ public function saveActionDataProvider() 'description' => 'Custom Description', 'meta_keywords' => 'Custom keywords', 'meta_description' => 'Custom meta description', - 'custom_layout_update' => null, 'custom_design_from' => '2015-05-21 00:00:00', 'custom_design_to' => '2015-05-29 00:00:00', 'filter_price_range' => null @@ -644,4 +642,76 @@ public function testSaveDesign(): void MessageInterface::TYPE_ERROR ); } + /** + * Test custom update files functionality. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @throws \Throwable + * @return void + */ + public function testSaveCustomLayout(): void + { + $file = 'test_file'; + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + /** @var CategoryLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + $layoutManager->setCategoryFakeFiles(2, [$file]); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + $requestData = [ + 'id' => '2', + 'entity_id' => '2', + 'path' => '1/2', + 'name' => 'Custom Name', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ]; + $uri = 'backend/catalog/category/save'; + + //Saving a wrong file + $requestData['custom_layout_update_file'] = $file . 'INVALID'; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + + //Checking that the value is not saved + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($requestData['entity_id']); + $this->assertEmpty($category->getData('custom_layout_update_file')); + + //Saving the correct file + $requestData['custom_layout_update_file'] = $file; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + + //Checking that the value is saved + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($requestData['entity_id']); + $this->assertEquals($file, $category->getData('custom_layout_update_file')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php index 87b8d4a117e2d..070ef99c15a34 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php @@ -5,6 +5,10 @@ */ namespace Magento\Catalog\Controller; +use Magento\Catalog\Model\Category; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; + /** * Test class for \Magento\Catalog\Controller\Category. * @@ -103,4 +107,34 @@ public function testViewActionInactiveCategory() $this->assert404NotFound(); } + + /** + * Check that custom layout update files is employed. + * + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_product_ids.php + * @return void + */ + public function testViewWithCustomUpdate(): void + { + //Setting a fake file for the category. + $file = 'test-file'; + $categoryId = 5; + /** @var CategoryLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + $layoutManager->setCategoryFakeFiles($categoryId, [$file]); + /** @var Category $category */ + $category = Bootstrap::getObjectManager()->create(Category::class); + $category->load($categoryId); + //Updating the custom attribute. + $category->setData('custom_layout_update_file', $file); + $category->save(); + + //Viewing the category + $this->dispatch("catalog/category/view/id/$categoryId"); + //Layout handles must contain the file. + $handles = Bootstrap::getObjectManager()->get(\Magento\Framework\View\LayoutInterface::class) + ->getUpdate() + ->getHandles(); + $this->assertContains("catalog_category_view_selectable_{$categoryId}_{$file}", $handles); + } } From 973eb86f7b4157e2452201039416f617ae58cdb5 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Fri, 30 Aug 2019 13:26:20 -0500 Subject: [PATCH 0474/2437] MQE-1715: .credentials file must exist but it should not have to --- composer.json | 2 +- composer.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 8b2cb0b53972a..e4596256f4427 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "~3.0.0", - "magento/magento2-functional-testing-framework": "2.4.4", + "magento/magento2-functional-testing-framework": "2.4.5", "pdepend/pdepend": "2.5.2", "phpmd/phpmd": "@stable", "phpunit/phpunit": "~6.5.0", diff --git a/composer.lock b/composer.lock index 8af9ead67d9c1..ae4219a2fd5dd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8819d140d0951fefd8e14b052ff0f61a", + "content-hash": "d6f99cc06f1aa39a903b382958676ba2", "packages": [ { "name": "braintree/braintree_php", @@ -6814,16 +6814,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.4.4", + "version": "2.4.5", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "ab347cf23d01f6bb9d158ab37d2dc56594999beb" + "reference": "d9de524babec36919b3ef73f3af03fbce99d427a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ab347cf23d01f6bb9d158ab37d2dc56594999beb", - "reference": "ab347cf23d01f6bb9d158ab37d2dc56594999beb", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/d9de524babec36919b3ef73f3af03fbce99d427a", + "reference": "d9de524babec36919b3ef73f3af03fbce99d427a", "shasum": "" }, "require": { @@ -6885,7 +6885,7 @@ "magento", "testing" ], - "time": "2019-08-15T14:46:36+00:00" + "time": "2019-08-30T18:17:27+00:00" }, { "name": "mikey179/vfsstream", From f577a003344330ab97b606117e1f8f92d0ec1faf Mon Sep 17 00:00:00 2001 From: Max Lesechko Date: Fri, 30 Aug 2019 16:55:43 -0500 Subject: [PATCH 0475/2437] MC-19740: Set of InnoDB tables is converted to Memory tables after setup:upgrade --- .../Patch/Schema/ChangeTmpTablesEngine.php | 68 ----------------- app/code/Magento/Bundle/etc/db_schema.xml | 6 +- .../Patch/Schema/ChangeTmpTablesEngine.php | 74 ------------------- app/code/Magento/Catalog/etc/db_schema.xml | 38 +++++----- .../Patch/Schema/ChangeTmpTablesEngine.php | 61 --------------- .../CatalogInventory/etc/db_schema.xml | 6 +- .../Patch/Schema/ChangeTmpTablesEngine.php | 61 --------------- .../Magento/Downloadable/etc/db_schema.xml | 2 +- 8 files changed, 26 insertions(+), 290 deletions(-) delete mode 100644 app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php delete mode 100644 app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php diff --git a/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index c6a67cc5a110c..0000000000000 --- a/app/code/Magento/Bundle/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,68 +0,0 @@ -schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tables = [ - 'catalog_product_index_price_bundle_tmp', - 'catalog_product_index_price_bundle_sel_tmp', - 'catalog_product_index_price_bundle_opt_tmp', - ]; - foreach ($tables as $table) { - $tableName = $this->schemaSetup->getTable($table); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Bundle/etc/db_schema.xml b/app/code/Magento/Bundle/etc/db_schema.xml index 97e86e5c17359..ade8fbf7cf1ce 100644 --- a/app/code/Magento/Bundle/etc/db_schema.xml +++ b/app/code/Magento/Bundle/etc/db_schema.xml @@ -203,7 +203,7 @@ - @@ -265,7 +265,7 @@
- @@ -320,7 +320,7 @@
- diff --git a/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index c39247f9b30df..0000000000000 --- a/app/code/Magento/Catalog/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,74 +0,0 @@ -schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tables = [ - 'catalog_product_index_price_cfg_opt_agr_tmp', - 'catalog_product_index_price_cfg_opt_tmp', - 'catalog_product_index_price_final_tmp', - 'catalog_product_index_price_opt_tmp', - 'catalog_product_index_price_opt_agr_tmp', - 'catalog_product_index_eav_tmp', - 'catalog_product_index_eav_decimal_tmp', - 'catalog_product_index_price_tmp', - 'catalog_category_product_index_tmp', - ]; - foreach ($tables as $table) { - $tableName = $this->schemaSetup->getTable($table); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 6fef4ca6e9128..b5e02b1daaa01 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -1238,7 +1238,7 @@
- @@ -1279,7 +1279,7 @@
- @@ -1327,7 +1327,7 @@
- @@ -1375,7 +1375,7 @@
- @@ -1418,7 +1418,7 @@
- @@ -1470,7 +1470,7 @@
- @@ -1489,13 +1489,13 @@ - + - + - +
@@ -1528,7 +1528,7 @@ - @@ -1547,13 +1547,13 @@ - + - + - +
@@ -1592,7 +1592,7 @@ - @@ -1617,17 +1617,17 @@ - + - + - +
- @@ -1646,7 +1646,7 @@ - + diff --git a/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index 7f43cd279d4e3..0000000000000 --- a/app/code/Magento/CatalogInventory/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,61 +0,0 @@ -schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tableName = $this->schemaSetup->getTable('cataloginventory_stock_status_tmp'); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/CatalogInventory/etc/db_schema.xml b/app/code/Magento/CatalogInventory/etc/db_schema.xml index 5ac7fedc5aa18..67a200eb37125 100644 --- a/app/code/Magento/CatalogInventory/etc/db_schema.xml +++ b/app/code/Magento/CatalogInventory/etc/db_schema.xml @@ -142,7 +142,7 @@
- @@ -159,10 +159,10 @@ - + - +
diff --git a/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php b/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php deleted file mode 100644 index caf2f7745a3dd..0000000000000 --- a/app/code/Magento/Downloadable/Setup/Patch/Schema/ChangeTmpTablesEngine.php +++ /dev/null @@ -1,61 +0,0 @@ -schemaSetup = $schemaSetup; - } - - /** - * @inheritdoc - */ - public function apply() - { - $this->schemaSetup->startSetup(); - - $tableName = $this->schemaSetup->getTable('catalog_product_index_price_downlod_tmp'); - if ($this->schemaSetup->getConnection()->isTableExists($tableName)) { - $this->schemaSetup->getConnection()->changeTableEngine($tableName, 'InnoDB'); - } - - $this->schemaSetup->endSetup(); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Downloadable/etc/db_schema.xml b/app/code/Magento/Downloadable/etc/db_schema.xml index ccbefa4fb3992..ee7b3c5683ea1 100644 --- a/app/code/Magento/Downloadable/etc/db_schema.xml +++ b/app/code/Magento/Downloadable/etc/db_schema.xml @@ -233,7 +233,7 @@ - From 75aa85d4ce8c3b48ed0c46c269b2e452b31ccab4 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan Date: Mon, 2 Sep 2019 14:56:25 +0400 Subject: [PATCH 0476/2437] MC-17869: Cart Total is shown as NaN when 100% discount applied through Cart Rule - Added automated test script --- .../Section/CheckoutCartSummarySection.xml | 2 + ...ValueWithFullDiscountUsingCartRuleTest.xml | 142 ++++++++++++++++++ .../Test/Mftf/Data/SalesRuleData.xml | 7 + .../Tax/Test/Mftf/Data/TaxCodeData.xml | 6 + 4 files changed, 157 insertions(+) create mode 100644 app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 477451ef003ce..803e322d8105a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -33,5 +33,7 @@ + + diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml new file mode 100644 index 0000000000000..b1e11cbf07ff6 --- /dev/null +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -0,0 +1,142 @@ + + + + + + + + + <description value="Cart Total value when 100% discount applied through Cart Rule"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19524"/> + <useCaseId value="MC-17869"/> + <group value="quote"/> + </annotations> + <before> + <!-- log in --> + <comment userInput="Log in" stepKey="commentLogIn"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!-- Set configurations --> + <comment userInput="Set configurations" stepKey="commentSetConfigurations"/> + <magentoCLI command="config:set carriers/tablerate/active 1" stepKey="setShippingMethodEnabled"/> + <magentoCLI command="config:set carriers/tablerate/condition_name package_value" stepKey="setShippingMethodConditionName"/> + <magentoCLI command="config:set tax/calculation/price_includes_tax 1" stepKey="setCatalogPrice"/> + <magentoCLI command="config:set tax/calculation/shipping_includes_tax 1" stepKey="setSippingPrice"/> + <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 0" stepKey="setCrossBorderTrade"/> + <magentoCLI command="config:set tax/calculation/discount_tax 1" stepKey="setDiscount"/> + <magentoCLI command="config:set tax/cart_display/gift_wrapping 1" stepKey="setGiftWrapping"/> + <magentoCLI command="config:set tax/cart_display/printed_card 1" stepKey="setPrintedCart"/> + <magentoCLI command="config:set tax/cart_display/price 2" stepKey="setPrice"/> + <magentoCLI command="config:set tax/cart_display/subtotal 2" stepKey="setSubtotal"/> + <magentoCLI command="config:set tax/sales_display/gift_wrapping 1" stepKey="setSalesGiftWrapping"/> + <magentoCLI command="config:set tax/sales_display/printed_card 1" stepKey="setSalesPrintedCart"/> + <magentoCLI command="config:set carriers/freeshipping/active 1" stepKey="setFreeShipping"/> + <createData entity="defaultTaxRule" stepKey="initialTaxRule"/> + <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> + <!-- Go to tax rule page --> + <comment userInput="Go to tax rule page" stepKey="commentGoTOTaxRulePage"/> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + <!-- Add tax rule with 20% tax rate --> + <comment userInput="Add tax rule with 20% tax rate" stepKey="commentAddTaxRule"/> + <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNYSecond"/> + </actionGroup> + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + <!-- Create cart price rule --> + <comment userInput="Create cart price rule" stepKey="commentCreatePriceRule"/> + <actionGroup ref="AdminCreateCartPriceRuleActionGroup" stepKey="createCartPriceRule"> + <argument name="ruleName" value="TestSalesRuleSecond"/> + </actionGroup> + <!-- Create 3 simple product --> + <comment userInput="Create simple products" stepKey="commentCreateSimpleProduct"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProductFirst"> + <field key="price">5.10</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"> + <field key="price">5.10</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSimpleProductThird"> + <field key="price">5.50</field> + </createData> + </before> + <after> + <!-- Removed created Data --> + <comment userInput="Removed created Data" stepKey="commentRemovedCreatedData"/> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <!-- Delete the tax rate that were created --> + <comment userInput="Delete the tax rate that were created" stepKey="commentRemovedTaxRate"/> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNYSecond.state}}-{{SimpleTaxNYSecond.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="{{TestSalesRuleSecond.name}}"/> + </actionGroup> + <!-- Delete products --> + <comment userInput="Delete products" stepKey="commentDeleteProducts"/> + <deleteData createDataKey="createSimpleProductFirst" stepKey="deleteSimpleProductFirst"/> + <deleteData createDataKey="createSimpleProductSecond" stepKey="deleteSimpleProductSecond"/> + <deleteData createDataKey="createSimpleProductThird" stepKey="deleteSimpleProductThird"/> + <!-- Unset configuration --> + <comment userInput="Unset configuration" stepKey="commentUnsetConfigurations"/> + <magentoCLI command="config:set carriers/tablerate/active 0" stepKey="unsetShippingMethodEnabled"/> + <magentoCLI command="config:set tax/calculation/price_includes_tax 0" stepKey="unsetCatalogPrice"/> + <magentoCLI command="config:set tax/calculation/shipping_includes_tax 0" stepKey="unsetSippingPrice"/> + <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 1" stepKey="unsetCrossBorderTrade"/> + <magentoCLI command="config:set tax/calculation/discount_tax 0" stepKey="unsetDiscount"/> + <magentoCLI command="config:set tax/cart_display/gift_wrapping 0" stepKey="unsetGiftWrapping"/> + <magentoCLI command="config:set tax/cart_display/printed_card 0" stepKey="unsetPrintedCart"/> + <magentoCLI command="config:set tax/cart_display/price 0" stepKey="unsetPrice"/> + <magentoCLI command="config:set tax/cart_display/subtotal 0" stepKey="unsetSubtotal"/> + <magentoCLI command="config:set tax/sales_display/gift_wrapping 0" stepKey="unsetSalesGiftWrapping"/> + <magentoCLI command="config:set tax/sales_display/printed_card 0" stepKey="unsetSalesPrintedCart"/> + <magentoCLI command="config:set carriers/freeshipping/active 0" stepKey="unsetFreeShipping"/> + <!-- Log out --> + <comment userInput="Log out" stepKey="commentLogOut"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Add testing products to the cart --> + <comment userInput="Add testing products to the cart" stepKey="commentAddProducts"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductFirst.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddProductToCard"> + <argument name="productName" value="$$createSimpleProductFirst.name$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductSecond.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantityForTheSecondProduct"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddPSecondProductToCard"> + <argument name="productName" value="$$createSimpleProductSecond.name$$"/> + </actionGroup> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductThird.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantityForTheThirdProduct"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddPThirdProductToCard"> + <argument name="productName" value="$$createSimpleProductThird.name$$"/> + </actionGroup> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="6" stepKey="seeCartQuantity"/> + <!-- Go to the shopping cart page --> + <comment userInput="Go to the shopping cart page" stepKey="commentGotToShippingCartPage"/> + <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCartPageLoad"/> + <wait time="200" stepKey="erfgr"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$31.40" stepKey="seeDiscountAmount"/> + <see selector="{{CheckoutCartSummarySection.subTotal}}" userInput="$31.40" stepKey="seeSubTotal"/> + <see selector="{{CheckoutCartSummarySection.orderTotal}}" userInput="0.00" stepKey="seeOrderTotal"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index c1ec728a6cfb9..a5287ad6468d1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -89,6 +89,13 @@ <data key="apply">Percent of product price discount</data> <data key="discountAmount">50</data> </entity> + <entity name="TestSalesRuleSecond" type="SalesRule"> + <data key="name" unique="suffix">TestSalesRule</data> + <data key="websites">Main Website</data> + <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> + <data key="apply">Percent of product price discount</data> + <data key="discountAmount">100</data> + </entity> <entity name="CatPriceRule" type="SalesRule"> <data key="name" unique="suffix">CartPriceRule</data> <data key="websites">Main Website</data> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml index 27c89162b5cea..f9afe84366d9e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml @@ -26,6 +26,12 @@ <data key="zip">*</data> <data key="rate">8.375</data> </entity> + <entity name="SimpleTaxNYSecond" type="tax"> + <data key="state">New York</data> + <data key="country">United States</data> + <data key="zip">*</data> + <data key="rate">20.00</data> + </entity> <entity name="SimpleTaxCA" type="tax"> <data key="state">California</data> <data key="country">United States</data> From 89f7262a766dbc7b9c48ee4e33982dce733e2fe8 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 2 Sep 2019 17:25:19 +0400 Subject: [PATCH 0477/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- .../Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 4 ++-- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 1 - .../Test/StrorefrontElasticsearchSearchInvalidValueTest.xml | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) rename app/code/Magento/{Search => Elasticsearch6}/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml (96%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 6b713ad2a991b..0c3172392435a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -320,10 +320,10 @@ <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> </actionGroup> - <actionGroup name="CreateSearchableProductAttribute" extends="createProductAttribute" insertAfter="checkRequired"> + <actionGroup name="StorefrontCreateSearchableProductAttribute" extends="createProductAttribute" insertAfter="checkRequired"> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab"/> <waitForElementVisible selector="{{StorefrontPropertiesSection.PageTitle}}" stepKey="waitTabLoad"/> - <selectOption selector="{{StorefrontPropertiesSection.isSearchable}}" userInput="Yes" stepKey="setSearchable"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="setSearchable"/> </actionGroup> <!-- Inputs text default value and attribute code--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 7b1dd6d565b40..462f721913b9c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -49,7 +49,6 @@ <element name="useForPromoRuleConditions" type="select" selector="#is_used_for_promo_rules"/> <element name="StorefrontPropertiesSectionToggle" type="button" selector="#front_fieldset-wrapper"/> <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> - <element name="isSearchable" type="select" selector="#is_searchable"/> </section> <section name="WYSIWYGProductAttributeSection"> <element name="ShowHideBtn" type="button" selector="#toggledefault_value_texteditor"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml similarity index 96% rename from app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml rename to app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml index cf6e94f905aba..4b68dbf95a529 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml @@ -11,6 +11,7 @@ <test name="StrorefrontElasticsearchSearchInvalidValueTest"> <annotations> <features value="Search"/> + <stories value="Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page"/> <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <severity value="MAJOR"/> @@ -49,7 +50,7 @@ <!--Create new searchable product attribute--> <comment userInput="Create new searchable product attribute" stepKey="commentCreateAttribute"/> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - <actionGroup ref="CreateSearchableProductAttribute" stepKey="createAttribute"> + <actionGroup ref="StorefrontCreateSearchableProductAttribute" stepKey="createAttribute"> <argument name="attribute" value="textProductAttribute"/> </actionGroup> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> From 7a3f7e39fec24f681cc8d38c33871e9960b19477 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Tue, 3 Sep 2019 11:45:14 +0400 Subject: [PATCH 0478/2437] MC-17869: Cart Total is shown as NaN when 100% discount applied through Cart Rule - Updated automated test script --- .../Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 803e322d8105a..fe4a69a18082c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -9,6 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> + <element name="orderTotal" type="input" selector=".grand.totals .amount .price"/> + <element name="subTotal" type="input" selector="span[data-th='Subtotal']"/> <element name="expandShoppingCartSummary" type="button" selector="//*[contains(@class, 'items-in-cart')][not(contains(@class, 'active'))]"/> <element name="elementPosition" type="text" selector=".data.table.totals > tbody tr:nth-of-type({{value}}) > th" parameterized="true"/> <element name="subtotal" type="text" selector="//*[@id='cart-totals']//tr[@class='totals sub']//td//span[@class='price']"/> @@ -33,7 +35,5 @@ <element name="methodName" type="text" selector="#co-shipping-method-form label"/> <element name="shippingPrice" type="text" selector="#co-shipping-method-form span .price"/> <element name="shippingMethodElementId" type="radio" selector="#s_method_{{carrierCode}}_{{methodCode}}" parameterized="true"/> - <element name="orderTotal" type="input" selector=".grand.totals .amount .price"/> - <element name="subTotal" type="input" selector="span[data-th='Subtotal']"/> </section> </sections> From c9ba95e1bbf150a9592ffa19da4eca5cddf6f9b3 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 3 Sep 2019 15:00:19 -0500 Subject: [PATCH 0479/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Backend/Customlayoutupdate.php | 47 +++++++- .../Attribute/Backend/LayoutUpdate.php | 7 +- .../Attribute/Backend/LayoutUpdate.php | 7 +- .../Backend/CustomlayoutupdateTest.php | 101 ++++++++++++++++++ 4 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index 2dc31e480055c..83dbbae7dda57 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -6,10 +6,11 @@ namespace Magento\Catalog\Model\Attribute\Backend; use Magento\Catalog\Model\AbstractModel; -use Magento\Catalog\Model\Category; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; use Magento\Eav\Model\Entity\Attribute\Exception; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate as CategoryLayoutUpdate; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as ProductLayoutUpdate; /** * Layout update attribute backend @@ -67,6 +68,36 @@ public function validate($object) return true; } + /** + * Extract an attribute value. + * + * @param AbstractModel $object + * @param string|null $attributeCode + * @return mixed + */ + private function extractValue(AbstractModel $object, ?string $attributeCode = null) + { + $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); + $attribute = $object->getCustomAttribute($attributeCode); + + return $object->getData($attributeCode) ?? $attribute ? $attribute->getValue() : null; + } + + /** + * Put an attribute value. + * + * @param AbstractModel $object + * @param mixed $value + * @param string|null $attributeCode + * @return void + */ + private function putValue(AbstractModel $object, $value, ?string $attributeCode = null): void + { + $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); + $object->setCustomAttribute($attributeCode, $value); + $object->setData($attributeCode, $value); + } + /** * @inheritDoc * @param AbstractModel $object @@ -75,11 +106,19 @@ public function validate($object) public function beforeSave($object) { $attributeName = $this->getAttribute()->getName(); - if ($object->getData($attributeName) - && $object->getOrigData($attributeName) !== $object->getData($attributeName) - ) { + $value = $this->extractValue($object); + //New values are not accepted + if ($value && $object->getOrigData($attributeName) !== $value) { throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); } + //If custom file was selected we need to remove this attribute + $file = $this->extractValue($object, 'custom_layout_update_file'); + if ($file + && $file !== CategoryLayoutUpdate::VALUE_USE_UPDATE_XML + && $file !== ProductLayoutUpdate::VALUE_USE_UPDATE_XML + ) { + $this->putValue($object, null); + } return parent::beforeSave($object); } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index 3447c91043b8e..4ce590421dcaf 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -18,7 +18,7 @@ */ class LayoutUpdate extends AbstractBackend { - private const VALUE_USE_UPDATE_XML = '__existing__'; + public const VALUE_USE_UPDATE_XML = '__existing__'; /** * @var LayoutUpdateManager @@ -91,10 +91,7 @@ public function validate($object) public function beforeSave($object) { $value = $this->extractValue($object); - if ($value !== self::VALUE_USE_UPDATE_XML) { - $object->setCustomAttribute('custom_layout_update', null); - $object->setData('custom_layout_update', null); - } else { + if ($value === self::VALUE_USE_UPDATE_XML) { $value = null; } $this->setValue($value, $object); diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index 6841900b30033..590ae8c14e684 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -18,7 +18,7 @@ */ class LayoutUpdate extends AbstractBackend { - private const VALUE_USE_UPDATE_XML = '__existing__'; + public const VALUE_USE_UPDATE_XML = '__existing__'; /** * @var LayoutUpdateManager @@ -91,10 +91,7 @@ public function validate($object) public function beforeSave($object) { $value = $this->extractValue($object); - if ($value !== self::VALUE_USE_UPDATE_XML) { - $object->setCustomAttribute('custom_layout_update', null); - $object->setData('custom_layout_update', null); - } else { + if ($value === self::VALUE_USE_UPDATE_XML) { $value = null; } $this->setValue($value, $object); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php new file mode 100644 index 0000000000000..fa658706a78c2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Backend; + +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Category; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test 'custom layout' attribute. + */ +class CustomlayoutupdateTest extends TestCase +{ + /** + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * @var Customlayoutupdate + */ + private $attribute; + + /** + * @var Category + */ + private $category; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryFactory::class); + $this->category = $this->categoryFactory->create(); + $this->category->load(2); + $this->attribute = $this->category->getAttributes()['custom_layout_update']->getBackend(); + } + + /** + * Test that attribute cannot be modified but only removed completely. + * + * @return void + * @throws \Throwable + */ + public function testImmutable(): void + { + //Value is empty + $this->category->setCustomAttribute('custom_layout_update', null); + $this->category->setOrigData('custom_layout_update', null); + $this->attribute->beforeSave($this->category); + + //New value + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', null); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + $this->category->setCustomAttribute('custom_layout_update', 'testNew'); + $this->category->setOrigData('custom_layout_update', 'test'); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + + //Removing a value + $this->category->setCustomAttribute('custom_layout_update', null); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->attribute->beforeSave($this->category); + } + + /** + * Check that custom layout update file's values erase the old attribute's value. + * + * @return void + */ + public function testDependsOnNewUpdate(): void + { + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setCustomAttribute('custom_layout_update_file', 'new'); + $this->attribute->beforeSave($this->category); + $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); + $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + } +} From 2b440370fb1d161c2f15e6b4b5435bc79bf4274d Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 3 Sep 2019 15:11:18 -0500 Subject: [PATCH 0480/2437] MC-18685: Remove custom layout updates from admin --- .../Model/Attribute/Backend/Customlayoutupdate.php | 2 +- .../Category/Attribute/Backend/LayoutUpdate.php | 4 +++- .../Product/Attribute/Backend/LayoutUpdate.php | 4 +++- .../Attribute/Backend/CustomlayoutupdateTest.php | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index 83dbbae7dda57..881fb3a57d3e7 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -80,7 +80,7 @@ private function extractValue(AbstractModel $object, ?string $attributeCode = nu $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); $attribute = $object->getCustomAttribute($attributeCode); - return $object->getData($attributeCode) ?? $attribute ? $attribute->getValue() : null; + return $object->getData($attributeCode) ?? ($attribute ? $attribute->getValue() : null); } /** diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index 4ce590421dcaf..f84b718a4025f 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -43,7 +43,8 @@ public function __construct(LayoutUpdateManager $manager) private function extractValue(Category $category): ?string { $attrCode = $this->getAttribute()->getAttributeCode(); - $value = $category->getData($attrCode); + $attrValue = $category->getCustomAttribute($attrCode); + $value = $category->getData($attrCode) ?? ($attrValue ? $attrValue->getValue() : null); if ($value && $value !== self::VALUE_USE_UPDATE_XML && !in_array($value, $this->manager->fetchAvailableFiles($category), true) @@ -66,6 +67,7 @@ private function extractValue(Category $category): ?string private function setValue(?string $value, Category $object): void { $attrCode = $this->getAttribute()->getAttributeCode(); + $object->setCustomAttribute($attrCode, $value); $object->setData($attrCode, $value); } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index 590ae8c14e684..240c97fd66bd2 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -43,7 +43,8 @@ public function __construct(LayoutUpdateManager $manager) private function extractValue(Product $product): ?string { $attrCode = $this->getAttribute()->getAttributeCode(); - $value = $product->getData($attrCode); + $attrValue = $product->getCustomAttribute($attrCode); + $value = $product->getData($attrCode) ?? ($attrValue ? $attrValue->getValue() : null); if ($value && $value !== self::VALUE_USE_UPDATE_XML && !in_array($value, $this->manager->fetchAvailableFiles($product), true) @@ -66,6 +67,7 @@ private function extractValue(Product $product): ?string private function setValue(?string $value, Product $object): void { $attrCode = $this->getAttribute()->getAttributeCode(); + $object->setCustomAttribute($attrCode, $value); $object->setData($attrCode, $value); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index fa658706a78c2..1a8133f675e19 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -13,6 +13,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; /** * Test 'custom layout' attribute. @@ -88,14 +89,27 @@ public function testImmutable(): void * Check that custom layout update file's values erase the old attribute's value. * * @return void + * @throws \Throwable */ public function testDependsOnNewUpdate(): void { + //New selected file value is set $this->category->setCustomAttribute('custom_layout_update', 'test'); $this->category->setOrigData('custom_layout_update', 'test'); $this->category->setCustomAttribute('custom_layout_update_file', 'new'); $this->attribute->beforeSave($this->category); $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + + //Existing update chosen + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setCustomAttribute('custom_layout_update_file', '__existing__'); + $this->attribute->beforeSave($this->category); + $this->assertEquals('test', $this->category->getCustomAttribute('custom_layout_update')->getValue()); + /** @var AbstractBackend $fileAttribute */ + $fileAttribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); + $fileAttribute->beforeSave($this->category); + $this->assertEquals(null, $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); } } From 22c4ee1413212f4b55cff5014d48966c26582fe5 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Fri, 16 Aug 2019 09:34:55 -0500 Subject: [PATCH 0481/2437] MC-19152: Custom dropdown customer address attribute option id showing on guest checkout - Moving test to EE module --- .../web/js/model/address-converter.js | 1 + .../frontend/web/js/view/billing-address.js | 25 ++++++++++++++++- .../address-renderer/default.js | 27 ++++++++++++++++++- .../web/js/view/shipping-information/list.js | 3 ++- .../web/template/billing-address/details.html | 15 ++--------- .../address-renderer/default.html | 15 ++--------- 6 files changed, 57 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js index 9b20a782c38d9..d319ea5014138 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js @@ -18,6 +18,7 @@ define([ return { /** * Convert address form data to Address object + * * @param {Object} formData * @returns {Object} */ diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js index 59d1daa757138..e728a5c0fcdd5 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/billing-address.js @@ -159,7 +159,6 @@ function ( } addressData['save_in_address_book'] = this.saveInAddressBook() ? 1 : 0; newBillingAddress = createBillingAddress(addressData); - // New address must be selected as a billing address selectBillingAddress(newBillingAddress); checkoutData.setSelectedBillingAddress(newBillingAddress.getKey()); @@ -237,6 +236,30 @@ function ( */ getCode: function (parent) { return _.isFunction(parent.getCode) ? parent.getCode() : 'shared'; + }, + + /** + * Get customer attribute label + * + * @param {*} attribute + * @returns {*} + */ + getCustomAttributeLabel: function (attribute) { + var resultAttribute; + + if (typeof attribute === 'string') { + return attribute; + } + + if (attribute.label) { + return attribute.label; + } + + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + + return resultAttribute && resultAttribute.label || attribute.value; } }); }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js index acc9f1c2391d9..009178cbb19b9 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/address-renderer/default.js @@ -5,8 +5,9 @@ define([ 'uiComponent', + 'underscore', 'Magento_Customer/js/customer-data' -], function (Component, customerData) { +], function (Component, _, customerData) { 'use strict'; var countryData = customerData.get('directory-data'); @@ -22,6 +23,30 @@ define([ */ getCountryName: function (countryId) { return countryData()[countryId] != undefined ? countryData()[countryId].name : ''; //eslint-disable-line + }, + + /** + * Get customer attribute label + * + * @param {*} attribute + * @returns {*} + */ + getCustomAttributeLabel: function (attribute) { + var resultAttribute; + + if (typeof attribute === 'string') { + return attribute; + } + + if (attribute.label) { + return attribute.label; + } + + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + + return resultAttribute && resultAttribute.label || attribute.value; } }); }); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/list.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/list.js index 28eb83c8be3e3..3bb2715c78a7b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/list.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information/list.js @@ -16,7 +16,8 @@ define([ var defaultRendererTemplate = { parent: '${ $.$data.parentName }', name: '${ $.$data.name }', - component: 'Magento_Checkout/js/view/shipping-information/address-renderer/default' + component: 'Magento_Checkout/js/view/shipping-information/address-renderer/default', + provider: 'checkoutProvider' }; return Component.extend({ diff --git a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html index a0827d17d6622..23bbce48fee2c 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/billing-address/details.html @@ -13,19 +13,8 @@ <a if="currentBillingAddress().telephone" attr="'href': 'tel:' + currentBillingAddress().telephone" text="currentBillingAddress().telephone"></a><br/> <each args="data: currentBillingAddress().customAttributes, as: 'element'"> - <if args="typeof element === 'object'"> - <if args="element.label"> - <text args="element.label"/> - </if> - <ifnot args="element.label"> - <if args="element.value"> - <text args="element.value"/> - </if> - </ifnot> - </if> - <if args="typeof element === 'string'"> - <text args="element"/> - </if><br/> + <text args="$parent.getCustomAttributeLabel(element)"/> + <br/> </each> <button visible="!isAddressSameAsShipping()" diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html index 75e061426d816..26dd7742d1da8 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-information/address-renderer/default.html @@ -13,18 +13,7 @@ <a if="address().telephone" attr="'href': 'tel:' + address().telephone" text="address().telephone"></a><br/> <each args="data: address().customAttributes, as: 'element'"> - <if args="typeof element === 'object'"> - <if args="element.label"> - <text args="element.label"/> - </if> - <ifnot args="element.label"> - <if args="element.value"> - <text args="element.value"/> - </if> - </ifnot> - </if> - <if args="typeof element === 'string'"> - <text args="element"/> - </if><br/> + <text args="$parent.getCustomAttributeLabel(element)"/> + <br/> </each> </if> From 333ed65f4efa5c2fc62ea710315af280c910eb10 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 3 Sep 2019 15:36:55 -0500 Subject: [PATCH 0482/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Source/LayoutUpdate.php | 2 +- .../Catalog/Model/Category/DataProvider.php | 16 +++-- .../Product/Attribute/Source/LayoutUpdate.php | 2 +- .../Product/Form/Modifier/LayoutUpdate.php | 2 +- .../Catalog/Controller/CategoryTest.php | 11 ++-- .../Backend/CustomlayoutupdateTest.php | 5 +- .../Model/Category/DataProviderTest.php | 66 +++++++++++++++---- 7 files changed, 77 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php index 2375a5d36a0a8..1ef1e910978e1 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -69,7 +69,7 @@ public function getOptionsFor(CustomAttributesDataInterface $entity): array { $options = $this->getAllOptions(); if ($entity->getCustomAttribute('custom_layout_update')) { - $existingValue = '__existing__'; + $existingValue = \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; $existingLabel = 'Use existing'; $options[] = ['label' => $existingLabel, 'value' => $existingValue]; $this->optionsText[$existingValue] = $existingLabel; diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index cff85d6060e34..bd377f4ab302e 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -365,8 +365,9 @@ public function getAttributesMeta(Type $entityType) } if ($attribute->usesSource()) { $source = $attribute->getSource(); - if ($source instanceof SpecificSourceInterface) { - $options = $source->getOptionsFor($this->getCurrentCategory()); + $currentCategory = $this->getCurrentCategory(); + if ($source instanceof SpecificSourceInterface && $currentCategory) { + $options = $source->getOptionsFor($currentCategory); } else { $options = $attribute->getSource()->getAllOptions(); } @@ -518,6 +519,12 @@ protected function filterFields($categoryData) private function convertValues($category, $categoryData) { foreach ($category->getAttributes() as $attributeCode => $attribute) { + if ($attributeCode === 'custom_layout_update_file') { + if (!empty($categoryData['custom_layout_update'])) { + $categoryData['custom_layout_update_file'] + = \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + } + } if (!isset($categoryData[$attributeCode])) { continue; } @@ -545,11 +552,6 @@ private function convertValues($category, $categoryData) $categoryData[$attributeCode][0]['type'] = $mime; } } - if ($attributeCode === 'custom_layout_update_file') { - if (!empty($categoryData['custom_layout_update'])) { - $categoryData['custom_layout_update_file'] = '__existing__'; - } - } } return $categoryData; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php index d793e110ac25e..78e29002beb25 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -67,7 +67,7 @@ public function getOptionsFor(CustomAttributesDataInterface $entity): array { $options = $this->getAllOptions(); if ($entity->getCustomAttribute('custom_layout_update')) { - $existingValue = '__existing__'; + $existingValue = \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; $existingLabel = 'Use existing'; $options[] = ['label' => $existingLabel, 'value' => $existingValue]; $this->optionsText[$existingValue] = $existingLabel; diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php index 92afe35c70bb5..de0e640e09d76 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php @@ -39,7 +39,7 @@ public function modifyData(array $data) if ($oldLayout = $product->getCustomAttribute('custom_layout_update')) { if ($oldLayout->getValue()) { $data[$product->getId()][AbstractModifier::DATA_SOURCE_DEFAULT]['custom_layout_update_file'] - = '__existing__'; + = \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php index 070ef99c15a34..8b9af5483d563 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Controller; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; @@ -122,12 +123,12 @@ public function testViewWithCustomUpdate(): void /** @var CategoryLayoutUpdateManager $layoutManager */ $layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); $layoutManager->setCategoryFakeFiles($categoryId, [$file]); - /** @var Category $category */ - $category = Bootstrap::getObjectManager()->create(Category::class); - $category->load($categoryId); + /** @var CategoryRepositoryInterface $categoryRepo */ + $categoryRepo = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class); + $category = $categoryRepo->get($categoryId); //Updating the custom attribute. - $category->setData('custom_layout_update_file', $file); - $category->save(); + $category->setCustomAttribute('custom_layout_update_file', $file); + $categoryRepo->save($category); //Viewing the category $this->dispatch("catalog/category/view/id/$categoryId"); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index 1a8133f675e19..dbc14c7d25dae 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -104,7 +104,10 @@ public function testDependsOnNewUpdate(): void //Existing update chosen $this->category->setCustomAttribute('custom_layout_update', 'test'); $this->category->setOrigData('custom_layout_update', 'test'); - $this->category->setCustomAttribute('custom_layout_update_file', '__existing__'); + $this->category->setCustomAttribute( + 'custom_layout_update_file', + \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML + ); $this->attribute->beforeSave($this->category); $this->assertEquals('test', $this->category->getCustomAttribute('custom_layout_update')->getValue()); /** @var AbstractBackend $fileAttribute */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index b8e1f07364c28..e93761f9043b8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -5,11 +5,14 @@ */ namespace Magento\Catalog\Model\Category; -use Magento\Catalog\Model\Category\DataProvider; -use Magento\Eav\Model\Config as EavConfig; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; -class DataProviderTest extends \PHPUnit\Framework\TestCase +class DataProviderTest extends TestCase { /** * @var DataProvider @@ -17,18 +20,23 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase private $dataProvider; /** - * @var \Magento\Eav\Model\Entity\Type + * @var Registry */ - private $entityType; + private $registry; /** - * {@inheritDoc} + * @var CategoryFactory */ - protected function setUp() + private $categoryFactory; + + /** + * Create subject instance. + * + * @return DataProvider + */ + private function createDataProvider(): DataProvider { - parent::setUp(); - $objectManager = Bootstrap::getObjectManager(); - $this->dataProvider = $objectManager->create( + return Bootstrap::getObjectManager()->create( DataProvider::class, [ 'name' => 'category_form_data_source', @@ -36,8 +44,18 @@ protected function setUp() 'requestFieldName' => 'id' ] ); + } - $this->entityType = $objectManager->create(EavConfig::class)->getEntityType('catalog_category'); + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + $objectManager = Bootstrap::getObjectManager(); + $this->dataProvider = $this->createDataProvider(); + $this->registry = $objectManager->get(Registry::class); + $this->categoryFactory = $objectManager->get(CategoryFactory::class); } /** @@ -59,4 +77,30 @@ public function testGetMetaRequiredAttributes() } } } + + /** + * Check that custom layout update file attribute is processed correctly. + * + * @return void + */ + public function testCustomLayoutFileAttribute(): void + { + //File has value + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load($id = 2); + $category->setData('custom_layout_update', null); + $category->setData('custom_layout_update_file', $file = 'test-file'); + $this->registry->register('category', $category); + $data = $this->dataProvider->getData(); + $this->assertEquals($file, $data[$id]['custom_layout_update_file']); + + //File has no value, the deprecated attribute does. + $this->dataProvider = $this->createDataProvider(); + $category->setData('custom_layout_update', $deprecated = 'test-deprecated'); + $category->setData('custom_layout_update_file', null); + $data = $this->dataProvider->getData(); + $this->assertEquals($deprecated, $data[$id]['custom_layout_update']); + $this->assertEquals(LayoutUpdate::VALUE_USE_UPDATE_XML, $data[$id]['custom_layout_update_file']); + } } From 1304bb49e1a8e2a91f946af2ac452ce003e496ff Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 3 Sep 2019 16:00:44 -0500 Subject: [PATCH 0483/2437] MC-18685: Remove custom layout updates from admin --- .../Catalog/Model/CategoryRepositoryTest.php | 102 +++++++----------- 1 file changed, 38 insertions(+), 64 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php index f1e235f8c9bf2..e79023e49d4cf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php @@ -7,14 +7,12 @@ namespace Magento\Catalog\Model; -use Magento\Backend\Model\Auth; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Api\Data\CategoryInterfaceFactory; -use Magento\Framework\Acl\Builder; +use Magento\Catalog\Api\CategoryRepositoryInterfaceFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Bootstrap as TestBootstrap; /** * Provide tests for CategoryRepository model. @@ -22,26 +20,14 @@ class CategoryRepositoryTest extends TestCase { /** - * Test subject. - * - * @var CategoryRepositoryInterface - */ - private $repo; - - /** - * @var Auth - */ - private $auth; - - /** - * @var Builder + * @var CategoryLayoutUpdateManager */ - private $aclBuilder; + private $layoutManager; /** - * @var CategoryInterfaceFactory + * @var CategoryRepositoryInterfaceFactory */ - private $categoryFactory; + private $repositoryFactory; /** * Sets up common objects. @@ -50,63 +36,51 @@ class CategoryRepositoryTest extends TestCase */ protected function setUp() { - $this->repo = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); - $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryInterfaceFactory::class); + $this->repositoryFactory = Bootstrap::getObjectManager()->get(CategoryRepositoryInterfaceFactory::class); + $this->layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); } /** - * @inheritDoc + * Create subject object. + * + * @return CategoryRepositoryInterface */ - protected function tearDown() + private function createRepo(): CategoryRepositoryInterface { - parent::tearDown(); - - $this->auth->logout(); - $this->aclBuilder->resetRuntimeAcl(); + return $this->repositoryFactory->create(); } /** - * Test authorization when saving category's design settings. + * Test that custom layout file attribute is saved. * + * @return void + * @throws \Throwable * @magentoDataFixture Magento/Catalog/_files/category.php - * @magentoAppArea adminhtml * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ - public function testSaveDesign() + public function testCustomLayout(): void { - $category = $this->repo->get(333); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); - - //Admin doesn't have access to category's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); - - $category->setCustomAttribute('custom_design', 2); - $category = $this->repo->save($category); - $customDesignAttribute = $category->getCustomAttribute('custom_design'); - $this->assertTrue(!$customDesignAttribute || !$customDesignAttribute->getValue()); - - //Admin has access to category' design. - $this->aclBuilder->getAcl() - ->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); - - $category->setCustomAttribute('custom_design', 2); - $category = $this->repo->save($category); - $this->assertNotEmpty($category->getCustomAttribute('custom_design')); - $this->assertEquals(2, $category->getCustomAttribute('custom_design')->getValue()); + //New valid value + $repo = $this->createRepo(); + $category = $repo->get(333); + $newFile = 'test'; + $this->layoutManager->setCategoryFakeFiles(333, [$newFile]); + $category->setCustomAttribute('custom_layout_update_file', $newFile); + $repo->save($category); + $repo = $this->createRepo(); + $category = $repo->get(333); + $this->assertEquals($newFile, $category->getCustomAttribute('custom_layout_update_file')->getValue()); - //Creating a new one - /** @var CategoryInterface $newCategory */ - $newCategory = $this->categoryFactory->create(); - $newCategory->setName('new category without design'); - $newCategory->setParentId($category->getParentId()); - $newCategory->setIsActive(true); - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); - $newCategory->setCustomAttribute('custom_design', 2); - $newCategory = $this->repo->save($newCategory); - $customDesignAttribute = $newCategory->getCustomAttribute('custom_design'); - $this->assertTrue(!$customDesignAttribute || !$customDesignAttribute->getValue()); + //Setting non-existent value + $newFile = 'does not exist'; + $category->setCustomAttribute('custom_layout_update_file', $newFile); + $caughtException = false; + try { + $repo->save($category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); } } From c7a746993cbb7d87e6cb3f4b83b7a69d895145c6 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 3 Sep 2019 16:53:17 -0500 Subject: [PATCH 0484/2437] MC-18685: Remove custom layout updates from admin --- .../integration/etc/di/preferences/ce.php | 4 +- .../Model/ProductLayoutUpdateManager.php | 50 +++++++++++++ .../Controller/Adminhtml/CategoryTest.php | 1 + .../Controller/Adminhtml/ProductTest.php | 58 +++++++++++++++ .../Catalog/Controller/ProductTest.php | 37 ++++++++++ .../Catalog/Model/ProductRepositoryTest.php | 73 ++++++++++--------- .../Form/Modifier/LayoutUpdateTest.php | 66 +++++++++++++++++ 7 files changed, 252 insertions(+), 37 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index 87ac54f4591eb..972f7fb3c669b 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -32,5 +32,7 @@ \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class, \Magento\Framework\HTTP\AsyncClientInterface::class => \Magento\TestFramework\HTTP\AsyncClientInterfaceMock::class, \Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager::class => - \Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager::class + \Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager::class, + \Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager::class => + \Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager::class ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php new file mode 100644 index 0000000000000..6c8826583be70 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; + +/** + * Easy way to fake available files. + */ +class ProductLayoutUpdateManager extends LayoutUpdateManager +{ + /** + * @var array Keys are product IDs, values - file names. + */ + private $fakeFiles = []; + + /** + * Supply fake files for a product. + * + * @param int $forProductId + * @param string[]|null $files Pass null to reset. + */ + public function setFakeFiles(int $forProductId, ?array $files): void + { + if ($files === null) { + unset($this->fakeFiles[$forProductId]); + } else { + $this->fakeFiles[$forProductId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(ProductInterface $product): array + { + if (array_key_exists($product->getId(), $this->fakeFiles)) { + return $this->fakeFiles[$product->getId()]; + } + + return parent::fetchAvailableFiles($product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 94a37327fcc39..8b54beeebbebf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -642,6 +642,7 @@ public function testSaveDesign(): void MessageInterface::TYPE_ERROR ); } + /** * Test custom update files functionality. * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 547b282474fe6..67b6ec4e7d70f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Catalog\Model\Category as CategoryModel; use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; @@ -12,6 +13,8 @@ use Magento\Catalog\Model\ProductRepository; use Magento\Catalog\Model\ProductRepositoryFactory; use Magento\Framework\Message\MessageInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; /** @@ -442,4 +445,59 @@ public function testSaveDesign(): void MessageInterface::TYPE_ERROR ); } + + /** + * Test custom update files functionality. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @throws \Throwable + * @return void + */ + public function testSaveCustomLayout(): void + { + $file = 'test_file'; + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + /** @var ProductLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); + $layoutManager->setFakeFiles((int)$product->getId(), [$file]); + $requestData = [ + 'product' => $product->getData() + ]; + $uri = 'backend/catalog/product/save'; + + //Saving a wrong file + $requestData['product']['custom_layout_update_file'] = $file . 'INVALID'; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('id', $product->getId()); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo(['tst']), + MessageInterface::TYPE_ERROR + ); + + //Checking that the value is not saved + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertEmpty($product->getData('custom_layout_update_file')); + + //Saving the correct file + $requestData['product']['custom_layout_update_file'] = $file; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('id', $product->getId()); + $this->dispatch($uri); + + //Checking that the value is saved + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertEquals($file, $product->getData('custom_layout_update_file')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php index ca9db3f28a91b..b4987e425d6b5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php @@ -9,6 +9,12 @@ */ namespace Magento\Catalog\Controller; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; + /** * @magentoAppIsolation enabled */ @@ -186,4 +192,35 @@ public function testImageActionNoImage() $this->assert404NotFound(); } + + /** + * Check that custom layout update files is employed. + * + * @magentoDataFixture Magento/Catalog/controllers/_files/products.php + * @return void + */ + public function testViewWithCustomUpdate(): void + { + //Setting a fake file for the product. + $file = 'test-file'; + /** @var ProductRepositoryInterface $repository */ + $repository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + $sku = 'simple_product_1'; + $product = $repository->get($sku); + $productId = $product->getId(); + /** @var ProductLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); + $layoutManager->setFakeFiles((int)$productId, [$file]); + //Updating the custom attribute. + $product->setCustomAttribute('custom_layout_update_file', $file); + $repository->save($product); + + //Viewing the product + $this->dispatch("catalog/product/view/id/$productId"); + //Layout handles must contain the file. + $handles = Bootstrap::getObjectManager()->get(\Magento\Framework\View\LayoutInterface::class) + ->getUpdate() + ->getHandles(); + $this->assertContains("catalog_product_view_selectable_{$sku}_{$file}", $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php index fa2a0e5cb34b7..fb07d08faca58 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php @@ -11,6 +11,8 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Bootstrap as TestBootstrap; use Magento\Framework\Acl\Builder; @@ -46,15 +48,10 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase */ private $productResource; - /* - * @var Auth - */ - private $auth; - /** - * @var Builder + * @var ProductLayoutUpdateManager */ - private $aclBuilder; + private $layoutManager; /** * Sets up common objects @@ -63,21 +60,19 @@ protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); $this->productFactory = Bootstrap::getObjectManager()->get(ProductFactory::class); $this->productResource = Bootstrap::getObjectManager()->get(ProductResource::class); + $this->layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); } /** - * @inheritDoc + * Create new subject instance. + * + * @return ProductRepositoryInterface */ - protected function tearDown() + private function createRepo(): ProductRepositoryInterface { - parent::tearDown(); - - $this->auth->logout(); - $this->aclBuilder->resetRuntimeAcl(); + return Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } /** @@ -206,30 +201,36 @@ public function testUpdateProductSku() } /** - * Test authorization when saving product's design settings. + * Test that custom layout file attribute is saved. * + * @return void + * @throws \Throwable * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ - public function testSaveDesign() + public function testCustomLayout(): void { - $product = $this->productRepository->get('simple'); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); - - //Admin doesn't have access to product's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); - - $product->setCustomAttribute('custom_design', 2); - $product = $this->productRepository->save($product); - $this->assertEmpty($product->getCustomAttribute('custom_design')); - - //Admin has access to products' design. - $this->aclBuilder->getAcl() - ->allow(null, ['Magento_Catalog::products','Magento_Catalog::edit_product_design']); - - $product->setCustomAttribute('custom_design', 2); - $product = $this->productRepository->save($product); - $this->assertNotEmpty($product->getCustomAttribute('custom_design')); - $this->assertEquals(2, $product->getCustomAttribute('custom_design')->getValue()); + //New valid value + $repo = $this->createRepo(); + $product = $repo->get('simple'); + $newFile = 'test'; + $this->layoutManager->setFakeFiles((int)$product->getId(), [$newFile]); + $product->setCustomAttribute('custom_layout_update_file', $newFile); + $repo->save($product); + $repo = $this->createRepo(); + $product = $repo->get('simple'); + $this->assertEquals($newFile, $product->getCustomAttribute('custom_layout_update_file')->getValue()); + + //Setting non-existent value + $newFile = 'does not exist'; + $product->setCustomAttribute('custom_layout_update_file', $newFile); + $caughtException = false; + try { + $repo->save($product); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php new file mode 100644 index 0000000000000..4ec50dc26da5c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as LayoutUpdateAttribute; + +/** + * Test the modifier. + */ +class LayoutUpdateTest extends TestCase +{ + /** + * @var LayoutUpdate + */ + private $modifier; + + /** + * @var ProductRepositoryInterface + */ + private $repo; + + /** + * @var MockObject + */ + private $locator; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->locator = $this->getMockForAbstractClass(LocatorInterface::class); + $this->modifier = Bootstrap::getObjectManager()->create(LayoutUpdate::class, ['locator' => $this->locator]); + $this->repo = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + } + + /** + * Test that data is being modified accordingly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->repo->get('simple'); + $this->locator->method('getProduct')->willReturn($product); + $product->setCustomAttribute('custom_layout_update', 'something'); + + $data = $this->modifier->modifyData([$product->getId() => ['product' => []]]); + $this->assertEquals( + LayoutUpdateAttribute::VALUE_USE_UPDATE_XML, + $data[$product->getId()]['product']['custom_layout_update_file'] + ); + } +} From 4d7d4a86bd1de4f57825ba53c6e8e65bd63f64e2 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 4 Sep 2019 10:23:10 +0300 Subject: [PATCH 0485/2437] graphQl-892: Improved strict typing in CartAddressInterface --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index c458b5e9dc05d..13114ef97219f 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -201,15 +201,15 @@ type Cart { } interface CartAddressInterface @typeResolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddressTypeResolver") { - firstname: String - lastname: String + firstname: String! + lastname: String! company: String - street: [String] - city: String + street: [String!]! + city: String! region: CartAddressRegion postcode: String - country: CartAddressCountry - telephone: String + country: CartAddressCountry! + telephone: String! customer_notes: String } @@ -229,13 +229,13 @@ type CartItemQuantity { } type CartAddressRegion { - code: String - label: String + code: String! + label: String! } type CartAddressCountry { - code: String - label: String + code: String! + label: String! } type SelectedShippingMethod { From b3f510d037dffb62b70fb14d7f3fba63b9670a43 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 4 Sep 2019 11:29:54 +0300 Subject: [PATCH 0486/2437] graphQl-890: Replaced usage of the CartItemQuantity with the CartItemInterface --- .../Magento/QuoteGraphQl/etc/schema.graphqls | 6 ++-- .../Customer/AddSimpleProductToCartTest.php | 30 +++++++++++++++++++ .../Guest/AddSimpleProductToCartTest.php | 30 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index c458b5e9dc05d..d7741c392e9f7 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -216,14 +216,14 @@ interface CartAddressInterface @typeResolver(class: "\\Magento\\QuoteGraphQl\\Mo type ShippingCartAddress implements CartAddressInterface { available_shipping_methods: [AvailableShippingMethod] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\AvailableShippingMethods") selected_shipping_method: SelectedShippingMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\SelectedShippingMethod") - items_weight: Float - cart_items: [CartItemQuantity] + items_weight: Float @deprecated + cart_items: [CartItemInterface] } type BillingCartAddress implements CartAddressInterface { } -type CartItemQuantity { +type CartItemQuantity @deprecated(reason: "Use CartItemInterface instead") { cart_item_id: Int! quantity: Float! } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php index e1b93e0bdb857..19251b3741106 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php @@ -52,6 +52,22 @@ public function testAddSimpleProductToCart() self::assertArrayHasKey('cart', $response['addSimpleProductsToCart']); self::assertEquals($quantity, $response['addSimpleProductsToCart']['cart']['items'][0]['quantity']); self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); + self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); + + self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']); + self::assertEquals(10, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['currency']); + + self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']); + self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['currency']); + + self::assertArrayHasKey('row_total_including_tax', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']); + self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['currency']); } /** @@ -262,6 +278,20 @@ private function getQuery(string $maskedQuoteId, string $sku, float $quantity): product { sku } + prices { + price { + value + currency + } + row_total { + value + currency + } + row_total_including_tax { + value + currency + } + } } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 4deed99243f9d..cc7365fb7bcf2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -47,6 +47,22 @@ public function testAddSimpleProductToCart() self::assertEquals($quantity, $response['addSimpleProductsToCart']['cart']['items'][0]['quantity']); self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); + self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); + + self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']); + self::assertEquals(10, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['currency']); + + self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']); + self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['currency']); + + self::assertArrayHasKey('row_total_including_tax', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']); + self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['value']); + self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['currency']); } /** @@ -231,6 +247,20 @@ private function getQuery(string $maskedQuoteId, string $sku, float $quantity): product { sku } + prices { + price { + value + currency + } + row_total { + value + currency + } + row_total_including_tax { + value + currency + } + } } } } From f7ab08e6103ad1a1b503b6226e9edad53f6e7700 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 4 Sep 2019 11:56:31 +0300 Subject: [PATCH 0487/2437] graphQl-890: fixed static tests --- .../Customer/AddSimpleProductToCartTest.php | 30 ++++++++++++------- .../Guest/AddSimpleProductToCartTest.php | 30 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php index 19251b3741106..f861b9db98fe7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php @@ -55,19 +55,29 @@ public function testAddSimpleProductToCart() self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']); - self::assertEquals(10, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['currency']); + $price = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']; + self::assertArrayHasKey('value', $price); + self::assertEquals(10, $price['value']); + self::assertArrayHasKey('currency', $price); + self::assertEquals('USD', $price['currency']); self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']); - self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['currency']); + $rowTotal = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']; + self::assertArrayHasKey('value', $rowTotal); + self::assertEquals(20, $rowTotal['value']); + self::assertArrayHasKey('currency', $rowTotal); + self::assertEquals('USD', $rowTotal['currency']); - self::assertArrayHasKey('row_total_including_tax', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']); - self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['currency']); + self::assertArrayHasKey( + 'row_total_including_tax', + $response['addSimpleProductsToCart']['cart']['items'][0]['prices'] + ); + $rowTotalIncludingTax = + $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']; + self::assertArrayHasKey('value', $rowTotalIncludingTax); + self::assertEquals(20, $rowTotalIncludingTax['value']); + self::assertArrayHasKey('currency', $rowTotalIncludingTax); + self::assertEquals('USD', $rowTotalIncludingTax['currency']); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index cc7365fb7bcf2..1a3a1c8a738e5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -50,19 +50,29 @@ public function testAddSimpleProductToCart() self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']); - self::assertEquals(10, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']['currency']); + $price = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']; + self::assertArrayHasKey('value', $price); + self::assertEquals(10, $price['value']); + self::assertArrayHasKey('currency', $price); + self::assertEquals('USD', $price['currency']); self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']); - self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']['currency']); + $rowTotal = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']; + self::assertArrayHasKey('value', $rowTotal); + self::assertEquals(20, $rowTotal['value']); + self::assertArrayHasKey('currency', $rowTotal); + self::assertEquals('USD', $rowTotal['currency']); - self::assertArrayHasKey('row_total_including_tax', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); - self::assertArrayHasKey('value', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']); - self::assertEquals(20, $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['value']); - self::assertEquals('USD', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']['currency']); + self::assertArrayHasKey( + 'row_total_including_tax', + $response['addSimpleProductsToCart']['cart']['items'][0]['prices'] + ); + $rowTotalIncludingTax = + $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']; + self::assertArrayHasKey('value', $rowTotalIncludingTax); + self::assertEquals(20, $rowTotalIncludingTax['value']); + self::assertArrayHasKey('currency', $rowTotalIncludingTax); + self::assertEquals('USD', $rowTotalIncludingTax['currency']); } /** From 60ce6fad3d6ce5c32cfdd3b3b5cf711b7183b7f6 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Tue, 3 Sep 2019 14:26:10 +0300 Subject: [PATCH 0488/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MAGETWO-99376 --- .../Sales/Model/ResourceModel/OrderTest.php | 55 +++++++++++++++++++ .../Store/_files/store_with_long_name.php | 50 +++++++++++++++++ .../_files/store_with_long_name_rollback.php | 34 ++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php create mode 100644 dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php index d6fe86b6232d3..9e102fab1d963 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php @@ -3,8 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\ResourceModel; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\Event\ManagerInterface; + class OrderTest extends \PHPUnit\Framework\TestCase { /** @@ -22,13 +28,31 @@ class OrderTest extends \PHPUnit\Framework\TestCase */ protected $objectManager; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->resourceModel = $this->objectManager->create(\Magento\Sales\Model\ResourceModel\Order::class); $this->orderIncrementId = '100000001'; + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); } + /** + * @inheritdoc + */ protected function tearDown() { $registry = $this->objectManager->get(\Magento\Framework\Registry::class); @@ -43,6 +67,9 @@ protected function tearDown() $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); + $defaultStore = $this->storeRepository->get('default'); + $this->storeManager->setCurrentStore($defaultStore->getId()); + parent::tearDown(); } @@ -108,4 +135,32 @@ public function testSaveOrder() $this->assertNotNull($order->getCreatedAt()); $this->assertNotNull($order->getUpdatedAt()); } + + /** + * Check that store name with length within 255 chars can be saved in table sales_order + * + * @magentoDataFixture Magento/Store/_files/store_with_long_name.php + * @return void + */ + public function testSaveStoreName() + { + $storeName = str_repeat('a', 220); + $store = $this->storeRepository->get('test_2'); + $this->storeManager->setCurrentStore($store->getId()); + $eventManager = $this->objectManager->get(ManagerInterface::class); + $eventManager->dispatch('store_add', ['store' => $store]); + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $payment = $this->objectManager->create(\Magento\Sales\Model\Order\Payment::class); + $payment->setMethod('checkmo'); + $order->setStoreId($store->getId())->setPayment($payment); + $this->resourceModel->save($order); + $this->resourceModel->load($order, $storeName, 'store_name'); + $name = [ + 'Main Website', + 'Main Website Store', + $storeName, + ]; + $expectedStoreName = implode(PHP_EOL, $name); + $this->assertEquals($expectedStoreName, $order->getStoreName()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php new file mode 100644 index 0000000000000..c225519e2b2b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var $store \Magento\Store\Model\Store */ +$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); +$storeName = str_repeat('a', 220); +if (!$store->load('test', 'code')->getId()) { + $store->setData( + [ + 'code' => 'test_2', + 'website_id' => '1', + 'group_id' => '1', + 'name' => $storeName, + 'sort_order' => '10', + 'is_active' => '1', + ] + ); + $store->save(); +} else { + if ($store->getId()) { + /** @var \Magento\TestFramework\Helper\Bootstrap $registry */ + $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\Framework\Registry::class + ); + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + $store->delete(); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + + $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); + $store->setData( + [ + 'code' => 'test_2', + 'website_id' => '1', + 'group_id' => '1', + 'name' => $storeName, + 'sort_order' => '10', + 'is_active' => '1', + ] + ); + $store->save(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name_rollback.php new file mode 100644 index 0000000000000..5fe19e1e97df1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var Store $store */ +$store = $objectManager->get(Store::class); +$store->load('test_2', 'code'); +if ($store->getId()) { + $store->delete(); +} + +/** @var Store $store */ +$store = $objectManager->get(Store::class); +$store->load('test_2', 'code'); +if ($store->getId()) { + $store->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From ab5bc5d9f81beb38aa60cab17cfe38e1cb5f2b04 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 4 Sep 2019 16:14:32 +0400 Subject: [PATCH 0489/2437] MC-18820: Increase test coverage for Admin functional area - Automation test for MC-6310 --- .../ActionGroup/AdminElasticConnectionTestActionGroup.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml index e40bf3691e8db..27b53b7c3e6dd 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml @@ -15,8 +15,9 @@ <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="waitForConnectionButton"/> <click selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="clickOnTestConnectionButton"/> <waitForPageLoad stepKey="waitForConnectionEstablishment"/> - <see selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" userInput="Successful! Test again?" stepKey="checkThatConnectionExists"/> + <grabTextFrom selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" stepKey="grabConnectionStatus"/> + <assertEquals expected="Successful! Test again?" expectedType="string" actual="$grabConnectionStatus" stepKey="assertThatConnectionSuccessful"/> <scrollTo selector="{{AdminConfigCatalogCategoryPermissionsSection.catalogPermissionsTab}}" stepKey="scrollToCatalogPermissionsTab"/> <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="closeCatalogSearchTab"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> From cde61373e25ea844d6e28dba45ab5008c3b88d6c Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 4 Sep 2019 12:00:07 -0500 Subject: [PATCH 0490/2437] MC-18685: Remove custom layout updates from admin --- .../Catalog/Controller/Adminhtml/Category.php | 18 +++-- .../Controller/Adminhtml/Category/Save.php | 4 +- .../Product/Initialization/Helper.php | 12 ++- .../Magento/Catalog/Helper/Product/View.php | 1 + .../Attribute/Backend/LayoutUpdate.php | 1 + .../Attribute/Source/LayoutUpdate.php | 1 + .../Category/Authorization.php | 8 +- app/code/Magento/Catalog/Model/Design.php | 2 + app/code/Magento/Catalog/Model/Product.php | 4 +- .../Attribute/Backend/LayoutUpdate.php | 1 + .../Product/Authorization.php | 62 ++++++++------- .../Observer/CategoryDesignAuthorization.php | 3 +- .../Catalog/Plugin/CategoryAuthorization.php | 3 +- .../Catalog/Plugin/ProductAuthorization.php | 3 +- .../Adminhtml/Category/MoveTest.php | 3 - .../Cms/Controller/Adminhtml/Page/Save.php | 38 ++++++--- .../Cms/Controller/Page/Authorization.php | 59 +++++++------- app/code/Magento/Cms/Helper/Page.php | 7 +- .../Page/CustomLayoutManagerInterface.php | 1 - .../Magento/Cms/Model/Page/DataProvider.php | 1 + .../Magento/Cms/Observer/PageAclPlugin.php | 1 + .../Controller/Adminhtml/Page/SaveTest.php | 4 +- app/code/Magento/Cms/composer.json | 3 +- .../Catalog/Api/CategoryRepositoryTest.php | 77 +++++++++++-------- .../Api/ProductRepositoryInterfaceTest.php | 8 +- .../Magento/Cms/Api/PageRepositoryTest.php | 2 + .../Controller/Adminhtml/CategoryTest.php | 6 +- 27 files changed, 202 insertions(+), 131 deletions(-) rename app/code/Magento/Catalog/{Controller/Adminhtml => Model}/Category/Authorization.php (92%) rename app/code/Magento/Catalog/{Controller/Adminhtml => Model}/Product/Authorization.php (53%) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php index 61fd714d518ba..c32ceb5109e7c 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php @@ -40,6 +40,7 @@ public function __construct( /** * Initialize requested category and put it into registry. + * * Root category can be returned, if inappropriate store/category is specified * * @param bool $getRootInstead @@ -98,6 +99,7 @@ private function resolveCategoryId() : int * Resolve store id * * Tries to take store id from store HTTP parameter + * * @see Store * * @return int @@ -142,13 +144,15 @@ protected function ajaxRequestResponse($category, $resultPage) } } - $eventResponse = new \Magento\Framework\DataObject([ - 'content' => $resultPage->getLayout()->getUiComponent('category_form')->getFormHtml() - . $resultPage->getLayout()->getBlock('category.tree') - ->getBreadcrumbsJavascript($breadcrumbsPath, 'editingCategoryBreadcrumbs'), - 'messages' => $resultPage->getLayout()->getMessagesBlock()->getGroupedHtml(), - 'toolbar' => $resultPage->getLayout()->getBlock('page.actions.toolbar')->toHtml() - ]); + $eventResponse = new \Magento\Framework\DataObject( + [ + 'content' => $resultPage->getLayout()->getUiComponent('category_form')->getFormHtml() + . $resultPage->getLayout()->getBlock('category.tree') + ->getBreadcrumbsJavascript($breadcrumbsPath, 'editingCategoryBreadcrumbs'), + 'messages' => $resultPage->getLayout()->getMessagesBlock()->getGroupedHtml(), + 'toolbar' => $resultPage->getLayout()->getBlock('page.actions.toolbar')->toHtml() + ] + ); $this->_eventManager->dispatch( 'category_prepare_ajax_response', ['response' => $eventResponse, 'controller' => $this] diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 95ef5e1279e1e..9812dda73fd3e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -211,7 +211,7 @@ public function execute() __('The "%1" attribute is required. Enter and try again.', $attribute) ); } else { - throw new \Exception($error); + throw new \RuntimeException($error); } } } @@ -220,10 +220,12 @@ public function execute() $category->save(); $this->messageManager->addSuccessMessage(__('You saved the category.')); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addExceptionMessage($e); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $this->_getSession()->setCategoryData($categoryPostData); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { $this->messageManager->addErrorMessage(__('Something went wrong while saving the category.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 2494412326075..b3ee23c9caf58 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -17,7 +17,7 @@ use Magento\Catalog\Model\Product\LinkTypeProvider; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; -use Magento\Catalog\Controller\Adminhtml\Product\Authorization as ProductAuthorization; +use Magento\Catalog\Model\Product\Authorization as ProductAuthorization; /** * Product helper @@ -116,6 +116,7 @@ class Helper * @param ProductRepositoryInterface|null $productRepository * @param LinkTypeProvider|null $linkTypeProvider * @param AttributeFilter|null $attributeFilter + * @param ProductAuthorization|null $productAuthorization * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -459,9 +460,12 @@ private function fillProductOptions(Product $product, array $productOptions) } if (isset($customOptionData['values'])) { - $customOptionData['values'] = array_filter($customOptionData['values'], function ($valueData) { - return empty($valueData['is_delete']); - }); + $customOptionData['values'] = array_filter( + $customOptionData['values'], + function ($valueData) { + return empty($valueData['is_delete']); + } + ); } $customOption = $this->customOptionFactory->create(['data' => $customOptionData]); diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 26776500438e5..cf5b15cadc997 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -86,6 +86,7 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper * @param array $messageGroups * @param \Magento\Framework\Stdlib\StringUtils|null $string * @param LayoutUpdateManager|null $layoutUpdateManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Helper\Context $context, diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index f84b718a4025f..ee10b170f0d84 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -73,6 +73,7 @@ private function setValue(?string $value, Category $object): void /** * @inheritDoc + * * @param Category $object */ public function validate($object) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php index 1ef1e910978e1..648fe2c57290d 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -63,6 +63,7 @@ public function getOptionText($value) /** * @inheritDoc + * * @param CategoryInterface $entity */ public function getOptionsFor(CustomAttributesDataInterface $entity): array diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php similarity index 92% rename from app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php rename to app/code/Magento/Catalog/Model/Category/Authorization.php index 023839a16ee89..a40db89e35906 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Authorization.php +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -6,10 +6,10 @@ declare(strict_types=1); -namespace Magento\Catalog\Controller\Adminhtml\Category; +namespace Magento\Catalog\Model\Category; use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category as CategoryModel; use Magento\Catalog\Model\CategoryFactory; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Framework\AuthorizationInterface; @@ -46,7 +46,7 @@ public function __construct(AuthorizationInterface $authorization, CategoryFacto * * @throws AuthorizationException * @throws NoSuchEntityException When a category with invalid ID given. - * @param CategoryInterface|Category $category + * @param CategoryInterface|CategoryModel $category * @return void */ public function authorizeSavingOf(CategoryInterface $category): void @@ -67,7 +67,7 @@ function (AttributeInterface $attribute) { } } } else { - /** @var Category $savedCategory */ + /** @var CategoryModel $savedCategory */ $savedCategory = $this->categoryFactory->create(); $savedCategory->load($category->getId()); if (!$savedCategory->getName()) { diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php index 854f8f5648926..fed18a5a60913 100644 --- a/app/code/Magento/Catalog/Model/Design.php +++ b/app/code/Magento/Catalog/Model/Design.php @@ -17,6 +17,7 @@ * * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Design extends \Magento\Framework\Model\AbstractModel { @@ -62,6 +63,7 @@ class Design extends \Magento\Framework\Model\AbstractModel * @param TranslateInterface|null $translator * @param CategoryLayoutManager|null $categoryLayoutManager * @param ProductLayoutManager|null $productLayoutManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 93f5da70d40ea..560a1fbacda41 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -825,10 +825,10 @@ public function getStoreIds() } foreach ($websiteIds as $websiteId) { $websiteStores = $this->_storeManager->getWebsite($websiteId)->getStoreIds(); - $storeIds = array_merge($storeIds, $websiteStores); + $storeIds[] = $websiteStores; } } - $this->setStoreIds($storeIds); + $this->setStoreIds(array_merge(...$storeIds)); } return $this->getData('store_ids'); } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index 240c97fd66bd2..e71e27cac6dd0 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -73,6 +73,7 @@ private function setValue(?string $value, Product $object): void /** * @inheritDoc + * * @param Product $object */ public function validate($object) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php similarity index 53% rename from app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php rename to app/code/Magento/Catalog/Model/Product/Authorization.php index d1ee659ce8175..9a5e8fc396dd5 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Authorization.php +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -6,10 +6,10 @@ declare(strict_types=1); -namespace Magento\Catalog\Controller\Adminhtml\Product; +namespace Magento\Catalog\Model\Product; use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product as ProductModel; use Magento\Catalog\Model\ProductFactory; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; @@ -40,47 +40,55 @@ public function __construct(AuthorizationInterface $authorization, ProductFactor $this->productFactory = $factory; } + /** + * Check whether the product has changed. + * + * @param ProductModel $product + * @param ProductModel|null $oldProduct + * @return bool + */ + private function hasProductChanged(ProductModel $product, ?ProductModel $oldProduct = null): bool + { + $designAttributes = [ + 'custom_design', + 'page_layout', + 'options_container', + 'custom_layout_update', + 'custom_design_from', + 'custom_design_to', + 'custom_layout_update_file' + ]; + foreach ($designAttributes as $designAttribute) { + $oldValue = $oldProduct ? $oldProduct->getData($designAttribute) : null; + if ($product->getData($designAttribute) != $oldValue) { + return true; + } + } + + return false; + } + /** * Authorize saving of a product. * * @throws AuthorizationException * @throws NoSuchEntityException When product with invalid ID given. - * @param ProductInterface|Product $product + * @param ProductInterface|ProductModel $product * @return void */ public function authorizeSavingOf(ProductInterface $product): void { if (!$this->authorization->isAllowed('Magento_Catalog::edit_product_design')) { - $notAllowed = false; - if (!$product->getId()) { - if ($product->getData('custom_design') - || $product->getData('page_layout') - || $product->getData('options_container') - || $product->getData('custom_layout_update') - || $product->getData('custom_design_from') - || $product->getData('custom_design_to') - ) { - $notAllowed = true; - } - } else { - /** @var Product $savedProduct */ + $savedProduct = null; + if ($product->getId()) { + /** @var ProductModel $savedProduct */ $savedProduct = $this->productFactory->create(); $savedProduct->load($product->getId()); if (!$savedProduct->getSku()) { throw NoSuchEntityException::singleField('id', $product->getId()); } - if ($product->getData('custom_design') != $savedProduct->getData('custom_design') - || $product->getData('page_layout') != $savedProduct->getData('page_layout') - || $product->getData('options_container') != $savedProduct->getData('options_container') - || $product->getData('custom_layout_update') != $savedProduct->getData('custom_layout_update') - || $product->getData('custom_design_from') != $savedProduct->getData('custom_design_from') - || $product->getData('custom_design_to') != $savedProduct->getData('custom_design_to') - ) { - $notAllowed = true; - } } - - if ($notAllowed) { + if ($this->hasProductChanged($product, $savedProduct)) { throw new AuthorizationException(__('Not allowed to edit the product\'s design attributes')); } } diff --git a/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php index b6486bd9256b1..94977485b95b3 100644 --- a/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php +++ b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php @@ -8,7 +8,7 @@ namespace Magento\Catalog\Observer; use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Controller\Adminhtml\Category\Authorization; +use Magento\Catalog\Model\Category\Authorization; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; use Magento\Framework\Exception\AuthorizationException; @@ -33,6 +33,7 @@ public function __construct(Authorization $authorization) /** * @inheritDoc + * * @throws AuthorizationException */ public function execute(Observer $observer) diff --git a/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php index 1e4f2ddc38b82..af2dccb96f937 100644 --- a/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php +++ b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Controller\Adminhtml\Category\Authorization; +use Magento\Catalog\Model\Category\Authorization; use Magento\Framework\Exception\LocalizedException; /** @@ -38,6 +38,7 @@ public function __construct(Authorization $authorization) * @param CategoryInterface $category * @throws LocalizedException * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSave(CategoryRepositoryInterface $subject, CategoryInterface $category): array { diff --git a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php index 2de7c1d5bc681..57a928731be93 100644 --- a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php +++ b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php @@ -10,7 +10,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Controller\Adminhtml\Product\Authorization; +use Magento\Catalog\Model\Product\Authorization; use Magento\Framework\Exception\LocalizedException; /** @@ -39,6 +39,7 @@ public function __construct(Authorization $authorization) * @param bool $saveOptions * @throws LocalizedException * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSave(ProductRepositoryInterface $subject, ProductInterface $product, $saveOptions): array { diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php index 746d3340a6605..7b151499d2d08 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php @@ -134,7 +134,6 @@ public function testExecuteWithGenericException() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $categoryMock->expects($this->once()) ->method('move') @@ -208,7 +207,6 @@ public function testExecuteWithLocalizedException() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $this->messageManager->expects($this->once()) ->method('addExceptionMessage'); @@ -280,7 +278,6 @@ public function testSuccessfulCategorySave() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $this->messageManager->expects($this->once()) ->method('getMessages') diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 974bc044e3ab0..1e4896c449d59 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -16,6 +16,8 @@ /** * Save CMS page action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterface { @@ -57,6 +59,7 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac * @param DataPersistorInterface $dataPersistor * @param \Magento\Cms\Model\PageFactory|null $pageFactory * @param \Magento\Cms\Api\PageRepositoryInterface|null $pageRepository + * @param CustomLayoutRepositoryInterface|null $customLayoutRepository */ public function __construct( Action\Context $context, @@ -103,7 +106,6 @@ public function execute() $customLayoutFile = null; } - /** @var \Magento\Cms\Model\Page $model */ $model = $this->pageFactory->create(); @@ -125,21 +127,12 @@ public function execute() ['page' => $model, 'request' => $this->getRequest()] ); - if (!$this->dataProcessor->validate($data)) { - return $resultRedirect->setPath('*/*/edit', ['page_id' => $model->getId(), '_current' => true]); - } - - $this->pageRepository->save($model); - if ($customLayoutFile) { - $this->customLayoutRepository->save(new CustomLayoutSelected($model->getId(), $customLayoutFile)); - } else { - $this->customLayoutRepository->deleteFor($model->getId()); - } + $this->savePage($model, $customLayoutFile); $this->messageManager->addSuccessMessage(__('You saved the page.')); return $this->processResultRedirect($model, $resultRedirect, $data); } catch (LocalizedException $e) { $this->messageManager->addExceptionMessage($e->getPrevious() ?: $e); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the page.')); } @@ -149,6 +142,27 @@ public function execute() return $resultRedirect->setPath('*/*/'); } + /** + * Save the page. + * + * @param Page $page + * @param string|null $customLayoutFile + * @return void + * @throws \Throwable + */ + private function savePage(Page $page, ?string $customLayoutFile): void + { + if (!$this->dataProcessor->validate($page->getData())) { + throw new \InvalidArgumentException('Page is invalid'); + } + $this->pageRepository->save($page); + if ($customLayoutFile) { + $this->customLayoutRepository->save(new CustomLayoutSelected($page->getId(), $customLayoutFile)); + } else { + $this->customLayoutRepository->deleteFor($page->getId()); + } + } + /** * Process result redirect * diff --git a/app/code/Magento/Cms/Controller/Page/Authorization.php b/app/code/Magento/Cms/Controller/Page/Authorization.php index f223a5c4d164c..a5ac659dd80f6 100644 --- a/app/code/Magento/Cms/Controller/Page/Authorization.php +++ b/app/code/Magento/Cms/Controller/Page/Authorization.php @@ -41,6 +41,35 @@ public function __construct( $this->authorization = $authorization; } + /** + * Check whether the design fields have been changed. + * + * @param PageInterface $page + * @param PageInterface|null $oldPage + * @return bool + */ + private function hasPageChanged(PageInterface $page, ?PageInterface $oldPage): bool + { + $oldUpdateXml = $oldPage ? $oldPage->getLayoutUpdateXml() : null; + $oldPageLayout = $oldPage ? $oldPage->getPageLayout() : null; + $oldCustomTheme = $oldPage ? $oldPage->getCustomTheme() : null; + $oldLayoutUpdate = $oldPage ? $oldPage->getCustomLayoutUpdateXml() : null; + $oldThemeFrom = $oldPage ? $oldPage->getCustomThemeFrom() : null; + $oldThemeTo = $oldPage ? $oldPage->getCustomThemeTo() : null; + + if ($page->getLayoutUpdateXml() !== $oldUpdateXml + || $page->getPageLayout() !== $oldPageLayout + || $page->getCustomTheme() !== $oldCustomTheme + || $page->getCustomLayoutUpdateXml() !== $oldLayoutUpdate + || $page->getCustomThemeFrom() !== $oldThemeFrom + || $page->getCustomThemeTo() !== $oldThemeTo + ) { + return true; + } + + return false; + } + /** * Authorize user before updating a page. * @@ -53,33 +82,11 @@ public function authorizeFor(PageInterface $page): void { //Validate design changes. if (!$this->authorization->isAllowed('Magento_Cms::save_design')) { - $notAllowed = false; - if (!$page->getId()) { - if ($page->getLayoutUpdateXml() - || $page->getPageLayout() - || $page->getCustomTheme() - || $page->getCustomLayoutUpdateXml() - || $page->getCustomThemeFrom() - || $page->getCustomThemeTo() - ) { - //Not allowed to set design properties value for new pages. - $notAllowed = true; - } - } else { - $savedPage = $this->pageRepository->getById($page->getId()); - if ($page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml() - || $page->getPageLayout() !== $savedPage->getPageLayout() - || $page->getCustomTheme() !== $savedPage->getCustomTheme() - || $page->getCustomThemeTo() !== $savedPage->getCustomThemeTo() - || $page->getCustomThemeFrom() !== $savedPage->getCustomThemeFrom() - || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml() - ) { - //Not allowed to update design settings. - $notAllowed = true; - } + $oldPage = null; + if ($page->getId()) { + $oldPage = $this->pageRepository->getById($page->getId()); } - - if ($notAllowed) { + if ($this->hasPageChanged($page, $oldPage)) { throw new AuthorizationException( __('You are not allowed to change CMS pages design settings') ); diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 634833ff45a23..bbef6fc059c36 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -103,6 +103,7 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\Escaper $escaper * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param CustomLayoutManagerInterface|null $customLayoutManager + * @param CustomLayoutRepositoryInterface|null $customLayoutRepo * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -176,7 +177,11 @@ public function prepareResultPage(Action $action, $pageId = null) $pageHandles = ['id' => str_replace('/', '_', $this->_page->getIdentifier())]; //Selected custom updates. try { - $this->customLayoutManager->applyUpdate($resultPage, $this->customLayoutRepo->getFor($this->_page->getId())); + $this->customLayoutManager->applyUpdate( + $resultPage, + $this->customLayoutRepo->getFor($this->_page->getId()) + ); + // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } catch (NoSuchEntityException $exception) { //No custom layout selected } diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php index 8d66ff36c846e..6f15fcef7f8f4 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php @@ -25,7 +25,6 @@ interface CustomLayoutManagerInterface */ public function fetchAvailableFiles(PageInterface $page): array; - /** * Apply the page's layout settings. * diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index 801440eaf0bcc..fb0b4eeefa84a 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -59,6 +59,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param AuthorizationInterface|null $auth * @param RequestInterface|null $request * @param CustomLayoutManagerInterface|null $customLayoutManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, diff --git a/app/code/Magento/Cms/Observer/PageAclPlugin.php b/app/code/Magento/Cms/Observer/PageAclPlugin.php index c8dc98f6f2807..b7e12fb7cb4b1 100644 --- a/app/code/Magento/Cms/Observer/PageAclPlugin.php +++ b/app/code/Magento/Cms/Observer/PageAclPlugin.php @@ -37,6 +37,7 @@ public function __construct(Authorization $authorization) * @param PageInterface $page * @return array * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSave(PageRepositoryInterface $subject, PageInterface $page): array { diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php index 26b4055923107..bc6703eaf4426 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php @@ -157,6 +157,7 @@ public function testSaveAction() $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page); $page->expects($this->once())->method('setData'); + $page->method('getId')->willReturn($this->pageId); $this->pageRepository->expects($this->once())->method('save')->with($page); $this->dataPersistorMock->expects($this->any()) @@ -235,6 +236,7 @@ public function testSaveAndContinue() $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class) ->disableOriginalConstructor() ->getMock(); + $page->method('getId')->willReturn(1); $this->pageFactory->expects($this->atLeastOnce()) ->method('create') ->willReturn($page); @@ -293,7 +295,7 @@ public function testSaveActionThrowsException() $this->dataPersistorMock->expects($this->any()) ->method('set') - ->with('cms_page', ['page_id' => $this->pageId]); + ->with('cms_page', ['page_id' => $this->pageId, 'custom_layout_update_xml' => null]); $this->resultRedirect->expects($this->atLeastOnce()) ->method('setPath') diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index d04587bbdd728..91036d31fdc2b 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -15,8 +15,7 @@ "magento/module-theme": "*", "magento/module-ui": "*", "magento/module-variable": "*", - "magento/module-widget": "*", - "magento/module-authorization": "*" + "magento/module-widget": "*" }, "suggest": { "magento/module-cms-sample-data": "*" diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 7e010d7631eed..510a4f1594fff 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -16,6 +16,11 @@ use Magento\Authorization\Model\RoleFactory; use Magento\Authorization\Model\RulesFactory; +/** + * Test repository web API. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CategoryRepositoryTest extends WebapiAbstract { const RESOURCE_PATH = '/V1/categories'; @@ -323,23 +328,53 @@ protected function updateCategory($id, $data, ?string $token = null) } /** - * Test design settings authorization + * Update admin role resources list. * - * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php - * @throws \Throwable + * @param string $roleName + * @param string[] $resources * @return void */ - public function testSaveDesign(): void + private function updateRoleResources(string $roleName, array $resources): void { - //Updating our admin user's role to allow saving categories but not their design settings. /** @var Role $role */ $role = $this->roleFactory->create(); - $role->load('test_custom_role', 'role_name'); + $role->load($roleName, 'role_name'); /** @var Rules $rules */ $rules = $this->rulesFactory->create(); $rules->setRoleId($role->getId()); - $rules->setResources(['Magento_Catalog::categories']); + $rules->setResources($resources); $rules->saveRel(); + } + + /** + * Extract error returned by the server. + * + * @param \Throwable $exception + * @return string + */ + private function extractCallExceptionMessage(\Throwable $exception): string + { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + return $restResponse['message']; + } else { + //SOAP + return $exception->getMessage(); + } + } + + /** + * Test design settings authorization + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving categories but not their design settings. + $roleName = 'test_custom_role'; + $this->updateRoleResources($roleName, ['Magento_Catalog::categories']); //Using the admin user with custom role. $token = $this->adminTokens->createAdminAccessToken( 'customRoleUser', @@ -354,23 +389,13 @@ public function testSaveDesign(): void try { $this->createCategory($categoryData, $token); } catch (\Throwable $exception) { - if ($restResponse = json_decode($exception->getMessage(), true)) { - //REST - $exceptionMessage = $restResponse['message']; - } else { - //SOAP - $exceptionMessage = $exception->getMessage(); - } + $exceptionMessage = $this->extractCallExceptionMessage($exception); } //We don't have the permissions. $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); //Updating the user role to allow access to design properties. - /** @var Rules $rules */ - $rules = Bootstrap::getObjectManager()->create(Rules::class); - $rules->setRoleId($role->getId()); - $rules->setResources(['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); - $rules->saveRel(); + $this->updateRoleResources($roleName, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); //Making the same request with design settings. $categoryData = $this->getSimpleCategoryData(); foreach ($categoryData['custom_attributes'] as &$attribute) { @@ -394,11 +419,7 @@ public function testSaveDesign(): void $categoryData = $categorySaved; //Updating our role to remove design properties access. - /** @var Rules $rules */ - $rules = Bootstrap::getObjectManager()->create(Rules::class); - $rules->setRoleId($role->getId()); - $rules->setResources(['Magento_Catalog::categories']); - $rules->saveRel(); + $this->updateRoleResources($roleName, ['Magento_Catalog::categories']); //Updating the category but with the same design properties values. $result = $this->updateCategory($categoryData['id'], $categoryData, $token); //We haven't changed the design so operation is successful. @@ -414,13 +435,7 @@ public function testSaveDesign(): void try { $this->updateCategory($categoryData['id'], $categoryData, $token); } catch (\Throwable $exception) { - if ($restResponse = json_decode($exception->getMessage(), true)) { - //REST - $exceptionMessage = $restResponse['message']; - } else { - //SOAP - $exceptionMessage = $exception->getMessage(); - } + $exceptionMessage = $this->extractCallExceptionMessage($exception); } //We don't have permissions to do that. $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index 58ad18dcaf29c..cb0c793356fee 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -764,11 +764,9 @@ public function testUpdateWithExtensionAttributes(): void protected function updateProduct($product, ?string $token = null) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { - if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' - && !is_array($product['custom_attributes'][$i]['value']) - ) { - $product['custom_attributes'][$i]['value'] = [""]; + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' && !is_array($attribute['value'])) { + $attribute['value'] = [""]; } } } diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index 66cb91e528dff..015eb067e4c8e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -20,6 +20,8 @@ /** * Tests for cms page service. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PageRepositoryTest extends WebapiAbstract { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 8b54beeebbebf..1faff4bc03ffa 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -16,7 +16,10 @@ use Magento\Catalog\Model\CategoryFactory as CategoryModelFactory; /** + * Test for admin category functionality. + * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -625,7 +628,8 @@ public function testSaveDesign(): void //Trying again with the permissions. $requestData['custom_layout_update_file'] = null; $requestData['custom_design'] = 'test-theme'; - $this->aclBuilder->getAcl()->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + $this->aclBuilder->getAcl() + ->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); $this->getRequest()->setDispatched(false); $this->getRequest()->setPostValue($requestData); $this->getRequest()->setParam('store', $requestData['store_id']); From 1320f0e0f117ba5ed4783f176e7bdae95531be2c Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 4 Sep 2019 13:24:46 -0500 Subject: [PATCH 0491/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Catalog/Plugin/ProductAuthorization.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php index 57a928731be93..ce2fe19cf1aee 100644 --- a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php +++ b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php @@ -41,8 +41,11 @@ public function __construct(Authorization $authorization) * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeSave(ProductRepositoryInterface $subject, ProductInterface $product, $saveOptions): array - { + public function beforeSave( + ProductRepositoryInterface $subject, + ProductInterface $product, + $saveOptions = false + ): array { $this->authorization->authorizeSavingOf($product); return [$product, $saveOptions]; From 9ab6d667a37b26aa8fb7dff48e6746d7f3399bc7 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 4 Sep 2019 14:59:29 -0500 Subject: [PATCH 0492/2437] MC-18685: Remove custom layout updates from admin --- .../Adminhtml/Category/MoveTest.php | 8 +- .../Page/Authorization.php | 4 +- .../Magento/Cms/Model/Page/DataProvider.php | 12 +-- .../Magento/Cms/Observer/PageAclPlugin.php | 2 +- .../Cms/Observer/PageValidatorObserver.php | 2 +- .../Api/ProductRepositoryInterfaceTest.php | 8 +- .../integration/etc/di/preferences/ce.php | 4 +- .../Cms/Model/CustomLayoutManager.php | 52 +++++++++++ .../Magento/Cms/Controller/PageTest.php | 23 +++++ .../Cms/Model/Page/DataProviderTest.php | 88 ++++++++++++++++++- .../Cms/_files/pages_with_layout_xml.php | 14 +++ .../_files/pages_with_layout_xml_rollback.php | 6 ++ 12 files changed, 200 insertions(+), 23 deletions(-) rename app/code/Magento/Cms/{Controller => Model}/Page/Authorization.php (95%) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php index 7b151499d2d08..da58943bb3722 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php @@ -137,9 +137,7 @@ public function testExecuteWithGenericException() ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $categoryMock->expects($this->once()) ->method('move') - ->willThrowException(new \Exception( - __('Some exception') - )); + ->willThrowException(new \Exception(__('Some exception'))); $this->messageManager->expects($this->once()) ->method('addErrorMessage') ->with(__('There was a category move error.')); @@ -234,9 +232,7 @@ public function testExecuteWithLocalizedException() ->willReturn(true); $categoryMock->expects($this->once()) ->method('move') - ->willThrowException(new \Magento\Framework\Exception\LocalizedException( - __($exceptionMessage) - )); + ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__($exceptionMessage))); $this->resultJsonFactoryMock ->expects($this->once()) ->method('create') diff --git a/app/code/Magento/Cms/Controller/Page/Authorization.php b/app/code/Magento/Cms/Model/Page/Authorization.php similarity index 95% rename from app/code/Magento/Cms/Controller/Page/Authorization.php rename to app/code/Magento/Cms/Model/Page/Authorization.php index a5ac659dd80f6..0ab63bb4901bc 100644 --- a/app/code/Magento/Cms/Controller/Page/Authorization.php +++ b/app/code/Magento/Cms/Model/Page/Authorization.php @@ -6,7 +6,7 @@ declare(strict_types=1); -namespace Magento\Cms\Controller\Page; +namespace Magento\Cms\Model\Page; use Magento\Cms\Api\Data\PageInterface; use Magento\Cms\Api\PageRepositoryInterface; @@ -47,6 +47,8 @@ public function __construct( * @param PageInterface $page * @param PageInterface|null $oldPage * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function hasPageChanged(PageInterface $page, ?PageInterface $oldPage): bool { diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index fb0b4eeefa84a..d1f148fe0198a 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -174,11 +174,13 @@ public function getMeta() } } //If custom layout XML is set then displaying this special option. - if ($page->getCustomLayoutUpdateXml()) { - $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; - } - foreach ($this->customLayoutManager->fetchAvailableFiles($found) as $layoutFile) { - $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; + if ($found) { + if ($found->getCustomLayoutUpdateXml()) { + $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; + } + foreach ($this->customLayoutManager->fetchAvailableFiles($found) as $layoutFile) { + $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; + } } } $customLayoutMeta = [ diff --git a/app/code/Magento/Cms/Observer/PageAclPlugin.php b/app/code/Magento/Cms/Observer/PageAclPlugin.php index b7e12fb7cb4b1..c71fe0af396c0 100644 --- a/app/code/Magento/Cms/Observer/PageAclPlugin.php +++ b/app/code/Magento/Cms/Observer/PageAclPlugin.php @@ -10,7 +10,7 @@ use Magento\Cms\Api\Data\PageInterface; use Magento\Cms\Api\PageRepositoryInterface; -use Magento\Cms\Controller\Page\Authorization; +use Magento\Cms\Model\Page\Authorization; /** * Perform additional authorization before saving a page. diff --git a/app/code/Magento/Cms/Observer/PageValidatorObserver.php b/app/code/Magento/Cms/Observer/PageValidatorObserver.php index d245e9a519f1f..b4e5d2bc0e0a7 100644 --- a/app/code/Magento/Cms/Observer/PageValidatorObserver.php +++ b/app/code/Magento/Cms/Observer/PageValidatorObserver.php @@ -9,7 +9,7 @@ namespace Magento\Cms\Observer; use Magento\Cms\Api\Data\PageInterface; -use Magento\Cms\Controller\Page\Authorization; +use Magento\Cms\Model\Page\Authorization; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Exception\LocalizedException; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index cb0c793356fee..c5014ed391fb3 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -1186,11 +1186,11 @@ protected function getSimpleProductData($productData = []) protected function saveProduct($product, $storeCode = null, ?string $token = null) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { - if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' - && !is_array($product['custom_attributes'][$i]['value']) + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' + && !is_array($attribute['value']) ) { - $product['custom_attributes'][$i]['value'] = [""]; + $attribute['value'] = [""]; } } } diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index 972f7fb3c669b..2770283637dc7 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -34,5 +34,7 @@ \Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager::class => \Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager::class, \Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager::class => - \Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager::class + \Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager::class, + \Magento\Cms\Model\Page\CustomLayoutManagerInterface::class => + \Magento\TestFramework\Cms\Model\CustomLayoutManager::class ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php b/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php new file mode 100644 index 0000000000000..527454c297d48 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Cms\Model; + +use Magento\Cms\Api\Data\PageInterface; + +/** + * Manager allowing to fake available files. + */ +class CustomLayoutManager extends \Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager +{ + /** + * @var string[][] + */ + private $files = []; + + /** + * Fake available files for given page. + * + * Pass null to unassign fake files. + * + * @param int $forPageId + * @param string[]|null $files + * @return void + */ + public function fakeAvailableFiles(int $forPageId, ?array $files): void + { + if ($files === null) { + unset($this->files[$forPageId]); + } else { + $this->files[$forPageId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(PageInterface $page): array + { + if (array_key_exists($page->getId(), $this->files)) { + return $this->files[$page->getId()]; + } + + return parent::fetchAvailableFiles($page); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php index 8a0c715cfaf75..4600cd28fd3fc 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php @@ -9,6 +9,10 @@ */ namespace Magento\Cms\Controller; +use Magento\Cms\Api\GetPageByIdentifierInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; + class PageTest extends \Magento\TestFramework\TestCase\AbstractController { public function testViewAction() @@ -62,4 +66,23 @@ public static function cmsPageWithSystemRouteFixture() ->setPageLayout('1column') ->save(); } + + /** + * Check that custom handles are applied when rendering a page. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + */ + public function testCustomHandles(): void + { + /** @var GetPageByIdentifierInterface $pageFinder */ + $pageFinder = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); + $page = $pageFinder->execute('test_custom_layout_page_3', 0); + $this->dispatch('/cms/page/view/page_id/' .$page->getId()); + /** @var LayoutInterface $layout */ + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); + $handles = $layout->getUpdate()->getHandles(); + $this->assertContains('cms_page_view_selectable_test_custom_layout_page_3_test_selected', $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php index 115e39269edba..7524e454bc055 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -8,13 +8,17 @@ namespace Magento\Cms\Model\Page; +use Magento\Cms\Api\GetPageByIdentifierInterface; +use Magento\TestFramework\Cms\Model\CustomLayoutManager; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; use Magento\Cms\Model\Page as PageModel; -use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\Framework\App\Request\Http as HttpRequest; /** * Test pages data provider. + * + * @magentoAppArea adminhtml */ class DataProviderTest extends TestCase { @@ -24,9 +28,19 @@ class DataProviderTest extends TestCase private $provider; /** - * @var PageModelFactory + * @var GetPageByIdentifierInterface */ - private $pageFactory; + private $repo; + + /** + * @var CustomLayoutManager + */ + private $filesFaker; + + /** + * @var HttpRequest + */ + private $request; /** * @inheritDoc @@ -34,7 +48,7 @@ class DataProviderTest extends TestCase protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->pageFactory = $objectManager->get(PageModelFactory::class); + $this->repo = $objectManager->get(GetPageByIdentifierInterface::class); $this->provider = $objectManager->create( DataProvider::class, [ @@ -43,6 +57,8 @@ protected function setUp() 'requestFieldName' => 'page_id' ] ); + $this->filesFaker = $objectManager->get(CustomLayoutManager::class); + $this->request = $objectManager->get(HttpRequest::class); } /** @@ -57,16 +73,80 @@ public function testCustomLayoutData(): void $data = $this->provider->getData(); $page1Data = null; $page2Data = null; + $page3Data = null; foreach ($data as $pageData) { if ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_1') { $page1Data = $pageData; } elseif ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_2') { $page2Data = $pageData; + } elseif ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_3') { + $page3Data = $pageData; } } $this->assertNotEmpty($page1Data); $this->assertNotEmpty($page2Data); $this->assertEquals('_existing_', $page1Data['layout_update_selected']); $this->assertEquals(null, $page2Data['layout_update_selected']); + $this->assertEquals('test_selected', $page3Data['layout_update_selected']); + } + + /** + * Check that proper meta for custom layout field is returned. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + */ + public function testCustomLayoutMeta(): void + { + //Testing a page without layout xml + $page = $this->repo->execute('test_custom_layout_page_3', 0); + $this->filesFaker->fakeAvailableFiles((int)$page->getId(), ['test1', 'test2']); + $this->request->setParam('page_id', $page->getId()); + + $meta = $this->provider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_select', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_select']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_select']['arguments']); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_select']['arguments']['data'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => ''], + ['label' => 'test1', 'value' => 'test1'], + ['label' => 'test2', 'value' => 'test2'] + ]; + $metaList = $meta['design']['children']['custom_layout_update_select']['arguments']['data']['options']; + sort($expectedList); + sort($metaList); + $this->assertEquals($expectedList, $metaList); + + //Page with old layout xml + $page = $this->repo->execute('test_custom_layout_page_1', 0); + $this->filesFaker->fakeAvailableFiles((int)$page->getId(), ['test3']); + $this->request->setParam('page_id', $page->getId()); + + $meta = $this->provider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_select', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_select']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_select']['arguments']); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_select']['arguments']['data'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => ''], + ['label' => 'Use existing layout update XML', 'value' => '_existing_'], + ['label' => 'test1', 'value' => 'test3'], + ]; + $metaList = $meta['design']['children']['custom_layout_update_select']['arguments']['data']['options']; + sort($expectedList); + sort($metaList); + $this->assertEquals($expectedList, $metaList); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php index c5e6986277ed0..80f0c8757a579 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -17,9 +17,23 @@ $page->setIdentifier('test_custom_layout_page_1'); $page->setTitle('Test Page'); $page->setCustomLayoutUpdateXml('tst'); +$page->setIsActive(true); +$page->setStoreId(0); $page->save(); /** @var PageModel $page2 */ $page2 = $pageFactory->create(); $page2->setIdentifier('test_custom_layout_page_2'); $page2->setTitle('Test Page 2'); +$page->setIsActive(true); +$page->setStoreId(0); $page2->save(); +/** @var PageModel $page3 */ +$page3 = $pageFactory->create(); +$page3->setIdentifier('test_custom_layout_page_3'); +$page3->setTitle('Test Page 3'); +$page3->setData('layout_update_selected', 'test_selected'); +$page3->setStores([0]); +$page3->setIsActive(1); +$page3->setContent('<h1>Test Page</h1>'); +$page3->setPageLayout('1column'); +$page3->save(); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php index ca9195256af8c..3217b94d7392b 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php @@ -24,3 +24,9 @@ if ($page2->getId()) { $page2->delete(); } +/** @var PageModel $page3 */ +$page3 = $pageFactory->create(); +$page3->load('test_custom_layout_page_3', PageModel::IDENTIFIER); +if ($page3->getId()) { + $page3->delete(); +} From 62690aa5e6bcc40099ea2bec5cb70a80955de393 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 4 Sep 2019 16:11:17 -0500 Subject: [PATCH 0493/2437] MC-18685: Remove custom layout updates from admin --- .../Model/Category/DataProviderTest.php | 83 +++++++++++++ .../Form/Modifier/LayoutUpdateTest.php | 114 ++++++++++++++++++ .../Cms/Model/Page/DataProviderTest.php | 2 +- 3 files changed, 198 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index e93761f9043b8..e91aa01eb9046 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Model\Category; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Registry; use PHPUnit\Framework\TestCase; @@ -12,6 +13,11 @@ use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; +/** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml + */ class DataProviderTest extends TestCase { /** @@ -29,6 +35,11 @@ class DataProviderTest extends TestCase */ private $categoryFactory; + /** + * @var CategoryLayoutUpdateManager + */ + private $fakeFiles; + /** * Create subject instance. * @@ -56,6 +67,7 @@ protected function setUp() $this->dataProvider = $this->createDataProvider(); $this->registry = $objectManager->get(Registry::class); $this->categoryFactory = $objectManager->get(CategoryFactory::class); + $this->fakeFiles = $objectManager->get(CategoryLayoutUpdateManager::class); } /** @@ -103,4 +115,75 @@ public function testCustomLayoutFileAttribute(): void $this->assertEquals($deprecated, $data[$id]['custom_layout_update']); $this->assertEquals(LayoutUpdate::VALUE_USE_UPDATE_XML, $data[$id]['custom_layout_update_file']); } + + /** + * Check that proper options are returned for a category. + * + * @return void + */ + public function testCustomLayoutMeta(): void + { + //Testing a category without layout xml + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load($id = 2); + $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test1', 'test2']); + $this->registry->register('category', $category); + + $meta = $this->dataProvider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_file']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_file']['arguments']); + $this->assertArrayHasKey( + 'config', + $meta['design']['children']['custom_layout_update_file']['arguments']['data'] + ); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], + ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] + ]; + $list = $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + + //Product with old layout xml + $category->setCustomAttribute('custom_layout_update', 'test'); + $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test3']); + + $meta = $this->dataProvider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_file']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_file']['arguments']); + $this->assertArrayHasKey( + 'config', + $meta['design']['children']['custom_layout_update_file']['arguments']['data'] + ); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'Use existing', + 'value' => LayoutUpdate::VALUE_USE_UPDATE_XML, + '__disableTmpl' => true + ], + ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], + ]; + $list = $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php index 4ec50dc26da5c..61e68561d9ee4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -10,13 +10,20 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as LayoutUpdateAttribute; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav as EavModifier; /** * Test the modifier. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml */ class LayoutUpdateTest extends TestCase { @@ -35,14 +42,47 @@ class LayoutUpdateTest extends TestCase */ private $locator; + /** + * @var EavModifier + */ + private $eavModifier; + + /** + * @var ProductLayoutUpdateManager + */ + private $fakeFiles; + /** * @inheritDoc */ protected function setUp() { $this->locator = $this->getMockForAbstractClass(LocatorInterface::class); + $store = Bootstrap::getObjectManager()->create(StoreInterface::class); + $this->locator->method('getStore')->willReturn($store); $this->modifier = Bootstrap::getObjectManager()->create(LayoutUpdate::class, ['locator' => $this->locator]); $this->repo = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + $this->eavModifier = Bootstrap::getObjectManager()->create( + EavModifier::class, + [ + 'locator' => $this->locator, + 'formElementMapper' => Bootstrap::getObjectManager()->create( + \Magento\Ui\DataProvider\Mapper\FormElement::class, + [ + 'mappings' => [ + "text" => "input", + "hidden" => "input", + "boolean" => "checkbox", + "media_image" => "image", + "price" => "input", + "weight" => "input", + "gallery" => "image" + ] + ] + ) + ] + ); + $this->fakeFiles = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); } /** @@ -50,6 +90,7 @@ protected function setUp() * * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @return void + * @throws \Throwable */ public function testModifyData(): void { @@ -63,4 +104,77 @@ public function testModifyData(): void $data[$product->getId()]['product']['custom_layout_update_file'] ); } + + /** + * Check that entity specific options are returned. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testEntitySpecificData(): void + { + //Testing a category without layout xml + $product = $this->repo->get('simple'); + $this->locator->method('getProduct')->willReturn($product); + $this->fakeFiles->setFakeFiles((int)$product->getId(), ['test1', 'test2']); + + $meta = $this->eavModifier->modifyMeta([]); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('container_custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('children', $meta['design']['children']['container_custom_layout_update_file']); + $this->assertArrayHasKey( + 'custom_layout_update_file', + $meta['design']['children']['container_custom_layout_update_file']['children'] + ); + $fieldMeta = $meta['design']['children']['container_custom_layout_update_file']['children']; + $fieldMeta = $fieldMeta['custom_layout_update_file']; + $this->assertArrayHasKey('arguments', $fieldMeta); + $this->assertArrayHasKey('data', $fieldMeta['arguments']); + $this->assertArrayHasKey('config', $fieldMeta['arguments']['data']); + $this->assertArrayHasKey('options', $fieldMeta['arguments']['data']['config']); + $expectedList = [ + ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], + ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] + ]; + $list = $fieldMeta['arguments']['data']['config']['options']; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + + //Product with old layout xml + $product->setCustomAttribute('custom_layout_update', 'test'); + $this->fakeFiles->setFakeFiles((int)$product->getId(), ['test3']); + + $meta = $this->eavModifier->modifyMeta([]); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('container_custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('children', $meta['design']['children']['container_custom_layout_update_file']); + $this->assertArrayHasKey( + 'custom_layout_update_file', + $meta['design']['children']['container_custom_layout_update_file']['children'] + ); + $fieldMeta = $meta['design']['children']['container_custom_layout_update_file']['children']; + $fieldMeta = $fieldMeta['custom_layout_update_file']; + $this->assertArrayHasKey('arguments', $fieldMeta); + $this->assertArrayHasKey('data', $fieldMeta['arguments']); + $this->assertArrayHasKey('config', $fieldMeta['arguments']['data']); + $this->assertArrayHasKey('options', $fieldMeta['arguments']['data']['config']); + $expectedList = [ + ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'Use existing', + 'value' => LayoutUpdateAttribute::VALUE_USE_UPDATE_XML, + '__disableTmpl' => true + ], + ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], + ]; + $list = $fieldMeta['arguments']['data']['config']['options']; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php index 7524e454bc055..2d2d36178ada8 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -142,7 +142,7 @@ public function testCustomLayoutMeta(): void $expectedList = [ ['label' => 'No update', 'value' => ''], ['label' => 'Use existing layout update XML', 'value' => '_existing_'], - ['label' => 'test1', 'value' => 'test3'], + ['label' => 'test3', 'value' => 'test3'], ]; $metaList = $meta['design']['children']['custom_layout_update_select']['arguments']['data']['options']; sort($expectedList); From 739fe4dc041d9721befb8ee2a4c783adadd57734 Mon Sep 17 00:00:00 2001 From: Evgeny Petrov <evgeny_petrov@epam.com> Date: Thu, 5 Sep 2019 11:32:02 +0300 Subject: [PATCH 0494/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) --- .../Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index a1d2d63096e46..afd383c13421f 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\Elasticsearch\SearchAdapter\Query\Builder; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; From b7d0657594e234d2f2b92335c6c48f769c5fdbe2 Mon Sep 17 00:00:00 2001 From: vital_pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Fri, 30 Aug 2019 17:58:31 +0300 Subject: [PATCH 0495/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Implemented validation URL key during category creation --- .../CategoryUrlPathAutogeneratorObserver.php | 11 +++++++---- ...ategoryWithRestrictedUrlKeyNotCreatedTest.xml | 16 ++++++++-------- app/code/Magento/CatalogUrlRewrite/etc/di.xml | 11 +++++++++++ .../Magento/CatalogUrlRewrite/i18n/en_US.csv | 3 ++- .../Controller/Adminhtml/CategoryTest.php | 4 ++-- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 2fd45bf3c0bc6..54ef7102fcc47 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -24,9 +24,9 @@ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface /** * Reserved endpoint names. * - * @var array + * @var string[] */ - private $invalidValues = ['admin', 'soap', 'rest', 'graphql']; + private $invalidValues = []; /** * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator @@ -59,13 +59,15 @@ class CategoryUrlPathAutogeneratorObserver implements ObserverInterface * @param \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService * @param CategoryRepositoryInterface $categoryRepository * @param \Magento\Backend\App\Area\FrontNameResolver $frontNameResolver + * @param string[] $invalidValues */ public function __construct( CategoryUrlPathGenerator $categoryUrlPathGenerator, ChildrenCategoriesProvider $childrenCategoriesProvider, StoreViewService $storeViewService, CategoryRepositoryInterface $categoryRepository, - \Magento\Backend\App\Area\FrontNameResolver $frontNameResolver = null + \Magento\Backend\App\Area\FrontNameResolver $frontNameResolver = null, + array $invalidValues = [] ) { $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; $this->childrenCategoriesProvider = $childrenCategoriesProvider; @@ -73,6 +75,7 @@ public function __construct( $this->categoryRepository = $categoryRepository; $this->frontNameResolver = $frontNameResolver ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Backend\App\Area\FrontNameResolver::class); + $this->invalidValues = $invalidValues; } /** @@ -116,7 +119,7 @@ private function updateUrlKey($category, $urlKey) if (in_array($urlKey, $this->getInvalidValues())) { throw new \Magento\Framework\Exception\LocalizedException( __( - 'URL key "%1" conflicts with reserved endpoint names: %2. Try another url key.', + 'URL key "%1" matches a reserved endpoint name (%2). Use another URL key.', $urlKey, implode(', ', $this->getInvalidValues()) ) diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 1e71d8e152651..0212be38f4265 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -45,12 +45,12 @@ <argument name="categoryName" value="admin"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "admin" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeAdminFirstErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeAdminFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminSecondCategoryForm"> <argument name="categoryName" value="{{SimpleSubCategory.name}}"/> <argument name="categoryUrlKey" value="admin"/> </actionGroup> - <see userInput='URL key "admin" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeAdminSecondErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeAdminSecondErrorMessage"/> <!--Create category with 'admin' name--> <comment userInput="Create category with 'admin' name" stepKey="commentAdminCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminThirdCategoryForm"> @@ -66,12 +66,12 @@ <argument name="categoryName" value="soap"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "soap" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeSoapFirstErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeSoapFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapSecondCategoryForm"> <argument name="categoryName" value="{{ApiCategory.name}}"/> <argument name="categoryUrlKey" value="soap"/> </actionGroup> - <see userInput='URL key "soap" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeSoapSecondErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeSoapSecondErrorMessage"/> <!--Create category with 'soap' name--> <comment userInput="Create category with 'soap' name" stepKey="commentSoapCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapThirdCategoryForm"> @@ -87,12 +87,12 @@ <argument name="categoryName" value="rest"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "rest" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeRestFirstErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeRestFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestSecondCategoryForm"> <argument name="categoryName" value="{{SubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="rest"/> </actionGroup> - <see userInput='URL key "rest" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeRestSecondErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeRestSecondErrorMessage"/> <!--Create category with 'rest' name--> <comment userInput="Create category with 'rest' name" stepKey="commentRestCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestThirdCategoryForm"> @@ -108,12 +108,12 @@ <argument name="categoryName" value="graphql"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlFirstErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeGraphQlFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlSecondCategoryForm"> <argument name="categoryName" value="{{NewSubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="graphql"/> </actionGroup> - <see userInput='URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlSecondErrorMessage"/> + <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlSecondErrorMessage"/> <!--Create category with 'graphql' name--> <comment userInput="Create category with 'graphql' name" stepKey="commentGraphQlCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlThirdCategoryForm"> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index e6fbcaefd0768..2a74b5cd92b28 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -45,4 +45,15 @@ </argument> </arguments> </type> + <type name="Magento\CatalogUrlRewrite\Observer\CategoryUrlPathAutogeneratorObserver"> + <arguments> + <argument name="invalidValues" xsi:type="array"> + <item name="0" xsi:type="string">admin</item> + <item name="1" xsi:type="string">soap</item> + <item name="2" xsi:type="string">rest</item> + <item name="3" xsi:type="string">graphql</item> + <item name="4" xsi:type="string">standard</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv index b3335dc3523ca..0f21e8ddf9fc9 100644 --- a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv +++ b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv @@ -5,4 +5,5 @@ "Product URL Suffix","Product URL Suffix" "Use Categories Path for Product URLs","Use Categories Path for Product URLs" "Create Permanent Redirect for URLs if URL Key Changed","Create Permanent Redirect for URLs if URL Key Changed" -"Generate "category/product" URL Rewrites","Generate "category/product" URL Rewrites" \ No newline at end of file +"Generate "category/product" URL Rewrites","Generate "category/product" URL Rewrites" +"URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key.","URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key." diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index bfe02f2aa2e10..6340900404bd3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -592,8 +592,8 @@ public function testSaveWithCustomBackendNameAction() $this->assertSessionMessages( $this->equalTo( [ - 'URL key "backend" conflicts with reserved endpoint names: ' - . 'admin, soap, rest, graphql, backend. Try another url key.' + 'URL key "backend" matches a reserved endpoint name ' + . '(admin, soap, rest, graphql, standard, backend). Use another URL key.' ] ), MessageInterface::TYPE_ERROR From a6d39e805405cca7954d43cf8591652997953234 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 5 Sep 2019 12:31:41 +0400 Subject: [PATCH 0496/2437] MAGETWO-69825: [GITHUB #9891] Subcategory "liquid-hand-soap" is not opened in category "soap" - Updated automated test script --- .../AdminCategoryRestrictedUrlMessageData.xml | 17 +++++++++++ ...goryWithRestrictedUrlKeyNotCreatedTest.xml | 28 +++++++++---------- 2 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/AdminCategoryRestrictedUrlMessageData.xml diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/AdminCategoryRestrictedUrlMessageData.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/AdminCategoryRestrictedUrlMessageData.xml new file mode 100644 index 0000000000000..b463b0524d5ff --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Data/AdminCategoryRestrictedUrlMessageData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminCategoryRestrictedUrlErrorMessage"> + <data key="urlAdmin">URL key "admin" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.</data> + <data key="urlSoap">URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.</data> + <data key="urlRest">URL key "rest" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.</data> + <data key="urlGraphql">URL key "graphql" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 0212be38f4265..6538ec6e935df 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -10,11 +10,11 @@ <test name="AdminCategoryWithRestrictedUrlKeyNotCreatedTest"> <annotations> <features value="CatalogUrlRewrite"/> - <stories value="Subcategory 'liquid-hand-soap' is not opened in category 'soap'"/> + <stories value="Url rewrites"/> <title value="Category with restricted Url Key cannot be created"/> <description value="Category with restricted Url Key cannot be created"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-71221"/> + <testCaseId value="MC-17515"/> <useCaseId value="MAGETWO-69825"/> <group value="CatalogUrlRewrite"/> </annotations> @@ -45,19 +45,19 @@ <argument name="categoryName" value="admin"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeAdminFirstErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminSecondCategoryForm"> <argument name="categoryName" value="{{SimpleSubCategory.name}}"/> <argument name="categoryUrlKey" value="admin"/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeAdminSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminSecondErrorMessage"/> <!--Create category with 'admin' name--> <comment userInput="Create category with 'admin' name" stepKey="commentAdminCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminThirdCategoryForm"> <argument name="categoryName" value="admin"/> <argument name="categoryUrlKey" value="{{SimpleSubCategory.name}}"/> </actionGroup> - <see userInput="You saved the category." stepKey="seeAdminSuccessMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeAdminSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('admin')}}" stepKey="seeAdminCategoryInTree"/> <!--Check category creation with restricted url key 'soap'--> <comment userInput="Check category creation with restricted url key 'soap'" stepKey="commentCheckSoapCategoryCreation"/> @@ -66,19 +66,19 @@ <argument name="categoryName" value="soap"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeSoapFirstErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapSecondCategoryForm"> <argument name="categoryName" value="{{ApiCategory.name}}"/> <argument name="categoryUrlKey" value="soap"/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeSoapSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapSecondErrorMessage"/> <!--Create category with 'soap' name--> <comment userInput="Create category with 'soap' name" stepKey="commentSoapCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapThirdCategoryForm"> <argument name="categoryName" value="soap"/> <argument name="categoryUrlKey" value="{{ApiCategory.name}}"/> </actionGroup> - <see userInput="You saved the category." stepKey="seeSoapSuccessMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeSoapSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('soap')}}" stepKey="seeSoapCategoryInTree"/> <!--Check category creation with restricted url key 'rest'--> <comment userInput="Check category creation with restricted url key 'rest'" stepKey="commentCheckRestCategoryCreation"/> @@ -87,19 +87,19 @@ <argument name="categoryName" value="rest"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeRestFirstErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestSecondCategoryForm"> <argument name="categoryName" value="{{SubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="rest"/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeRestSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestSecondErrorMessage"/> <!--Create category with 'rest' name--> <comment userInput="Create category with 'rest' name" stepKey="commentRestCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestThirdCategoryForm"> <argument name="categoryName" value="rest"/> <argument name="categoryUrlKey" value="{{SubCategoryWithParent.name}}"/> </actionGroup> - <see userInput="You saved the category." stepKey="seeRestSuccessMesdgssage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeRestSuccessMesdgssage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('rest')}}" stepKey="seeRestCategoryInTree"/> <!--Check category creation with restricted url key 'graphql'--> <comment userInput="Check category creation with restricted url key 'graphql'" stepKey="commentCheckGraphQlCategoryCreation"/> @@ -108,19 +108,19 @@ <argument name="categoryName" value="graphql"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.' stepKey="seeGraphQlFirstErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlFirstErrorMessage"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlSecondCategoryForm"> <argument name="categoryName" value="{{NewSubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="graphql"/> </actionGroup> - <see userInput='URL key "soap" matches a reserved endpoint name (admin, soap, rest, graphql, standard). Use another URL key.URL key "graphql" conflicts with reserved endpoint names: admin, soap, rest, graphql. Try another url key.' stepKey="seeGraphQlSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlSecondErrorMessage"/> <!--Create category with 'graphql' name--> <comment userInput="Create category with 'graphql' name" stepKey="commentGraphQlCategoryCreation"/> <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlThirdCategoryForm"> <argument name="categoryName" value="graphql"/> <argument name="categoryUrlKey" value="{{NewSubCategoryWithParent.name}}"/> </actionGroup> - <see userInput="You saved the category." stepKey="seeGraphQlSuccessMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeGraphQlSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('graphql')}}" stepKey="seeGraphQlCategoryInTree"/> </test> </tests> From 3b4166229afdeba2394a0a8241b0537780a66f96 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 5 Sep 2019 16:00:11 +0400 Subject: [PATCH 0497/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- .../AdminProductAttributeActionGroup.xml | 4 +-- .../Test/Mftf/Data/CatalogSearchData.xml | 6 ----- .../Mftf/Metadata/catalog_search-meta.xml | 3 --- ...ontElasticsearchSearchInvalidValueTest.xml | 25 ++++++++++++++----- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 0c3172392435a..42e0bb24c744f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -213,7 +213,7 @@ </arguments> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <waitForPageLoad stepKey="waitForAttributeGridPgeLoad"/> + <waitForPageLoad stepKey="waitForAttributeGridPageLoad"/> <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttributeCode}}" stepKey="setAttributeCode"/> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> @@ -320,7 +320,7 @@ <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> </actionGroup> - <actionGroup name="StorefrontCreateSearchableProductAttribute" extends="createProductAttribute" insertAfter="checkRequired"> + <actionGroup name="AdminCreateSearchableProductAttribute" extends="createProductAttribute" insertAfter="checkRequired"> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab"/> <waitForElementVisible selector="{{StorefrontPropertiesSection.PageTitle}}" stepKey="waitTabLoad"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="setSearchable"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml index 58e2929090059..d631cfd29113d 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml @@ -11,9 +11,6 @@ <entity name="SetMinQueryLengthToDefault" type="catalog_search_config_def"> <requiredEntity type="enable">DefaultMinQueryLength</requiredEntity> </entity> - <entity name="SetSearchEngineToDefault" type="catalog_search_config_def"> - <requiredEntity type="enable_engine">DefaultSearchEngine</requiredEntity> - </entity> <entity name="UncheckMinQueryLengthAndSet" type="catalog_search_config_query_length"> <requiredEntity type="number">SetMinQueryLengthToOne</requiredEntity> </entity> @@ -26,7 +23,4 @@ <entity name="SetMinQueryLengthToOne" type="number"> <data key="value">1</data> </entity> - <entity name="DefaultSearchEngine" type="enable_engine"> - <data key="inherit">true</data> - </entity> </entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml index 7e880262d5922..7405377249aa4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml @@ -14,9 +14,6 @@ <object key="min_query_length" dataType="enable"> <field key="inherit">boolean</field> </object> - <object key="engine" dataType="enable_engine"> - <field key="inherit">boolean</field> - </object> </object> </object> </object> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml index 4b68dbf95a529..55e31e91e9016 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml @@ -11,7 +11,7 @@ <test name="StrorefrontElasticsearchSearchInvalidValueTest"> <annotations> <features value="Search"/> - <stories value="Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page"/> + <stories value="Search Product on Storefront"/> <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <severity value="MAJOR"/> @@ -21,18 +21,25 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create category--> + <comment userInput="Create category" stepKey="commentCreateCategory"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Enable Elasticsearch--> <comment userInput="Enable Elasticsearch" stepKey="commentEnableElasticsearch"/> <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="enableElasticsearch"/> <!--Set Minimal Query Length--> <comment userInput="Set Minimal Query Length" stepKey="commentSetMinQueryLength"/> <magentoCLI command="config:set catalog/search/min_query_length 2" stepKey="setMinQueryLength"/> + <!--Reindex indexes and clear cache--> + <comment userInput="Reindex indexes and clear cache" stepKey="commentReindexClearCache"/> + <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> + <magentoCLI command="cache:flush config" stepKey="flushCache"/> </before> <after> <!--Set configs to default--> <comment userInput="Set configs to default" stepKey="commentSetDefault"/> - <createData entity="SetMinQueryLengthToDefault" stepKey="setMinimumQueryLengthToDefault"/> - <createData entity="SetSearchEngineToDefault" stepKey="setSearchEngineToDefault"/> + <magentoCLI command="config:set catalog/search/min_query_length 3" stepKey="setMinQueryLengthPreviousState"/> + <magentoCLI command="config:set catalog/search/engine mysql" stepKey="resetSearchEnginePreviousState"/> <!--Delete create data--> <comment userInput="Delete create data" stepKey="commentDeletedData"/> <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> @@ -44,18 +51,19 @@ <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{SimpleProduct.sku}}"/> </actionGroup> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Create new searchable product attribute--> <comment userInput="Create new searchable product attribute" stepKey="commentCreateAttribute"/> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - <actionGroup ref="StorefrontCreateSearchableProductAttribute" stepKey="createAttribute"> + <actionGroup ref="AdminCreateSearchableProductAttribute" stepKey="createAttribute"> <argument name="attribute" value="textProductAttribute"/> </actionGroup> - <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> <!--Assign attribute to the Default set--> <comment userInput="Assign attribute to the Default set" stepKey="commentAssignToDefaultSet"/> + <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> + <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="{{textProductAttribute.attribute_code}}"/> @@ -71,8 +79,13 @@ <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> <argument name="product" value="SimpleProduct"/> </actionGroup> + <actionGroup ref="SetCategoryByName" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> <fillField selector="{{AdminProductFormSection.attributeRequiredInput(textProductAttribute.attribute_code)}}" userInput="searchable" stepKey="fillTheAttributeRequiredInputField"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush catalog_product_attribute " stepKey="flushCache"/> <!--Assert search results on storefront--> <comment userInput="Assert search results on storefront" stepKey="commentAssertSearchResult"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> From 0cd1a774d391589aa0bc8fdb706d569ff57fdede Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Tue, 20 Aug 2019 10:31:28 +0400 Subject: [PATCH 0498/2437] MC-18822: Increase test coverage for Content functional area - Automation test for MC-6192 --- .../AdminProductAttributeSetActionGroup.xml | 10 + .../Catalog/Test/Mftf/Data/ProductData.xml | 13 ++ .../Mftf/Section/AdminProductFormSection.xml | 1 + .../StorefrontCategorySidebarSection.xml | 1 + .../AdminAddOptionsToAttributeActionGroup.xml | 38 ++++ .../AdminConfigurableProductActionGroup.xml | 53 ++++++ .../Mftf/Data/ConfigurableProductData.xml | 45 +++++ ...reateProductConfigurationsPanelSection.xml | 1 + ...CheckResultsOfColorAndOtherFiltersTest.xml | 172 ++++++++++++++++++ 9 files changed, 334 insertions(+) create mode 100644 app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index e20b5f113a7ec..c67c2148673a5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -64,6 +64,16 @@ <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> </actionGroup> + <actionGroup name="AddUnsignedAttributeToGroup" extends="CreateDefaultAttributeSet"> + <arguments> + <argument name="firstOption" type="string"/> + <argument name="secondOption" type="string"/> + <argument name="group" type="string"/> + </arguments> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(firstOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign1"/> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(secondOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign2"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSaveButton"/> + </actionGroup> <actionGroup name="goToAttributeGridPage"> <annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 517ab253b8238..b8d7aa878230a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1165,6 +1165,19 @@ <requiredEntity type="product_extension_attribute">EavStock10</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> + <entity name="Simple1" type="product"> + <data key="sku">Simple1</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name">Simple1</data> + <data key="price">1.00</data> + <data key="urlKey" unique="suffix">api-simple-product</data> + <data key="status">1</data> + <data key="quantity">111</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="SimpleProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">simple-product_</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 80b4159167453..b8aa4aa0ce822 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> + <element name="additionalOptions" type="select" selector="//select[@class='admin__control-multiselect']"/> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 1b7bbd58eea9f..406bea8d8aeab 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> + <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> <element name="filterOptions" type="text" selector=".filter-options-content .items"/> <element name="filterOption" type="text" selector=".filter-options-content .item"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml index e88f71ce23ac2..b8bdbdfe082c5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml @@ -8,6 +8,44 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewAttributeWithOptions"> + <arguments> + <argument name="labelName" type="string"/> + <argument name="inputType" type="string"/> + <argument name="firstOption" type="string"/> + <argument name="secondOption" type="string"/> + <argument name="thirdOption" type="string"/> + <argument name="fourthOption" type="string"/> + <argument name="useInLayer" type="string"/> + </arguments> + <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="createNewAttribute"/> + <fillField stepKey="fillDefaultLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{labelName}}"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" stepKey="checkInputType" userInput="{{inputType}}"/> + <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption6"/> + <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('1')}}" userInput="{{firstOption}}" stepKey="fillAdminValue6"/> + <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption7"/> + <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('2')}}" userInput="{{secondOption}}" stepKey="fillAdminValue7"/> + <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption8"/> + <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('3')}}" userInput="{{thirdOption}}" stepKey="fillAdminValue8"/> + <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption9"/> + <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('4')}}" userInput="{{fourthOption}}" stepKey="fillAdminValue9"/> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" stepKey="selectUseInLayer" userInput="{{useInLayer}}"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForGridPageLoad"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> + <actionGroup name="CreateAttributeWithOptions" extends="CreateNewAttributeWithOptions"> + <arguments> + <argument name="fifthOption" type="string"/> + </arguments> + <click selector="{{AttributeOptionsSection.AddOption}}" after="fillAdminValue9" stepKey="clickAddOption10"/> + <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('5')}}" after="clickAddOption10" userInput="{{fifthOption}}" stepKey="fillAdminValue10"/> + </actionGroup> <actionGroup name="addOptionsToAttributeActionGroup"> <annotations> <description>Adds 5 provided Options to a new Attribute on the Configurable Product creation/edit page.</description> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index a0a3a551c3d93..a1042141d9373 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -8,6 +8,59 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateConfigurableProductWithAttributeSet"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="label" type="string"/> + <argument name="option1" type="string"/> + </arguments> + <!-- fill in basic configurable product values --> + <comment userInput="fill in basic configurable product values" stepKey="commentFillInProductValue"/> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{label}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> + <selectOption selector="{{AdminProductFormSection.additionalOptions}}" parameterArray="{{option1}}" stepKey="searchAndMultiSelect1"/> + </actionGroup> + <actionGroup name="CreateConfigurationsForOptions"> + <arguments> + <argument name="option2" type="string"/> + <argument name="price" type="string"/> + <argument name="sku" type="string"/> + </arguments> + <!-- create configurations for colors the product is available in --> + <comment userInput="create configurations for colors the product is available in" stepKey="commentCreateConfigurations"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{option2}}" stepKey="fillFilterAttributeCodeField2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton12"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton22"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" stepKey="enterAttributePrice"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="{{sku}}" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextStep3"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNew"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + </actionGroup> + <actionGroup name="CreateConfigurableProductWithAttributeSetAndOption" extends="createConfigurationsForOptions"> + <arguments> + <argument name="colorOption" type="string"/> + </arguments> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeOption('colorOption')}}" after="clickOnSelectAll2" stepKey="clickToUncheckOption"/> + </actionGroup> <!--Filter the product grid and view expected products--> <actionGroup name="viewConfigurableProductInAdminGrid"> <annotations> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index de6714a9b959e..1ec9909576432 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -64,6 +64,51 @@ <var key="sku" entityKey="sku" entityType="product" /> <var key="childSku" entityKey="sku" entityType="product2"/> </entity> + <entity name="ConfigurableProductWithAttributeSet1" type="product"> + <data key="sku" unique="suffix">configurable</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="attribute_set_name">mySet</data> + <data key="visibility">4</data> + <data key="name">Jacket</data> + <data key="price">1.00</data> + <data key="weight">2</data> + <data key="urlKey" unique="suffix">configurableurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="ConfigurableProductWithAttributeSet2" type="product"> + <data key="sku" unique="suffix">configurable</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="attribute_set_name">mySet</data> + <data key="visibility">4</data> + <data key="name">Cardigan</data> + <data key="price">1.00</data> + <data key="weight">2</data> + <data key="urlKey" unique="suffix">configurableurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> + <entity name="ConfigurableProductWithAttributeSet3" type="product"> + <data key="sku" unique="suffix">configurable</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="attribute_set_name">mySet</data> + <data key="visibility">4</data> + <data key="name">Pants</data> + <data key="price">1.00</data> + <data key="weight">2</data> + <data key="urlKey" unique="suffix">configurableurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="ConfigurableProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">configurable-product_</data> <data key="type_id">configurable</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index f3c628d002e7a..488b227c29cbd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreateProductConfigurationsPanel"> + <element name="attributeOption" type="checkbox" selector="//li[@class='attribute-option'][@data-attribute-option-title='{{colorOption}}']" parameterized="true"/> <element name="next" type="button" selector=".steps-wizard-navigation .action-next-step" timeout="30"/> <element name="createNewAttribute" type="button" selector=".select-attributes-actions button[title='Create New Attribute']" timeout="30"/> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml new file mode 100644 index 0000000000000..5b16f083067ee --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckResultsOfColorAndOtherFiltersTest"> + <annotations> + <features value="LayeredNavigation"/> + <stories value="Checking filters results"/> + <title value="Checking results of color and other filters"/> + <description value="Checking results of filters: color and other filters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6192"/> + <useCaseId value="MAGETWO-91753"/> + <group value="layeredNavigation"/> + </annotations> + <before> + <!-- Login as Admin --> + <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create default category with subcategory --> + <comment userInput="Create default category with subcategory" stepKey="commentCreateCategory"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SubCategoryWithParent" stepKey="createSubcategory"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Add first attribute with options --> + <comment userInput="Add first attribute with options" stepKey="commentAddFirstAttribute"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForProductAttributes"/> + <actionGroup ref="CreateAttributeWithOptions" stepKey="createAttribute"> + <argument name="labelName" value="color2"/> + <argument name="inputType" value="Dropdown"/> + <argument name="firstOption" value="red"/> + <argument name="secondOption" value="green"/> + <argument name="thirdOption" value="blue"/> + <argument name="fourthOption" value="brown"/> + <argument name="fifthOption" value="black"/> + <argument name="useInLayer" value="Filterable (with results)"/> + </actionGroup> + <!-- Add second attribute with options--> + <comment userInput="Add second attribute with options" stepKey="commentAddSecondAttribute"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes2"/> + <actionGroup ref="CreateNewAttributeWithOptions" stepKey="createSecondAttribute"> + <argument name="labelName" value="material"/> + <argument name="inputType" value="Multiple Select"/> + <argument name="firstOption" value="cotton"/> + <argument name="secondOption" value="fabric"/> + <argument name="thirdOption" value="jeans"/> + <argument name="fourthOption" value="synthetic"/> + <argument name="useInLayer" value="Filterable (with results)"/> + </actionGroup> + <actionGroup ref="AddUnsignedAttributeToGroup" stepKey="createDefaultAttributeSet"> + <argument name="label" value="mySet"/> + <argument name="firstOption" value="color2"/> + <argument name="secondOption" value="material"/> + <argument name="group" value="Product Details"/> + </actionGroup> + <!-- Create three configurable products with options --> + <comment userInput="Create three configurable products with options" stepKey="commentCreateConfigurableProducts"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct1"> + <argument name="product" value="ConfigurableProductWithAttributeSet1"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['jeans', 'synthetic', 'cotton', 'fabric']"/> + </actionGroup> + <actionGroup ref="CreateConfigurationsForOptions" stepKey="createConfigurations1"> + <argument name="option2" value="color2"/> + <argument name="price" value="34"/> + <argument name="sku" value="1000"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct2"> + <argument name="product" value="ConfigurableProductWithAttributeSet2"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['jeans','cotton', 'fabric']"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetAndOption" stepKey="createConfigurations2"> + <argument name="option2" value="color2"/> + <argument name="price" value="34"/> + <argument name="sku" value="111"/> + <argument name="colorOption" value="black"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct3"> + <argument name="product" value="ConfigurableProductWithAttributeSet3"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['jeans','synthetic', 'fabric']"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetAndOption" stepKey="createConfigurations3"> + <argument name="option2" value="color2"/> + <argument name="price" value="34"/> + <argument name="sku" value="222"/> + <argument name="colorOption" value="red"/> + </actionGroup> + <!-- Create Simple product with options --> + <comment userInput="Create Simple product with options" stepKey="commentCreateProduct"/> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createSimpleProduct"> + <argument name="product" value="Simple1"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['cotton', 'fabric']"/> + </actionGroup> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSave"/> + <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <!-- Open a category on storefront --> + <comment userInput="Open a category on storefront" stepKey="commentOpenCategoryOnStorefront"/> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPage"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose color filter --> + <comment userInput="Choose color filter" stepKey="commentChooseColorFilter"/> + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('color2')}}" stepKey="waitForCartRuleButton"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('color2')}}" stepKey="expandColorAttribute"/> + <waitForPageLoad stepKey="waitForFilterLoad"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('green')}}" stepKey="expandGreenAttribute"/> + <see userInput="Pants" stepKey="seeFirstProduct"/> + <see userInput="Cardigan" stepKey="seeSecondProduct"/> + <see userInput="Jacket" stepKey="seeThirdProduct"/> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose material filter --> + <comment userInput="Choose material filter" stepKey="commentChooseMaterialFilter"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('material')}}" stepKey="expandMaterialAttribute"/> + <waitForPageLoad stepKey="waitForFilterPageLoad"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('cotton')}}" stepKey="expandCottonAttribute"/> + <see userInput="Cardigan" stepKey="seeFourthProduct"/> + <see userInput="Jacket" stepKey="seeFifthProduct"/> + </before> + <after> + <!-- Delete created data --> + <comment userInput="Delete created data" stepKey="commentDeleteData"/> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="wait1"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{ConfigurableProductWithAttributeSet1.attribute_set_name}}" stepKey="filterByName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="wait2"/> + <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="delete"/> + <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="wait3"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> + <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageAgain"> + <argument name="ProductAttribute" value="color2"/> + </actionGroup> + <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletionAttribute"/> + <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageAttribute"> + <argument name="ProductAttribute" value="material"/> + </actionGroup> + <click stepKey="clickDeleteAgain" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> + <click stepKey="clickOkForTheSecondTime" selector="{{AttributeDeleteModalSection.confirm}}"/> + <waitForPageLoad stepKey="waitForDeletion"/> + <!-- Log out --> + <comment userInput="Log out" stepKey="commentLogOut"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + </test> +</tests> From 4d0d2743cf87d2b2f69ebb3a00613a443dbb575b Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Fri, 6 Sep 2019 15:09:35 +0300 Subject: [PATCH 0499/2437] DeleteEIntegrationEntity --- .../AdminCreatesNewIntegrationActionGroup.xml | 22 +++++++ ...dminDeleteIntegrationEntityActionGroup.xml | 17 ++++++ ...gateToCreateIntegrationPageActionGroup.xml | 18 ++++++ ...dminSearchIntegrationInGridActionGroup.xml | 23 ++++++++ ...sageCreateIntegrationEntityActionGroup.xml | 19 ++++++ ...letedIntegrationIsNotInGridActionGroup.xml | 17 ++++++ .../Mftf/Section/AdminIntegrationsSection.xml | 26 ++++++++ .../Test/AdminDeleteIntegrationEntityTest.xml | 59 +++++++++++++++++++ .../TestCase/DeleteIntegrationEntityTest.xml | 1 + 9 files changed, 202 insertions(+) create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml create mode 100644 app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml new file mode 100644 index 0000000000000..899ca8b7d7f4e --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Fill Required Fields --> + <actionGroup name="AdminCreatesNewIntegrationActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <fillField stepKey="fillNameField" selector="{{AddNewIntegrationSection.name}}" userInput="{{name}}"/> + <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.saveButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..87bcff8145184 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteIntegrationEntityActionGroup"> + <click stepKey="clickRemoveButon" selector="{{IntegrationsGridSection.remove}}"/> + <waitForElementVisible selector="{{IntegrationsGridSection.submitButton}}" stepKey="waitForConfirmButtonVisible"/> + <click stepKey="clickSubmitButton" selector="{{IntegrationsGridSection.submitButton}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml new file mode 100644 index 0000000000000..f31102419b665 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Click the "Add New Integration" Button --> + + <actionGroup name="AdminNavigateToCreateIntegrationPageActionGroup"> + <click stepKey="clickAddNewIntegrationButton" selector="{{IntegrationsGridSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNIntegrationPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml new file mode 100644 index 0000000000000..6e0b7dc3eb9d5 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSearchIntegrationInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <!--Reset Search Filters --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <!--Fill Integration Name Field --> + <fillField selector="{{IntegrationsGridSection.name}}" userInput="{{name}}" stepKey="filterByName"/> + <!--Click "Search" Button --> + <click selector="{{IntegrationsGridSection.search}}" stepKey="doFilter"/> + <waitForPageLoad stepKey="waitForSitemapPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..e928149c7f08f --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminMessageCreateIntegrationEntityActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="The integration '{{name}}' has been saved."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{IntegrationsGridSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{IntegrationsGridSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml new file mode 100644 index 0000000000000..895f147fa8834 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertDeletedIntegrationIsNotInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <dontSee userInput="{{name}}" selector="{{IntegrationsGridSection.rowByIndex('1')}}" stepKey="donSeeIntegration"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml new file mode 100644 index 0000000000000..4e43cd3babdf6 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsSection.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + + <section name="IntegrationsGridSection"> + <element name="add" type="button" selector=".add"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + <element name="name" type="input" selector="#integrationGrid_filter_name"/> + <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> + <element name="remove" type="button" selector=".delete"/> + <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> + </section> + + <section name="AddNewIntegrationSection"> + <element name="name" type="input" selector="#integration_properties_name"/> + <element name="password" type="input" selector="#integration_properties_current_password"/> + <element name="saveButton" type="button" selector="#save-split-button-button"/> + </section> +</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml new file mode 100644 index 0000000000000..00dc9b320d7ad --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteIntegrationEntityTest"> + <annotations> + <features value="Integration"/> + <stories value="System Integration"/> + <title value="Admin system integration"/> + <description value="Admin Deletes Created Integration"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login As Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <!-- Click the "Add New Integration" button --> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <!-- Create New Integration --> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Find Created Integration In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <!-- Delete Created Integration Entity --> + <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteIntegration"/> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The integration 'Integration1' has been deleted."/> + <argument value="success" name="messageType"/> + </actionGroup> + <!-- Assert Deleted Integration Is Not In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findDeletedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <actionGroup ref="AssertDeletedIntegrationIsNotInGridActionGroup" stepKey="dontSeeIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml index 607c0abf4302e..a43b88469faae 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml @@ -11,6 +11,7 @@ <data name="integration/dataset" xsi:type="string">default</data> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationSuccessDeleteMessage" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 5f4254ee2a1047d9b4c66a1624f885bb59b19277 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 6 Sep 2019 16:47:04 +0400 Subject: [PATCH 0500/2437] MC-17869: Cart Total is shown as NaN when 100% discount applied through Cart Rule - Updated automated test script --- ...lValueWithFullDiscountUsingCartRuleTest.xml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml index b1e11cbf07ff6..c8a8f6db850f9 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -10,7 +10,7 @@ <test name="StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest"> <annotations> <features value="Quote"/> - <stories value="Cart total value with full discount"/> + <stories value="Cart total with full discount"/> <title value="Cart Total value when 100% discount applied through Cart Rule"/> <description value="Cart Total value when 100% discount applied through Cart Rule"/> <severity value="CRITICAL"/> @@ -30,12 +30,8 @@ <magentoCLI command="config:set tax/calculation/shipping_includes_tax 1" stepKey="setSippingPrice"/> <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 0" stepKey="setCrossBorderTrade"/> <magentoCLI command="config:set tax/calculation/discount_tax 1" stepKey="setDiscount"/> - <magentoCLI command="config:set tax/cart_display/gift_wrapping 1" stepKey="setGiftWrapping"/> - <magentoCLI command="config:set tax/cart_display/printed_card 1" stepKey="setPrintedCart"/> <magentoCLI command="config:set tax/cart_display/price 2" stepKey="setPrice"/> <magentoCLI command="config:set tax/cart_display/subtotal 2" stepKey="setSubtotal"/> - <magentoCLI command="config:set tax/sales_display/gift_wrapping 1" stepKey="setSalesGiftWrapping"/> - <magentoCLI command="config:set tax/sales_display/printed_card 1" stepKey="setSalesPrintedCart"/> <magentoCLI command="config:set carriers/freeshipping/active 1" stepKey="setFreeShipping"/> <createData entity="defaultTaxRule" stepKey="initialTaxRule"/> <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> @@ -67,6 +63,8 @@ <createData entity="SimpleProduct2" stepKey="createSimpleProductThird"> <field key="price">5.50</field> </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!-- Removed created Data --> @@ -100,12 +98,8 @@ <magentoCLI command="config:set tax/calculation/shipping_includes_tax 0" stepKey="unsetSippingPrice"/> <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 1" stepKey="unsetCrossBorderTrade"/> <magentoCLI command="config:set tax/calculation/discount_tax 0" stepKey="unsetDiscount"/> - <magentoCLI command="config:set tax/cart_display/gift_wrapping 0" stepKey="unsetGiftWrapping"/> - <magentoCLI command="config:set tax/cart_display/printed_card 0" stepKey="unsetPrintedCart"/> <magentoCLI command="config:set tax/cart_display/price 0" stepKey="unsetPrice"/> <magentoCLI command="config:set tax/cart_display/subtotal 0" stepKey="unsetSubtotal"/> - <magentoCLI command="config:set tax/sales_display/gift_wrapping 0" stepKey="unsetSalesGiftWrapping"/> - <magentoCLI command="config:set tax/sales_display/printed_card 0" stepKey="unsetSalesPrintedCart"/> <magentoCLI command="config:set carriers/freeshipping/active 0" stepKey="unsetFreeShipping"/> <!-- Log out --> <comment userInput="Log out" stepKey="commentLogOut"/> @@ -132,9 +126,9 @@ <see selector="{{StorefrontMinicartSection.quantity}}" userInput="6" stepKey="seeCartQuantity"/> <!-- Go to the shopping cart page --> <comment userInput="Go to the shopping cart page" stepKey="commentGotToShippingCartPage"/> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> - <waitForPageLoad stepKey="waitForCartPageLoad"/> - <wait time="200" stepKey="erfgr"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.orderTotal}}" stepKey="waitForOrderTotalVisible"/> <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$31.40" stepKey="seeDiscountAmount"/> <see selector="{{CheckoutCartSummarySection.subTotal}}" userInput="$31.40" stepKey="seeSubTotal"/> <see selector="{{CheckoutCartSummarySection.orderTotal}}" userInput="0.00" stepKey="seeOrderTotal"/> From 79cb787f04fa7c27b036b0a6c54b2f99e9adcabd Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Wed, 4 Sep 2019 17:49:37 -0500 Subject: [PATCH 0501/2437] MC-19398: Changing Attribute Set while editing disabled Product makes it enabled - Fixing static issues and Unit tests --- .../Product/Form/Modifier/GeneralTest.php | 75 ++++++++++++++++--- .../Product/Form/Modifier/General.php | 57 +++++++------- 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php index a9d717db7b7f9..16e3aa08c993f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php @@ -33,7 +33,7 @@ protected function setUp() parent::setUp(); $this->attributeRepositoryMock = $this->getMockBuilder(AttributeRepositoryInterface::class) - ->getMockForAbstractClass(); + ->getMockForAbstractClass(); $arrayManager = $this->objectManager->getObject(ArrayManager::class); @@ -52,10 +52,13 @@ protected function setUp() */ protected function createModel() { - return $this->objectManager->getObject(General::class, [ + return $this->objectManager->getObject( + General::class, + [ 'locator' => $this->locatorMock, 'arrayManager' => $this->arrayManagerMock, - ]); + ] + ); } public function testModifyMeta() @@ -63,8 +66,10 @@ public function testModifyMeta() $this->arrayManagerMock->expects($this->any()) ->method('merge') ->willReturnArgument(2); - $this->assertNotEmpty($this->getModel()->modifyMeta([ - 'first_panel_code' => [ + $this->assertNotEmpty( + $this->getModel()->modifyMeta( + [ + 'first_panel_code' => [ 'arguments' => [ 'data' => [ 'config' => [ @@ -72,15 +77,17 @@ public function testModifyMeta() ] ], ] - ] - ])); + ] + ] + ) + ); } /** - * @param array $data - * @param int $defaultStatusValue - * @param array $expectedResult - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param array $data + * @param int $defaultStatusValue + * @param array $expectedResult + * @throws \Magento\Framework\Exception\NoSuchEntityException * @dataProvider modifyDataDataProvider */ public function testModifyDataNewProduct(array $data, int $defaultStatusValue, array $expectedResult) @@ -100,6 +107,52 @@ public function testModifyDataNewProduct(array $data, int $defaultStatusValue, a $this->assertSame($expectedResult, $this->generalModifier->modifyData($data)); } + /** + * Verify the product attribute status set owhen editing existing product + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testModifyDataExistingProduct() + { + $data = []; + $modelId = 1; + $defaultStatusValue = 1; + $expectedResult = [ + 'enabledProductStatus' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 1, + ], + ], + 'disabledProductStatus' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 2, + ], + ], + ]; + $enabledProductStatus = 1; + $disabledProductStatus = 2; + $attributeMock = $this->getMockBuilder(AttributeInterface::class) + ->getMockForAbstractClass(); + $attributeMock + ->method('getDefaultValue') + ->willReturn($defaultStatusValue); + $this->attributeRepositoryMock + ->method('get') + ->with( + ProductAttributeInterface::ENTITY_TYPE_CODE, + ProductAttributeInterface::CODE_STATUS + ) + ->willReturn($attributeMock); + $this->productMock->expects($this->any()) + ->method('getId') + ->willReturn($modelId); + $this->productMock->expects($this->any()) + ->method('getStatus') + ->willReturnOnConsecutiveCalls($enabledProductStatus, $disabledProductStatus); + $this->assertSame($expectedResult['enabledProductStatus'], current($this->generalModifier->modifyData($data))); + $this->assertSame($expectedResult['disabledProductStatus'], current($this->generalModifier->modifyData($data))); + } + /** * @return array */ diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 91c74a2da5048..f4046b4a600db 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -21,13 +21,13 @@ class General extends AbstractModifier { /** - * @var LocatorInterface + * @var LocatorInterface * @since 101.0.0 */ protected $locator; /** - * @var ArrayManager + * @var ArrayManager * @since 101.0.0 */ protected $arrayManager; @@ -43,8 +43,8 @@ class General extends AbstractModifier private $attributeRepository; /** - * @param LocatorInterface $locator - * @param ArrayManager $arrayManager + * @param LocatorInterface $locator + * @param ArrayManager $arrayManager * @param AttributeRepositoryInterface|null $attributeRepository */ public function __construct( @@ -61,10 +61,10 @@ public function __construct( /** * Customize number fields for advanced price and weight fields. * - * @param array $data + * @param array $data * @return array * @throws \Magento\Framework\Exception\NoSuchEntityException - * @since 101.0.0 + * @since 101.0.0 */ public function modifyData(array $data) { @@ -72,7 +72,10 @@ public function modifyData(array $data) $data = $this->customizeAdvancedPriceFormat($data); $modelId = $this->locator->getProduct()->getId(); - if (!isset($data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS])) { + $productStatus = $this->locator->getProduct()->getStatus(); + if ((isset($productStatus) && !empty($productStatus)) && (isset($modelId)) && !empty($modelId)) { + $data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS] = $productStatus; + } elseif (!isset($data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS])) { $attributeStatus = $this->attributeRepository->get( ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_STATUS @@ -87,9 +90,9 @@ public function modifyData(array $data) /** * Customizing weight fields * - * @param array $data + * @param array $data * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeWeightFormat(array $data) { @@ -112,9 +115,9 @@ protected function customizeWeightFormat(array $data) /** * Customizing number fields for advanced price * - * @param array $data + * @param array $data * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeAdvancedPriceFormat(array $data) { @@ -136,9 +139,9 @@ protected function customizeAdvancedPriceFormat(array $data) /** * Customize product form fields. * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ public function modifyMeta(array $meta) { @@ -154,9 +157,9 @@ public function modifyMeta(array $meta) /** * Disable collapsible and set empty label * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function prepareFirstPanel(array $meta) { @@ -177,9 +180,9 @@ protected function prepareFirstPanel(array $meta) /** * Customize Status field * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeStatusField(array $meta) { @@ -203,9 +206,9 @@ protected function customizeStatusField(array $meta) /** * Customize Weight filed * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeWeightField(array $meta) { @@ -277,9 +280,9 @@ protected function customizeWeightField(array $meta) /** * Customize "Set Product as New" date fields * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeNewDateRangeField(array $meta) { @@ -335,9 +338,9 @@ protected function customizeNewDateRangeField(array $meta) /** * Add links for fields depends of product name * - * @param array $meta + * @param array $meta * @return array - * @since 101.0.0 + * @since 101.0.0 */ protected function customizeNameListeners(array $meta) { @@ -409,9 +412,9 @@ private function getLocaleCurrency() /** * Format price according to the locale of the currency * - * @param mixed $value + * @param mixed $value * @return string - * @since 101.0.0 + * @since 101.0.0 */ protected function formatPrice($value) { @@ -429,9 +432,9 @@ protected function formatPrice($value) /** * Format number according to the locale of the currency and precision of input * - * @param mixed $value + * @param mixed $value * @return string - * @since 101.0.0 + * @since 101.0.0 */ protected function formatNumber($value) { From c5319ae90944006fe86be53fac1a5fc3b87e60a8 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Wed, 4 Sep 2019 17:49:37 -0500 Subject: [PATCH 0502/2437] MC-19398: Changing Attribute Set while editing disabled Product makes it enabled - Fixing unit tests --- .../DisableProductLabelActionGroup.xml | 27 +++++ ...sableProductOnChangingAttributeSetTest.xml | 49 +++++++++ .../Product/Form/Modifier/GeneralTest.php | 101 +++++++++++++----- .../Product/Form/Modifier/General.php | 2 +- 4 files changed, 150 insertions(+), 29 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/DisableProductLabelActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DisableProductLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DisableProductLabelActionGroup.xml new file mode 100644 index 0000000000000..a416957dabc2b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DisableProductLabelActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DisableProductLabelActionGroup"> + <annotations> + <description>Disable Product Label and Change Attribute Set.</description> + </annotations> + <arguments> + <argument name="createAttributeSet"/> + </arguments> + + <checkOption selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <waitForPageLoad time="30" stepKey="waitForChangeAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{createAttributeSet.attribute_set_name}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + <dontSeeCheckboxIsChecked selector="{{AdminProductFormSection.productStatus}}" stepKey="dontSeeCheckboxEnableProductIsChecked"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml new file mode 100644 index 0000000000000..dab1704d50bf3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDisableProductOnChangingAttributeSetTest"> + <annotations> + <features value="Catalog"/> + <stories value="Disabled product is enabled when change attribute set"/> + <title value="Verify product status while changing attribute set"/> + <description value="Value set for enabled product has to be shown when attribute set is changed"/> + <severity value="MAJOR"/> + <testCaseId value="MC-19716"/> + <group value="catalog"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + </before> + <after> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductsFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> + <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="DisableProductLabelActionGroup" stepKey="disableWhileChangingAttributeSet" > + <argument name="createAttributeSet" value="$$createAttributeSet$$"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php index 16e3aa08c993f..9d0e7fc57ffce 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/GeneralTest.php @@ -110,33 +110,26 @@ public function testModifyDataNewProduct(array $data, int $defaultStatusValue, a /** * Verify the product attribute status set owhen editing existing product * - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param array $data + * @param string $modelId + * @param int $defaultStatus + * @param int $statusAttributeValue + * @param array $expectedResult + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @dataProvider modifyDataOfExistingProductDataProvider */ - public function testModifyDataExistingProduct() - { - $data = []; - $modelId = 1; - $defaultStatusValue = 1; - $expectedResult = [ - 'enabledProductStatus' => [ - General::DATA_SOURCE_DEFAULT => [ - ProductAttributeInterface::CODE_STATUS => 1, - ], - ], - 'disabledProductStatus' => [ - General::DATA_SOURCE_DEFAULT => [ - ProductAttributeInterface::CODE_STATUS => 2, - ], - ], - ]; - $enabledProductStatus = 1; - $disabledProductStatus = 2; - $attributeMock = $this->getMockBuilder(AttributeInterface::class) - ->getMockForAbstractClass(); - $attributeMock + public function testModifyDataOfExistingProduct( + array $data, + string $modelId, + int $defaultStatus, + int $statusAttributeValue, + array $expectedResult + ) { + $attributeMock = $this->getMockForAbstractClass(AttributeInterface::class); + $attributeMock->expects($this->any()) ->method('getDefaultValue') - ->willReturn($defaultStatusValue); - $this->attributeRepositoryMock + ->willReturn($defaultStatus); + $this->attributeRepositoryMock->expects($this->any()) ->method('get') ->with( ProductAttributeInterface::ENTITY_TYPE_CODE, @@ -148,9 +141,61 @@ public function testModifyDataExistingProduct() ->willReturn($modelId); $this->productMock->expects($this->any()) ->method('getStatus') - ->willReturnOnConsecutiveCalls($enabledProductStatus, $disabledProductStatus); - $this->assertSame($expectedResult['enabledProductStatus'], current($this->generalModifier->modifyData($data))); - $this->assertSame($expectedResult['disabledProductStatus'], current($this->generalModifier->modifyData($data))); + ->willReturn($statusAttributeValue); + $this->assertSame($expectedResult, current($this->generalModifier->modifyData($data))); + } + + /** + * @return array + */ + public function modifyDataOfExistingProductDataProvider(): array + { + return [ + 'With enable status value' => [ + 'data' => [], + 'modelId' => '1', + 'defaultStatus' => 1, + 'statusAttributeValue' => 1, + 'expectedResult' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 1, + ], + ], + ], + 'Without disable status value' => [ + 'data' => [], + 'modelId' => '1', + 'defaultStatus' => 1, + 'statusAttributeValue' => 2, + 'expectedResult' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 2, + ], + ], + ], + 'With enable status value with empty modelId' => [ + 'data' => [], + 'modelId' => '', + 'defaultStatus' => 1, + 'statusAttributeValue' => 1, + 'expectedResult' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 1, + ], + ], + ], + 'Without disable status value with empty modelId' => [ + 'data' => [], + 'modelId' => '', + 'defaultStatus' => 2, + 'statusAttributeValue' => 2, + 'expectedResult' => [ + General::DATA_SOURCE_DEFAULT => [ + ProductAttributeInterface::CODE_STATUS => 2, + ], + ], + ], + ]; } /** diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index f4046b4a600db..7c42b881bad3e 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -73,7 +73,7 @@ public function modifyData(array $data) $modelId = $this->locator->getProduct()->getId(); $productStatus = $this->locator->getProduct()->getStatus(); - if ((isset($productStatus) && !empty($productStatus)) && (isset($modelId)) && !empty($modelId)) { + if (!empty($productStatus) && !empty($modelId)) { $data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS] = $productStatus; } elseif (!isset($data[$modelId][static::DATA_SOURCE_DEFAULT][ProductAttributeInterface::CODE_STATUS])) { $attributeStatus = $this->attributeRepository->get( From 3e5cb451a2a18c9f00f0360fb83d0bcc6a4c1391 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Sat, 7 Sep 2019 10:52:51 -0500 Subject: [PATCH 0503/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Catalog/Model/Product.php | 5 ++++- .../testsuite/Magento/Cms/Model/Page/DataProviderTest.php | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 560a1fbacda41..de0628d58df27 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -828,7 +828,10 @@ public function getStoreIds() $storeIds[] = $websiteStores; } } - $this->setStoreIds(array_merge(...$storeIds)); + if ($storeIds) { + $storeIds = array_merge(...$storeIds); + } + $this->setStoreIds($storeIds); } return $this->getData('store_ids'); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php index 2d2d36178ada8..d2ca833f3923f 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -49,16 +49,17 @@ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->repo = $objectManager->get(GetPageByIdentifierInterface::class); + $this->filesFaker = $objectManager->get(CustomLayoutManager::class); + $this->request = $objectManager->get(HttpRequest::class); $this->provider = $objectManager->create( DataProvider::class, [ 'name' => 'test', 'primaryFieldName' => 'page_id', - 'requestFieldName' => 'page_id' + 'requestFieldName' => 'page_id', + 'customLayoutManager' => $this->filesFaker ] ); - $this->filesFaker = $objectManager->get(CustomLayoutManager::class); - $this->request = $objectManager->get(HttpRequest::class); } /** From 06a2b7b1b30bb6747849e6cf66e739e0d817fd64 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 9 Sep 2019 11:58:50 +0300 Subject: [PATCH 0504/2437] MC-18165: Quick search with two chars shows all products --- .../ResourceModel/Fulltext/Collection.php | 45 ++++++++++++------- .../StorefrontCatalogSearchActionGroup.xml | 20 ++++++++- .../StorefrontCatalogSearchMainSection.xml | 2 + .../Mftf/Test/SearchEntityResultsTest.xml | 19 +++++--- .../view/frontend/templates/form.mini.phtml | 3 +- .../Search/view/frontend/web/js/form-mini.js | 2 +- .../ResourceModel/Fulltext/CollectionTest.php | 5 ++- 7 files changed, 70 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 4f84f3868c6a3..a5be9dc2fd82b 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -212,10 +212,8 @@ public function __construct( DefaultFilterStrategyApplyCheckerInterface $defaultFilterStrategyApplyChecker = null ) { $this->queryFactory = $catalogSearchData; - if ($searchResultFactory === null) { - $this->searchResultFactory = \Magento\Framework\App\ObjectManager::getInstance() + $this->searchResultFactory = $searchResultFactory ?? \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Api\Search\SearchResultFactory::class); - } parent::__construct( $entityFactory, $logger, @@ -427,25 +425,40 @@ protected function _renderFiltersBefore() return; } - $this->prepareSearchTermFilter(); - $this->preparePriceAggregation(); - - $searchCriteria = $this->getSearchCriteriaResolver()->resolve(); - try { - $this->searchResult = $this->getSearch()->search($searchCriteria); - $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve(); - } catch (EmptyRequestDataException $e) { - /** @var \Magento\Framework\Api\Search\SearchResultInterface $searchResult */ - $this->searchResult = $this->searchResultFactory->create()->setItems([]); - } catch (NonExistingRequestNameException $e) { - $this->_logger->error($e->getMessage()); - throw new LocalizedException(__('An error occurred. For details, see the error log.')); + if ($this->searchRequestName != 'quick_search_container' + || strlen(trim($this->queryText)) + ) { + $this->prepareSearchTermFilter(); + $this->preparePriceAggregation(); + + $searchCriteria = $this->getSearchCriteriaResolver()->resolve(); + try { + $this->searchResult = $this->getSearch()->search($searchCriteria); + $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve(); + } catch (EmptyRequestDataException $e) { + $this->searchResult = $this->createEmptyResult(); + } catch (NonExistingRequestNameException $e) { + $this->_logger->error($e->getMessage()); + throw new LocalizedException(__('An error occurred. For details, see the error log.')); + } + } else { + $this->searchResult = $this->createEmptyResult(); } $this->getSearchResultApplier($this->searchResult)->apply(); parent::_renderFiltersBefore(); } + /** + * Create empty search result + * + * @return SearchResultInterface + */ + private function createEmptyResult() + { + return $this->searchResultFactory->create()->setItems([]); + } + /** * Set sort order for search query. * diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index a72762ff796e0..a907df2d718df 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -41,6 +41,24 @@ <see userInput="Search results for: '{{phrase}}'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> </actionGroup> + <actionGroup name="StorefrontQuickSearchTooShortStringActionGroup" extends="StorefrontCheckQuickSearchStringActionGroup"> + <annotations> + <description>Fill the Storefront Search field. Submits the Form. Validates that 'Minimum Search query length' warning appears.</description> + </annotations> + + <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Minimum Search query length is 3" stepKey="assertQuickSearchNeedThreeOrMoreChars"/> + </actionGroup> + + <actionGroup name="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup"> + <arguments> + <argument name="term" type="string"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCatalogSearchMainSection.relatedSearchTermsTitle}}" stepKey="waitMessageAppears"/> + <see selector="{{StorefrontCatalogSearchMainSection.relatedSearchTermsTitle}}" userInput="Related search terms" stepKey="checkRelatedTermsTitle"/> + <see selector="{{StorefrontCatalogSearchMainSection.relatedSearchTerm}}" userInput="{{term}}" stepKey="checkRelatedTermExists"/> + </actionGroup> + <!-- Opens product from QuickSearch and performs assertions--> <actionGroup name="StorefrontOpenProductFromQuickSearch"> <annotations> @@ -101,7 +119,7 @@ <dontSee selector="{{StorefrontQuickSearchResultsSection.allResults}}" userInput="{{productName}}" stepKey="dontSeeProductName"/> </actionGroup> - + <!-- Open advanced search page --> <actionGroup name="StorefrontOpenAdvancedSearchActionGroup"> <annotations> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml index 667f08fea6579..b005e100b30bb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml @@ -16,5 +16,7 @@ <element name="productCount" type="text" selector="#toolbar-amount"/> <element name="message" type="text" selector="div.message div"/> <element name="searchResults" type="block" selector="#maincontent .column.main"/> + <element name="relatedSearchTermsTitle" type="text" selector="div.message dl.block dt.title"/> + <element name="relatedSearchTerm" type="text" selector="div.message dl.block dd.item a"/> </section> </sections> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 19db201e91f40..47d107148a574 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -92,6 +92,7 @@ <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="ThisShouldn'tReturnAnything"/> @@ -100,6 +101,7 @@ </test> <test name="QuickSearchWithTwoCharsEmptyResults" extends="QuickSearchEmptyResults"> <annotations> + <features value="CatalogSearch"/> <stories value="Search Product on Storefront"/> <title value="User should not get search results on query that only contains two characters"/> <description value="Use of 2 character query to return no products"/> @@ -107,13 +109,18 @@ <testCaseId value="MC-14794"/> <group value="CatalogSearch"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-15827"/> - </skip> </annotations> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,2); return ret;" stepKey="getFirstTwoLetters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$getFirstTwoLetters}"/> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,2); return ret;" before="searchStorefront" stepKey="getFirstTwoLetters"/> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" before="searchStorefront" stepKey="getFirstThreeLetters"/> + + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontThreeLetters"> + <argument name="phrase" value="$getFirstThreeLetters"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontThreeLetters" stepKey="checkCannotSearchWithTooShortString"> + <argument name="phrase" value="$getFirstTwoLetters"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> + <argument name="term" value="$getFirstThreeLetters"/> </actionGroup> </test> <test name="QuickSearchProductByNameWithThreeLetters" extends="QuickSearchProductBySku"> diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index 766dd4d992bd4..0dd9c819c855a 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -24,7 +24,8 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); data-mage-init='{"quickSearch":{ "formSelector":"#search_mini_form", "url":"<?= $block->escapeUrl($helper->getSuggestUrl())?>", - "destinationSelector":"#search_autocomplete"} + "destinationSelector":"#search_autocomplete", + "minSearchLength":"<?= $block->escapeHtml($helper->getMinQueryLength()) ?>"} }' type="text" name="<?= $block->escapeHtmlAttr($helper->getQueryParamName()) ?>" diff --git a/app/code/Magento/Search/view/frontend/web/js/form-mini.js b/app/code/Magento/Search/view/frontend/web/js/form-mini.js index 64e6eceb1eba6..b4493c5f38089 100644 --- a/app/code/Magento/Search/view/frontend/web/js/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/js/form-mini.js @@ -30,7 +30,7 @@ define([ $.widget('mage.quickSearch', { options: { autocomplete: 'off', - minSearchLength: 2, + minSearchLength: 3, responseFieldElements: 'ul li', selectClass: 'selected', template: diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php index 93df194080b69..e84d64b681c40 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php @@ -26,6 +26,9 @@ public function testLoadWithFilterSearch($request, $filters, $expectedCount) foreach ($filters as $field => $value) { $fulltextCollection->addFieldToFilter($field, $value); } + if ($request == 'quick_search_container' && isset($filters['search_term'])) { + $fulltextCollection->addSearchFilter($filters['search_term']); + } $fulltextCollection->loadWithFilter(); $items = $fulltextCollection->getItems(); $this->assertCount($expectedCount, $items); @@ -48,7 +51,7 @@ public function testSearchResultsAreTheSameForSameRequests() ['searchRequestName' => 'quick_search_container'] ); - $fulltextCollection->addFieldToFilter('search_term', 'shorts'); + $fulltextCollection->addSearchFilter('shorts'); $fulltextCollection->setOrder('relevance'); $fulltextCollection->load(); $items = $fulltextCollection->getItems(); From 30d35df50c7767f66a76c0c0a1e15155111407b6 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Sun, 1 Sep 2019 23:24:07 +0300 Subject: [PATCH 0505/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Fix CR comments --- .../Sales/Model/Order/Shipment/TrackRepository.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index 93396976565ea..3cf173117b4b6 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -120,13 +120,11 @@ public function delete(ShipmentTrackInterface $entity) */ public function save(ShipmentTrackInterface $entity) { - $shipments = $this->shipmentCollection->create()->addFieldToFilter('order_id', $entity['order_id']); - $shipmentId = []; - foreach ($shipments->getItems() as $shipment) { - $shipmentId[] = $shipment->getId(); - } + $shipments = $this->shipmentCollection->create() + ->addFieldToFilter('entity_id', $entity['parent_id']) + ->toArray(); - if (array_search($entity['parent_id'], $shipmentId) === false) { + if (empty($shipments['items'])) { $this->logger->error('The shipment doesn\'t belong to the order.'); throw new CouldNotSaveException(__('Could not save the shipment tracking.')); } From 30d8cac72b4e2b64e2f8c8f20bf1d4e646167284 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 23 Aug 2019 15:49:14 +0300 Subject: [PATCH 0506/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6405 --- .../Customer/Test/Mftf/Data/AddressData.xml | 18 +++++ .../Customer/Test/Mftf/Data/CustomerData.xml | 13 ++++ .../Customer/Test/Mftf/Data/RegionData.xml | 5 ++ .../StorefrontShipmentActionGroup.xml | 18 +++++ ...splayTableRatesShippingMethodForAETest.xml | 76 +++++++++++++++++++ .../acceptance/tests/_data/tablerates.csv | 21 +++++ 6 files changed, 151 insertions(+) create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml create mode 100644 dev/tests/acceptance/tests/_data/tablerates.csv diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4d7a39b3246e1..756574b76d4e8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -312,4 +312,22 @@ <data key="postcode">90230</data> <data key="telephone">555-55-555-55</data> </entity> + <entity name="US_Address_AFE" type="address"> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>7700 West Parmer Lane</item> + <item>113</item> + </array> + <data key="city">Los Angeles</data> + <data key="state">Armed Forces Europe</data> + <data key="country_id">US</data> + <data key="country">United States</data> + <data key="postcode">90001</data> + <data key="telephone">512-345-6789</data> + <data key="default_billing">Yes</data> + <data key="default_shipping">Yes</data> + <requiredEntity type="region">RegionAFE</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index c7a73b61dc48a..f10732c559038 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -309,4 +309,17 @@ <data key="store_id">0</data> <data key="website_id">0</data> </entity> + <entity name="Simple_US_Customer_ArmedForcesEurope" type="customer"> + <data key="group_id">0</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">US_Address_AFE</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml index 280bae7de411a..1e6589057fa7f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml @@ -32,4 +32,9 @@ <data key="region_code">UT</data> <data key="region_id">58</data> </entity> + <entity name="RegionAFE" type="region"> + <data key="region">Armed Forces Europe</data> + <data key="region_code">AFE</data> + <data key="region_id">9</data> + </entity> </entities> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml new file mode 100644 index 0000000000000..6e75ed9217171 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetShippingMethodActionGroup"> + <arguments> + <argument name="shippingMethodName" type="string" defaultValue="Flat Rate"/> + </arguments> + <checkOption selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName(shippingMethodName)}}" stepKey="selectFlatRateShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForNextButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml new file mode 100644 index 0000000000000..5b6bcd1ebcd97 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontDisplayTableRatesShippingMethodForAETest"> + <annotations> + <features value="Shipping"/> + <stories value="Table Rates"/> + <title value="Displaying of Table Rates for Armed Forces Europe (AE)"/> + <description value="Displaying of Table Rates for Armed Forces Europe (AE)"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6405"/> + <group value="shipping"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer_ArmedForcesEurope" stepKey="createCustomer"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <comment userInput="Rollback config" stepKey="rollbackConfigComment"/> + <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodSystemConfigPage"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreViewToMainWebsite"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="disableTableRatesShippingMethod"> + <argument name="status" value="0"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveSystemConfig"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <comment userInput="Admin Configuration: enable Table Rates and import CSV file with the rates" stepKey="prepareAdminConfigurationComment"/> + <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreView"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethod"/> + <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="tablerates.csv" stepKey="attachFileForImport"/> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> + <comment userInput="Login as created customer" stepKey="loginAsCustomerComment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <comment userInput="Add the created product to the shopping cart" stepKey="addProductToCartComment"/> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <comment userInput="Proceed to Checkout from the mini cart" stepKey="proceedToCheckoutComment"/> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + <comment userInput="Shipping Method: select table rate" stepKey="assertShippingMethodComment"/> + <actionGroup ref="AssertStoreFrontShippingMethodAvailableActionGroup" stepKey="assertShippingMethodAvailable"> + <argument name="shippingMethodName" value="Best Way"/> + </actionGroup> + <actionGroup ref="SetShippingMethodActionGroup" stepKey="setShippingMethodTableRate"> + <argument name="shippingMethodName" value="Best Way"/> + </actionGroup> + <comment userInput="Proceed to Review and Payments section" stepKey="proceedToReviewAndPaymentsComment"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickToSaveShippingInfo"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> + <waitForPageLoad stepKey="waitForReviewAndPaymentsPageIsLoaded"/> + <comment userInput="Place order and assert the message of success" stepKey="placeOrderComment"/> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/tablerates.csv b/dev/tests/acceptance/tests/_data/tablerates.csv new file mode 100644 index 0000000000000..ddc591798e3cc --- /dev/null +++ b/dev/tests/acceptance/tests/_data/tablerates.csv @@ -0,0 +1,21 @@ +Country,Region/State,Zip/Postal Code,Weight (and above),Shipping Price +ASM,*,*,0,9.95 +FSM,*,*,0,9.95 +GUM,*,*,0,9.95 +MHL,*,*,0,9.95 +MNP,*,*,0,9.95 +PLW,*,*,0,9.95 +USA,AA,*,0,9.95 +USA,AE,*,0,9.95 +USA,AK,*,0,9.95 +USA,AP,*,0,9.95 +USA,AS,*,0,9.95 +USA,FM,*,0,9.95 +USA,GU,*,0,9.95 +USA,HI,*,0,9.95 +USA,MH,*,0,9.95 +USA,MP,*,0,9.95 +USA,PR,*,0,9.95 +USA,PW,*,0,9.95 +USA,VI,*,0,9.95 +VIR,*,*,0,9.95 \ No newline at end of file From 9097ecccbc95c1c15e385ece55fcb2bda62557a7 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 9 Sep 2019 13:14:44 +0300 Subject: [PATCH 0507/2437] MC-19783: Weight attribute label is missing --- .../Catalog/Ui/DataProvider/Product/Form/Modifier/General.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 91c74a2da5048..e783fc61fe0ce 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -221,6 +221,7 @@ protected function customizeWeightField(array $meta) 'validate-zero-or-greater' => true ], 'additionalClasses' => 'admin__field-small', + 'sortOrder' => 0, 'addafter' => $this->locator->getStore()->getConfig('general/locale/weight_unit'), 'imports' => $disabled ? [] : [ 'disabled' => '!${$.provider}:' . self::DATA_SCOPE_PRODUCT @@ -266,6 +267,7 @@ protected function customizeWeightField(array $meta) ], ], 'value' => (int)$this->locator->getProduct()->getTypeInstance()->hasWeight(), + 'sortOrder' => 10, 'disabled' => $disabled, ] ); From 2bdab9af3c6bf0c7df63fb148ce1d396e97ab322 Mon Sep 17 00:00:00 2001 From: Krissy Hiserote <khiserote@magento.com> Date: Mon, 9 Sep 2019 11:56:03 -0500 Subject: [PATCH 0508/2437] MC-18532: Update Changelog based on delivered scope - remove reverts and fix minor issues --- CHANGELOG.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24e445b043bfa..59adb577b0cd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * [#22527](https://github.com/magento/magento2/issues/22527) -- Wishlist and compare icon align issue in product listing page (fixed in [magento/magento2#22532](https://github.com/magento/magento2/pull/22532)) * [#628](https://github.com/magento/magento2/issues/628) -- Feature Request: Parent entities links in child entities grid. (fixed in [magento/graphql-ce#636](https://github.com/magento/graphql-ce/pull/636)) * [#640](https://github.com/magento/magento2/issues/640) -- [Insight] Files should not be executable (fixed in [magento/graphql-ce#648](https://github.com/magento/graphql-ce/pull/648)) - * [#603](https://github.com/magento/magento2/issues/603) -- 'Continue' button is disabled even though 'I've read OSL licence' is checked (fixed in [magento/graphql-ce#653](https://github.com/magento/graphql-ce/pull/653)) + * [#603](https://github.com/magento/magento2/issues/603) -- 'Continue' button is disabled even though "I've read OSL licence" is checked (fixed in [magento/graphql-ce#653](https://github.com/magento/graphql-ce/pull/653)) * [#22406](https://github.com/magento/magento2/issues/22406) -- Store view specific labels cut in left navigation menu (fixed in [magento/magento2#22423](https://github.com/magento/magento2/pull/22423)) * [#19515](https://github.com/magento/magento2/issues/19515) -- Create new order from backend saves the credit card when it is told not to (fixed in [magento/magento2#19767](https://github.com/magento/magento2/pull/19767)) * [#21473](https://github.com/magento/magento2/issues/21473) -- Form element validation is not triggered when validation rules change (fixed in [magento/magento2#21992](https://github.com/magento/magento2/pull/21992)) @@ -93,7 +93,7 @@ * [#22004](https://github.com/magento/magento2/issues/22004) -- ce231 - can't update attribute for all product (fixed in [magento/magento2#22704](https://github.com/magento/magento2/pull/22704)) * [#22870](https://github.com/magento/magento2/issues/22870) -- ProductRepository fails to update an existing product with a changed SKU (fixed in [magento/magento2#22933](https://github.com/magento/magento2/pull/22933)) * [#22808](https://github.com/magento/magento2/issues/22808) -- php bin/magento catalog:image:resize error if image is missing (fixed in [magento/magento2#23005](https://github.com/magento/magento2/pull/23005)) - * [#674](https://github.com/magento/magento2/issues/674) -- Widgets in content pages. (fixed in [magento/graphql-ce#709](https://github.com/magento/graphql-ce/pull/709)) + * [#674](https://github.com/magento/magento2/issues/674) -- Widgets in content pages. (fixed in [magento/graphql-ce#709](https://github.com/magento/graphql-ce/pull/709)) * [#683](https://github.com/magento/magento2/issues/683) -- CMS Router not routing correctly (fixed in [magento/graphql-ce#717](https://github.com/magento/graphql-ce/pull/717)) * [#9113](https://github.com/magento/magento2/issues/9113) -- [Bug or Feature?] url_path attribute value is not populated for any product (fixed in [magento/graphql-ce#721](https://github.com/magento/graphql-ce/pull/721)) * [#18337](https://github.com/magento/magento2/issues/18337) -- #search input is missing required attribute aria-expanded. (fixed in [magento/magento2#22942](https://github.com/magento/magento2/pull/22942)) @@ -137,8 +137,6 @@ * [#16234](https://github.com/magento/magento2/issues/16234) -- Unable to enter `+` character in widget content (fixed in [magento/magento2#23496](https://github.com/magento/magento2/pull/23496)) * [#9798](https://github.com/magento/magento2/issues/9798) -- Problem adding attribute options to configurable product via REST Api (fixed in [magento/magento2#23529](https://github.com/magento/magento2/pull/23529)) * [#6287](https://github.com/magento/magento2/issues/6287) -- Customer Admin Shopping Cart View Missing (fixed in [magento/magento2#20918](https://github.com/magento/magento2/pull/20918)) - * [#8258](https://github.com/magento/magento2/issues/8258) -- M2.1.3 : Form validation with identical field names does not work as expected. (fixed in [magento/magento2#15383](https://github.com/magento/magento2/pull/15383)) - * [#13561](https://github.com/magento/magento2/issues/13561) -- Magento 2 and SSL connection to MySQL (fixed in [magento/magento2#18075](https://github.com/magento/magento2/pull/18075)) * [#22545](https://github.com/magento/magento2/issues/22545) -- Status downloadable product stays pending after succesfull payment (fixed in [magento/magento2#22658](https://github.com/magento/magento2/pull/22658)) * [#23383](https://github.com/magento/magento2/issues/23383) -- Products which are not assigned to any store are automatically being force-assigned a store ID after being saved (fixed in [magento/magento2#23500](https://github.com/magento/magento2/pull/23500)) * [#22950](https://github.com/magento/magento2/issues/22950) -- Spacing issue for Gift message section in my account (fixed in [magento/magento2#23226](https://github.com/magento/magento2/pull/23226)) @@ -181,7 +179,7 @@ * [#23916](https://github.com/magento/magento2/issues/23916) -- Missing Validation at some Payment Method Settings (fixed in [magento/magento2#23917](https://github.com/magento/magento2/pull/23917)) * [#23932](https://github.com/magento/magento2/issues/23932) -- Decimal quantity is not displayed for wishlist items. (fixed in [magento/magento2#23933](https://github.com/magento/magento2/pull/23933)) * GitHub pull requests: - * [magento/magento2#20135](https://github.com/magento/magento2/pull/20135) -- issue fixed #20124 Sort By label is hidden by Shop By Menu on listing… (by @cedarvinda) + * [magento/magento2#20135](https://github.com/magento/magento2/pull/20135) -- issue fixed #20124 Sort By label is hidden by Shop By Menu on listing (by @cedarvinda) * [magento/magento2#22020](https://github.com/magento/magento2/pull/22020) -- Non existing file, when adding image to gallery with move option. Fix for #21978 (by @dudzio12) * [magento/magento2#22260](https://github.com/magento/magento2/pull/22260) -- Disabling "Display on Product Details Page" the button is shown anyway. (by @Nazar65) * [magento/magento2#22287](https://github.com/magento/magento2/pull/22287) -- #222249 configurable product images wrong sorting fix (by @Wirson) @@ -295,7 +293,6 @@ * [magento/magento2#21876](https://github.com/magento/magento2/pull/21876) -- $product->getUrlInStore() does not allow to override the scope in backend context (by @Nazar65) * [magento/magento2#23007](https://github.com/magento/magento2/pull/23007) -- [Fixed] Reset feature does not clear the date (by @niravkrish) * [magento/magento2#23118](https://github.com/magento/magento2/pull/23118) -- #23053 : sendfriend verifies product visibility instead of status (by @Wirson) - * [magento/magento2#18748](https://github.com/magento/magento2/pull/18748) -- Add a module manager to the Magento Framework API (by @navarr) * [magento/magento2#22637](https://github.com/magento/magento2/pull/22637) -- Fixed #22484 Customer address States are duplicated in backend (by @shikhamis11) * [magento/magento2#23140](https://github.com/magento/magento2/pull/23140) -- magento/magento2#23138: Magento_Theme. Incorrect configuration file location (by @atwixfirster) * [magento/magento2#23179](https://github.com/magento/magento2/pull/23179) -- Fix for translation function (by @kkdg) @@ -361,8 +358,6 @@ * [magento/magento2#23529](https://github.com/magento/magento2/pull/23529) -- Feature/9798 updating configurable product options based on produc id and sku (by @lpouwelse) * [magento/magento2#20918](https://github.com/magento/magento2/pull/20918) -- Enabled 'Shopping Cart' tab for customer edit interface in admin (by @rav-redchamps) * [magento/magento2#22624](https://github.com/magento/magento2/pull/22624) -- Resolve Typo (by @prashantsharmacedcoss) - * [magento/magento2#15383](https://github.com/magento/magento2/pull/15383) -- issue fixed #8258 - assign indices for all array inputs so that validation works properly (by @jayankaghosh) - * [magento/magento2#18075](https://github.com/magento/magento2/pull/18075) -- Feature/issue 13561 2.3 (by @bnymn) * [magento/magento2#22658](https://github.com/magento/magento2/pull/22658) -- Fixed #22545 Status downloadable product stays pending after succesfu... (by @shikhamis11) * [magento/magento2#23500](https://github.com/magento/magento2/pull/23500) -- Fixed issue #23383 (by @manishgoswamij) * [magento/magento2#23226](https://github.com/magento/magento2/pull/23226) -- Spacing issue for Gift message section in my account (by @amjadm61) From 2134abaadbba832545b3008863e167a7c3e31ca9 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 12:00:36 -0500 Subject: [PATCH 0509/2437] MC-18685: Remove custom layout updates from admin --- .../Controller/Adminhtml/Category/Save.php | 9 +- .../Backend/AbstractLayoutUpdate.php | 122 ++++++++++++++++++ .../Attribute/Backend/Customlayoutupdate.php | 73 +++++------ .../Attribute/Backend/LayoutUpdate.php | 73 +---------- .../Attribute/Backend/LayoutUpdate.php | 73 +---------- .../Product/Attribute/LayoutUpdateManager.php | 2 - .../Controller/Adminhtml/ProductTest.php | 9 +- 7 files changed, 174 insertions(+), 187 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 9812dda73fd3e..2dc193d399ec0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -175,6 +175,10 @@ public function execute() } try { + $this->_eventManager->dispatch( + 'catalog_category_prepare_save', + ['category' => $category, 'request' => $this->getRequest()] + ); /** * Check "Use Default Value" checkboxes values */ @@ -186,11 +190,6 @@ public function execute() } } - $this->_eventManager->dispatch( - 'catalog_category_prepare_save', - ['category' => $category, 'request' => $this->getRequest()] - ); - /** * Proceed with $_POST['use_config'] * set into category model for processing through validation diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php new file mode 100644 index 0000000000000..ece3c8e499e4d --- /dev/null +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Backend; + +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Framework\Exception\LocalizedException; +use Magento\Catalog\Model\AbstractModel; + +/** + * Custom layout file attribute. + */ +abstract class AbstractLayoutUpdate extends AbstractBackend +{ + public const VALUE_USE_UPDATE_XML = '__existing__'; + + /** + * Extract attribute value. + * + * @param AbstractModel $model + * @throws LocalizedException + * @return mixed + */ + private function extractAttributeValue(AbstractModel $model) + { + $code = $this->getAttribute()->getAttributeCode(); + $data = $model->getData(); + //Custom attributes must not be initialized if they have not already been or it will break the saving process. + if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) + && array_key_exists($code, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + return $model->getCustomAttribute($code)->getValue(); + } elseif (array_key_exists($code, $data)) { + return $data[$code]; + } + + return null; + } + + /** + * Compose list of available files (layout handles) for given entity. + * + * @param AbstractModel $forModel + * @return string[] + */ + abstract protected function listAvailableValues(AbstractModel $forModel): array; + + /** + * Extracts prepare attribute value to be saved. + * + * @throws LocalizedException + * @param AbstractModel $model + * @return string|null + */ + private function prepareValue(AbstractModel $model): ?string + { + $value = $this->extractAttributeValue($model); + if ($value + && $value !== self::VALUE_USE_UPDATE_XML + && !in_array($value, $this->listAvailableValues($model), true) + ) { + throw new LocalizedException(__('Selected layout update is not available')); + } + if ($value === self::VALUE_USE_UPDATE_XML) { + $value = null; + } + if (!$value) { + $value = null; + } + + return $value; + } + + /** + * Set value for the object. + * + * @param string|null $value + * @param AbstractModel $forObject + * @return void + */ + private function setAttributeValue(?string $value, AbstractModel $forObject): void + { + $attrCode = $this->getAttribute()->getAttributeCode(); + $data = $forObject->getData(); + if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) + && array_key_exists($attrCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + $forObject->setCustomAttribute($attrCode, $value); + } + $forObject->setData($attrCode, $value); + } + + /** + * @inheritDoc + * + * @param AbstractModel $object + */ + public function validate($object) + { + $valid = parent::validate($object); + if ($valid) { + $this->prepareValue($object); + } + + return $valid; + } + + /** + * @inheritDoc + * @param AbstractModel $object + * @throws LocalizedException + */ + public function beforeSave($object) + { + $this->setAttributeValue($this->prepareValue($object), $object); + + return $this; + } +} diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index 881fb3a57d3e7..d5190d5df5ed3 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -8,9 +8,7 @@ use Magento\Catalog\Model\AbstractModel; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; -use Magento\Eav\Model\Entity\Attribute\Exception; -use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate as CategoryLayoutUpdate; -use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as ProductLayoutUpdate; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; /** * Layout update attribute backend @@ -20,18 +18,15 @@ * @SuppressWarnings(PHPMD.LongVariable) * @since 100.0.2 */ -class Customlayoutupdate extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend +class Customlayoutupdate extends AbstractBackend { /** - * Layout update validator factory - * * @var ValidatorFactory + * @deprecated Is not used anymore. */ protected $_layoutUpdateValidatorFactory; /** - * Construct the custom layout update class - * * @param ValidatorFactory $layoutUpdateValidatorFactory */ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) @@ -40,31 +35,19 @@ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) } /** - * Validate the custom layout update - * - * @param \Magento\Framework\DataObject $object - * @return bool - * @throws Exception + * @inheritDoc + * @param AbstractModel $object */ public function validate($object) { - $attributeName = $this->getAttribute()->getName(); - $xml = trim($object->getData($attributeName)); - - if (!$this->getAttribute()->getIsRequired() && empty($xml)) { - return true; + if (parent::validate($object)) { + $attrCode = $this->getAttribute()->getAttributeCode(); + $value = $this->extractValue($object); + if ($value && $object->getOrigData($attrCode) !== $value) { + throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); + } } - /** @var $validator \Magento\Framework\View\Model\Layout\Update\Validator */ - $validator = $this->_layoutUpdateValidatorFactory->create(); - if (!$validator->isValid($xml)) { - $messages = $validator->getMessages(); - //Add first message to exception - $message = array_shift($messages); - $eavExc = new Exception(__($message)); - $eavExc->setAttributeCode($attributeName); - throw $eavExc; - } return true; } @@ -78,23 +61,34 @@ public function validate($object) private function extractValue(AbstractModel $object, ?string $attributeCode = null) { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); - $attribute = $object->getCustomAttribute($attributeCode); + $data = $object->getData(); + //Custom attributes must not be initialized if they have not already been or it will break the saving process. + if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) + && array_key_exists($attributeCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + return $object->getCustomAttribute($attributeCode)->getValue(); + } elseif (array_key_exists($attributeCode, $data)) { + return $data[$attributeCode]; + } - return $object->getData($attributeCode) ?? ($attribute ? $attribute->getValue() : null); + return null; } /** * Put an attribute value. * * @param AbstractModel $object - * @param mixed $value + * @param string|null $value * @param string|null $attributeCode * @return void */ - private function putValue(AbstractModel $object, $value, ?string $attributeCode = null): void + private function putValue(AbstractModel $object, ?string $value, ?string $attributeCode = null): void { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); - $object->setCustomAttribute($attributeCode, $value); + $data = $object->getData(); + if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) + && array_key_exists($attributeCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + $object->setCustomAttribute($attributeCode, $value); + } $object->setData($attributeCode, $value); } @@ -105,18 +99,11 @@ private function putValue(AbstractModel $object, $value, ?string $attributeCode */ public function beforeSave($object) { - $attributeName = $this->getAttribute()->getName(); - $value = $this->extractValue($object); - //New values are not accepted - if ($value && $object->getOrigData($attributeName) !== $value) { - throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); - } + //Validate first, validation might have been skipped. + $this->validate($object); //If custom file was selected we need to remove this attribute $file = $this->extractValue($object, 'custom_layout_update_file'); - if ($file - && $file !== CategoryLayoutUpdate::VALUE_USE_UPDATE_XML - && $file !== ProductLayoutUpdate::VALUE_USE_UPDATE_XML - ) { + if ($file && $file !== AbstractLayoutUpdate::VALUE_USE_UPDATE_XML) { $this->putValue($object, null); } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index ee10b170f0d84..cab8dc0961bb1 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -8,17 +8,16 @@ namespace Magento\Catalog\Model\Category\Attribute\Backend; -use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; -use Magento\Framework\Exception\LocalizedException; /** * Allows to select a layout file to merge when rendering the category's page. */ -class LayoutUpdate extends AbstractBackend +class LayoutUpdate extends AbstractLayoutUpdate { - public const VALUE_USE_UPDATE_XML = '__existing__'; /** * @var LayoutUpdateManager @@ -33,72 +32,12 @@ public function __construct(LayoutUpdateManager $manager) $this->manager = $manager; } - /** - * Extracts the attributes value from given entity. - * - * @throws LocalizedException - * @param Category $category - * @return string|null - */ - private function extractValue(Category $category): ?string - { - $attrCode = $this->getAttribute()->getAttributeCode(); - $attrValue = $category->getCustomAttribute($attrCode); - $value = $category->getData($attrCode) ?? ($attrValue ? $attrValue->getValue() : null); - if ($value - && $value !== self::VALUE_USE_UPDATE_XML - && !in_array($value, $this->manager->fetchAvailableFiles($category), true) - ) { - throw new LocalizedException(__('Selected layout update is not available')); - } - if (!$value) { - $value = null; - } - - return $value; - } - - /** - * Set value for the object. - * - * @param string|null $value - * @param Category $object - */ - private function setValue(?string $value, Category $object): void - { - $attrCode = $this->getAttribute()->getAttributeCode(); - $object->setCustomAttribute($attrCode, $value); - $object->setData($attrCode, $value); - } - /** * @inheritDoc - * - * @param Category $object + * @param AbstractModel|Category $forModel */ - public function validate($object) + protected function listAvailableValues(AbstractModel $forModel): array { - $valid = parent::validate($object); - if ($valid) { - $this->extractValue($object); - } - - return $valid; - } - - /** - * @inheritDoc - * @param Category $object - * @throws LocalizedException - */ - public function beforeSave($object) - { - $value = $this->extractValue($object); - if ($value === self::VALUE_USE_UPDATE_XML) { - $value = null; - } - $this->setValue($value, $object); - - return $this; + return $this->manager->fetchAvailableFiles($forModel); } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index e71e27cac6dd0..e3050baee2f5b 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -8,17 +8,16 @@ namespace Magento\Catalog\Model\Product\Attribute\Backend; -use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; -use Magento\Framework\Exception\LocalizedException; /** * Allows to select a layout file to merge when rendering the product's page. */ -class LayoutUpdate extends AbstractBackend +class LayoutUpdate extends AbstractLayoutUpdate { - public const VALUE_USE_UPDATE_XML = '__existing__'; /** * @var LayoutUpdateManager @@ -33,72 +32,12 @@ public function __construct(LayoutUpdateManager $manager) $this->manager = $manager; } - /** - * Extracts the attributes value from given entity. - * - * @throws LocalizedException - * @param Product $product - * @return string|null - */ - private function extractValue(Product $product): ?string - { - $attrCode = $this->getAttribute()->getAttributeCode(); - $attrValue = $product->getCustomAttribute($attrCode); - $value = $product->getData($attrCode) ?? ($attrValue ? $attrValue->getValue() : null); - if ($value - && $value !== self::VALUE_USE_UPDATE_XML - && !in_array($value, $this->manager->fetchAvailableFiles($product), true) - ) { - throw new LocalizedException(__('Selected layout update is not available')); - } - if (!$value) { - $value = null; - } - - return $value; - } - - /** - * Set value for the object. - * - * @param string|null $value - * @param Product $object - */ - private function setValue(?string $value, Product $object): void - { - $attrCode = $this->getAttribute()->getAttributeCode(); - $object->setCustomAttribute($attrCode, $value); - $object->setData($attrCode, $value); - } - /** * @inheritDoc - * - * @param Product $object + * @param AbstractModel|Product $forModel */ - public function validate($object) + protected function listAvailableValues(AbstractModel $forModel): array { - $valid = parent::validate($object); - if ($valid) { - $this->extractValue($object); - } - - return $valid; - } - - /** - * @inheritDoc - * @param Product $object - * @throws LocalizedException - */ - public function beforeSave($object) - { - $value = $this->extractValue($object); - if ($value === self::VALUE_USE_UPDATE_XML) { - $value = null; - } - $this->setValue($value, $object); - - return $this; + return $this->manager->fetchAvailableFiles($forModel); } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php index 1e0acdc989cdb..12f7118924268 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -8,7 +8,6 @@ namespace Magento\Catalog\Model\Product\Attribute; -use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Framework\App\Area; use Magento\Framework\DataObject; @@ -16,7 +15,6 @@ use Magento\Framework\View\DesignInterface; use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; -use Magento\Framework\View\Result\Page as PageLayout; /** * Manage available layout updates for products. diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 67b6ec4e7d70f..c5537c89b78d0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -449,8 +449,8 @@ public function testSaveDesign(): void /** * Test custom update files functionality. * - * @magentoDbIsolation enabled * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation disabled * @throws \Throwable * @return void */ @@ -463,8 +463,11 @@ public function testSaveCustomLayout(): void /** @var ProductLayoutUpdateManager $layoutManager */ $layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); $layoutManager->setFakeFiles((int)$product->getId(), [$file]); + $productData = $product->getData(); + unset($productData['options']); + unset($productData[$product->getIdFieldName()]); $requestData = [ - 'product' => $product->getData() + 'product' => $productData ]; $uri = 'backend/catalog/product/save'; @@ -476,7 +479,7 @@ public function testSaveCustomLayout(): void $this->getRequest()->setParam('id', $product->getId()); $this->dispatch($uri); $this->assertSessionMessages( - self::equalTo(['tst']), + self::equalTo(['Selected layout update is not available']), MessageInterface::TYPE_ERROR ); From cb08621a0c4330adf90d552f3c30cacaf2f1b717 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 13:47:56 -0500 Subject: [PATCH 0510/2437] MC-18685: Remove custom layout updates from admin --- .../Catalog/Model/Category/DataProvider.php | 5 ++ .../Model/Category/DataProviderTest.php | 78 ++++++++++++------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index bd377f4ab302e..16239f71355fa 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -67,6 +67,8 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider 'size' => 'multiline_count', ]; + private $boolMetaProperties = ['visible', 'required']; + /** * Form element mapping * @@ -358,6 +360,9 @@ public function getAttributesMeta(Type $entityType) foreach ($this->metaProperties as $metaName => $origName) { $value = $attribute->getDataUsingMethod($origName); $meta[$code][$metaName] = $value; + if (in_array($metaName, $this->boolMetaProperties, true)) { + $meta[$code][$metaName] = (bool)$meta[$code][$metaName]; + } if ('frontend_input' === $origName) { $meta[$code]['formElement'] = isset($this->formElement[$value]) ? $this->formElement[$value] diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index e91aa01eb9046..5934f32dcd746 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -90,6 +90,33 @@ public function testGetMetaRequiredAttributes() } } + /** + * Check that deprecated custom layout attribute is hidden. + * + * @return void + */ + public function testOldCustomLayoutInvisible(): void + { + //Testing a category without layout xml + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load($id = 2); + $this->registry->register('category', $category); + + $meta = $this->dataProvider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update']['arguments']); + $this->assertArrayHasKey( + 'config', + $meta['design']['children']['custom_layout_update']['arguments']['data'] + ); + $config = $meta['design']['children']['custom_layout_update']['arguments']['data']['config']; + $this->assertTrue($config['visible'] === false); + } + /** * Check that custom layout update file attribute is processed correctly. * @@ -117,20 +144,13 @@ public function testCustomLayoutFileAttribute(): void } /** - * Check that proper options are returned for a category. + * Extract custom layout update file attribute's options from metadata. * - * @return void + * @param array $meta + * @return array */ - public function testCustomLayoutMeta(): void + private function extractCustomLayoutOptions(array $meta): array { - //Testing a category without layout xml - /** @var Category $category */ - $category = $this->categoryFactory->create(); - $category->load($id = 2); - $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test1', 'test2']); - $this->registry->register('category', $category); - - $meta = $this->dataProvider->getMeta(); $this->assertArrayHasKey('design', $meta); $this->assertArrayHasKey('children', $meta['design']); $this->assertArrayHasKey('custom_layout_update_file', $meta['design']['children']); @@ -144,12 +164,31 @@ public function testCustomLayoutMeta(): void 'options', $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config'] ); + + return $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; + } + + /** + * Check that proper options are returned for a category. + * + * @return void + */ + public function testCustomLayoutMeta(): void + { + //Testing a category without layout xml + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load($id = 2); + $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test1', 'test2']); + $this->registry->register('category', $category); + + $meta = $this->dataProvider->getMeta(); + $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ ['label' => 'No update', 'value' => '', '__disableTmpl' => true], ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] ]; - $list = $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; sort($expectedList); sort($list); $this->assertEquals($expectedList, $list); @@ -159,19 +198,6 @@ public function testCustomLayoutMeta(): void $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test3']); $meta = $this->dataProvider->getMeta(); - $this->assertArrayHasKey('design', $meta); - $this->assertArrayHasKey('children', $meta['design']); - $this->assertArrayHasKey('custom_layout_update_file', $meta['design']['children']); - $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_file']); - $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_file']['arguments']); - $this->assertArrayHasKey( - 'config', - $meta['design']['children']['custom_layout_update_file']['arguments']['data'] - ); - $this->assertArrayHasKey( - 'options', - $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config'] - ); $expectedList = [ ['label' => 'No update', 'value' => '', '__disableTmpl' => true], [ @@ -181,7 +207,7 @@ public function testCustomLayoutMeta(): void ], ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], ]; - $list = $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; + $list = $this->extractCustomLayoutOptions($meta); sort($expectedList); sort($list); $this->assertEquals($expectedList, $list); From 422bc7f5b4f0b617e7a88650927d150bc45c3ebb Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 14:09:11 -0500 Subject: [PATCH 0511/2437] MC-18685: Remove custom layout updates from admin --- .../Backend/AbstractLayoutUpdate.php | 15 +- .../Attribute/Backend/Customlayoutupdate.php | 22 +-- .../Backend/CustomlayoutupdateTest.php | 137 ------------------ .../Backend/CustomlayoutupdateTest.php | 25 +++- 4 files changed, 28 insertions(+), 171 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php index ece3c8e499e4d..6aedd509af8e0 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php @@ -23,22 +23,13 @@ abstract class AbstractLayoutUpdate extends AbstractBackend * Extract attribute value. * * @param AbstractModel $model - * @throws LocalizedException * @return mixed */ private function extractAttributeValue(AbstractModel $model) { $code = $this->getAttribute()->getAttributeCode(); - $data = $model->getData(); - //Custom attributes must not be initialized if they have not already been or it will break the saving process. - if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) - && array_key_exists($code, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { - return $model->getCustomAttribute($code)->getValue(); - } elseif (array_key_exists($code, $data)) { - return $data[$code]; - } - return null; + return $model->getData($code); } /** @@ -85,9 +76,7 @@ private function prepareValue(AbstractModel $model): ?string private function setAttributeValue(?string $value, AbstractModel $forObject): void { $attrCode = $this->getAttribute()->getAttributeCode(); - $data = $forObject->getData(); - if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) - && array_key_exists($attrCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + if ($forObject->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { $forObject->setCustomAttribute($attrCode, $value); } $forObject->setData($attrCode, $value); diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index d5190d5df5ed3..ceb6f660aed6f 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -42,9 +42,11 @@ public function validate($object) { if (parent::validate($object)) { $attrCode = $this->getAttribute()->getAttributeCode(); - $value = $this->extractValue($object); - if ($value && $object->getOrigData($attrCode) !== $value) { - throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); + if ($object instanceof AbstractModel) { + $value = $this->extractValue($object); + if ($value && $object->getOrigData($attrCode) !== $value) { + throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); + } } } @@ -61,16 +63,8 @@ public function validate($object) private function extractValue(AbstractModel $object, ?string $attributeCode = null) { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); - $data = $object->getData(); - //Custom attributes must not be initialized if they have not already been or it will break the saving process. - if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) - && array_key_exists($attributeCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { - return $object->getCustomAttribute($attributeCode)->getValue(); - } elseif (array_key_exists($attributeCode, $data)) { - return $data[$attributeCode]; - } - return null; + return $object->getData($attributeCode); } /** @@ -84,9 +78,7 @@ private function extractValue(AbstractModel $object, ?string $attributeCode = nu private function putValue(AbstractModel $object, ?string $value, ?string $attributeCode = null): void { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); - $data = $object->getData(); - if (array_key_exists(AbstractModel::CUSTOM_ATTRIBUTES, $data) - && array_key_exists($attributeCode, $data[AbstractModel::CUSTOM_ATTRIBUTES])) { + if ($object->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { $object->setCustomAttribute($attributeCode, $value); } $object->setData($attributeCode, $value); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php deleted file mode 100644 index 01fad60609c29..0000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ /dev/null @@ -1,137 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend; - -use Magento\Framework\DataObject; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class CustomlayoutupdateTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private $attributeName = 'private'; - - /** - * @var \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate - */ - private $model; - - /** - * @expectedException \Magento\Eav\Model\Entity\Attribute\Exception - */ - public function testValidateException() - { - $object = new DataObject(); - $object->setData($this->attributeName, 'exception'); - $this->model->validate($object); - } - - /** - * @param string - * @dataProvider validateProvider - */ - public function testValidate($data) - { - $object = new DataObject(); - $object->setData($this->attributeName, $data); - - $this->assertTrue($this->model->validate($object)); - $this->assertTrue($this->model->validate($object)); - } - - /** - * @return array - */ - public function validateProvider() - { - return [[''], ['xml']]; - } - - protected function setUp() - { - $helper = new ObjectManager($this); - $this->model = $helper->getObject( - \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate::class, - [ - 'layoutUpdateValidatorFactory' => $this->getMockedLayoutUpdateValidatorFactory() - ] - ); - $this->model->setAttribute($this->getMockedAttribute()); - } - - /** - * @return \Magento\Framework\View\Model\Layout\Update\ValidatorFactory - */ - private function getMockedLayoutUpdateValidatorFactory() - { - $mockBuilder = $this->getMockBuilder(\Magento\Framework\View\Model\Layout\Update\ValidatorFactory::class); - $mockBuilder->disableOriginalConstructor(); - $mockBuilder->setMethods(['create']); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('create') - ->will($this->returnValue($this->getMockedValidator())); - - return $mock; - } - - /** - * @return \Magento\Framework\View\Model\Layout\Update\Validator - */ - private function getMockedValidator() - { - $mockBuilder = $this->getMockBuilder(\Magento\Framework\View\Model\Layout\Update\Validator::class); - $mockBuilder->disableOriginalConstructor(); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('isValid') - ->will( - /** - * @param string $xml - * $return bool - */ - $this->returnCallback( - function ($xml) { - if ($xml == 'exception') { - return false; - } else { - return true; - } - } - ) - ); - - $mock->expects($this->any()) - ->method('getMessages') - ->will($this->returnValue(['error'])); - - return $mock; - } - - /** - * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute - */ - private function getMockedAttribute() - { - $mockBuilder = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class); - $mockBuilder->disableOriginalConstructor(); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('getName') - ->will($this->returnValue($this->attributeName)); - - $mock->expects($this->any()) - ->method('getIsRequired') - ->will($this->returnValue(false)); - - return $mock; - } -} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index dbc14c7d25dae..2efeae50dc3c4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -35,14 +35,24 @@ class CustomlayoutupdateTest extends TestCase */ private $category; + /** + * Recreate the category model. + * + * @return void + */ + private function recreateCategory(): void + { + $this->category = $this->categoryFactory->create(); + $this->category->load(2); + } + /** * @inheritDoc */ protected function setUp() { $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryFactory::class); - $this->category = $this->categoryFactory->create(); - $this->category->load(2); + $this->recreateCategory(); $this->attribute = $this->category->getAttributes()['custom_layout_update']->getBackend(); } @@ -100,19 +110,22 @@ public function testDependsOnNewUpdate(): void $this->attribute->beforeSave($this->category); $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + $this->assertEmpty($this->category->getData('custom_layout_update')); + $this->assertEquals('new', $this->category->getData('custom_layout_update_file')); //Existing update chosen - $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); $this->category->setOrigData('custom_layout_update', 'test'); - $this->category->setCustomAttribute( + $this->category->setData( 'custom_layout_update_file', \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML ); $this->attribute->beforeSave($this->category); - $this->assertEquals('test', $this->category->getCustomAttribute('custom_layout_update')->getValue()); + $this->assertEquals('test', $this->category->getData('custom_layout_update')); /** @var AbstractBackend $fileAttribute */ $fileAttribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); $fileAttribute->beforeSave($this->category); - $this->assertEquals(null, $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + $this->assertEquals(null, $this->category->getData('custom_layout_update_file')); } } From b12bd5a34a79dbd621a4a1a0288fe9d0ddf20f8b Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 15:13:07 -0500 Subject: [PATCH 0512/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Backend/Customlayoutupdate.php | 42 +++++++++++-------- .../Attribute/Backend/LayoutUpdate.php | 1 + .../Attribute/Backend/LayoutUpdate.php | 1 + .../Catalog/Api/CategoryRepositoryTest.php | 4 +- .../Category/CreateCategoryEntityTest.xml | 4 +- .../Backend/CustomlayoutupdateTest.php | 3 +- .../Model/Category/DataProviderTest.php | 3 +- .../Form/Modifier/LayoutUpdateTest.php | 1 + 8 files changed, 37 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index ceb6f660aed6f..d3cdb7c545cbc 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -34,8 +34,27 @@ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) $this->_layoutUpdateValidatorFactory = $layoutUpdateValidatorFactory; } + /** + * Extract an attribute value. + * + * @param AbstractModel $object + * @param string|null $attributeCode + * @return mixed + */ + private function extractValue(AbstractModel $object, ?string $attributeCode = null) + { + $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); + $value = $object->getData($attributeCode); + if (!$value) { + $value = null; + } + + return $value; + } + /** * @inheritDoc + * * @param AbstractModel $object */ public function validate($object) @@ -53,31 +72,16 @@ public function validate($object) return true; } - /** - * Extract an attribute value. - * - * @param AbstractModel $object - * @param string|null $attributeCode - * @return mixed - */ - private function extractValue(AbstractModel $object, ?string $attributeCode = null) - { - $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); - - return $object->getData($attributeCode); - } - /** * Put an attribute value. * * @param AbstractModel $object * @param string|null $value - * @param string|null $attributeCode * @return void */ - private function putValue(AbstractModel $object, ?string $value, ?string $attributeCode = null): void + private function putValue(AbstractModel $object, ?string $value): void { - $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); + $attributeCode = $this->getAttribute()->getName(); if ($object->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { $object->setCustomAttribute($attributeCode, $value); } @@ -86,6 +90,7 @@ private function putValue(AbstractModel $object, ?string $value, ?string $attrib /** * @inheritDoc + * * @param AbstractModel $object * @throws LocalizedException */ @@ -93,10 +98,13 @@ public function beforeSave($object) { //Validate first, validation might have been skipped. $this->validate($object); + $value = $this->extractValue($object); //If custom file was selected we need to remove this attribute $file = $this->extractValue($object, 'custom_layout_update_file'); if ($file && $file !== AbstractLayoutUpdate::VALUE_USE_UPDATE_XML) { $this->putValue($object, null); + } else { + $this->putValue($object, $value); } return parent::beforeSave($object); diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php index cab8dc0961bb1..215fe1c19bd8d 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -34,6 +34,7 @@ public function __construct(LayoutUpdateManager $manager) /** * @inheritDoc + * * @param AbstractModel|Category $forModel */ protected function listAvailableValues(AbstractModel $forModel): array diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php index e3050baee2f5b..fa5a218824eea 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -34,6 +34,7 @@ public function __construct(LayoutUpdateManager $manager) /** * @inheritDoc + * * @param AbstractModel|Product $forModel */ protected function listAvailableValues(AbstractModel $forModel): array diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 510a4f1594fff..e0c45967b214e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -316,7 +316,9 @@ protected function updateCategory($id, $data, ?string $token = null) 'operation' => self::SERVICE_NAME . 'Save', ], ]; - $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { $data['id'] = $id; diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 69093b8adb8db..54f0fd8e3e79b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -37,7 +37,7 @@ <data name="category/data/meta_keywords" xsi:type="string">custom meta keywords %isolation%</data> <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="catalog.leftnav" remove="true"/></data> + <data name="category/data/layout_update_xml" xsi:type="string" /> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> @@ -80,7 +80,7 @@ <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="content.aside" remove="true"/></data> + <data name="category/data/layout_update_xml" xsi:type="string" /> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index 2efeae50dc3c4..7447950ea2ab6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -90,9 +90,10 @@ public function testImmutable(): void $this->assertTrue($caughtException); //Removing a value - $this->category->setCustomAttribute('custom_layout_update', null); + $this->category->setCustomAttribute('custom_layout_update', ''); $this->category->setOrigData('custom_layout_update', 'test'); $this->attribute->beforeSave($this->category); + $this->assertNull($this->category->getCustomAttribute('custom_layout_update')->getValue()); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index 5934f32dcd746..93cf11477ed56 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -127,7 +127,8 @@ public function testCustomLayoutFileAttribute(): void //File has value /** @var Category $category */ $category = $this->categoryFactory->create(); - $category->load($id = 2); + $id = 2; + $category->load($id); $category->setData('custom_layout_update', null); $category->setData('custom_layout_update_file', $file = 'test-file'); $this->registry->register('category', $category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php index 61e68561d9ee4..6ccae88d38672 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -24,6 +24,7 @@ * @magentoDbIsolation enabled * @magentoAppIsolation enabled * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LayoutUpdateTest extends TestCase { From fe50575bcebec1e4afa0177b69ac8b122c62bd23 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 16:56:13 -0500 Subject: [PATCH 0513/2437] MC-18685: Remove custom layout updates from admin --- .../Category/CreateCategoryEntityTest.xml | 2 - .../Model/Category/DataProviderTest.php | 4 +- .../Form/Modifier/LayoutUpdateTest.php | 56 +++++++++---------- .../Adminhtml/Category/Tab/AttributesTest.php | 4 +- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 54f0fd8e3e79b..5ea1b692e3eb9 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -37,7 +37,6 @@ <data name="category/data/meta_keywords" xsi:type="string">custom meta keywords %isolation%</data> <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string" /> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> @@ -80,7 +79,6 @@ <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string" /> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index 93cf11477ed56..49cba292f974a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -100,7 +100,7 @@ public function testOldCustomLayoutInvisible(): void //Testing a category without layout xml /** @var Category $category */ $category = $this->categoryFactory->create(); - $category->load($id = 2); + $category->load(2); $this->registry->register('category', $category); $meta = $this->dataProvider->getMeta(); @@ -179,7 +179,7 @@ public function testCustomLayoutMeta(): void //Testing a category without layout xml /** @var Category $category */ $category = $this->categoryFactory->create(); - $category->load($id = 2); + $category->load(2); $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test1', 'test2']); $this->registry->register('category', $category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php index 6ccae88d38672..4a928fb1386c0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -107,20 +107,13 @@ public function testModifyData(): void } /** - * Check that entity specific options are returned. + * Extract options meta. * - * @return void - * @throws \Throwable - * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @param array $meta + * @return array */ - public function testEntitySpecificData(): void + private function extractCustomLayoutOptions(array $meta): array { - //Testing a category without layout xml - $product = $this->repo->get('simple'); - $this->locator->method('getProduct')->willReturn($product); - $this->fakeFiles->setFakeFiles((int)$product->getId(), ['test1', 'test2']); - - $meta = $this->eavModifier->modifyMeta([]); $this->assertArrayHasKey('design', $meta); $this->assertArrayHasKey('children', $meta['design']); $this->assertArrayHasKey('container_custom_layout_update_file', $meta['design']['children']); @@ -135,12 +128,31 @@ public function testEntitySpecificData(): void $this->assertArrayHasKey('data', $fieldMeta['arguments']); $this->assertArrayHasKey('config', $fieldMeta['arguments']['data']); $this->assertArrayHasKey('options', $fieldMeta['arguments']['data']['config']); + + return $fieldMeta['arguments']['data']['config']['options']; + } + + /** + * Check that entity specific options are returned. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testEntitySpecificData(): void + { + //Testing a category without layout xml + $product = $this->repo->get('simple'); + $this->locator->method('getProduct')->willReturn($product); + $this->fakeFiles->setFakeFiles((int)$product->getId(), ['testOne', 'test_two']); + + $meta = $this->eavModifier->modifyMeta([]); + $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ ['label' => 'No update', 'value' => '', '__disableTmpl' => true], - ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], - ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] + ['label' => 'testOne', 'value' => 'testOne', '__disableTmpl' => true], + ['label' => 'test_two', 'value' => 'test_two', '__disableTmpl' => true] ]; - $list = $fieldMeta['arguments']['data']['config']['options']; sort($expectedList); sort($list); $this->assertEquals($expectedList, $list); @@ -150,20 +162,7 @@ public function testEntitySpecificData(): void $this->fakeFiles->setFakeFiles((int)$product->getId(), ['test3']); $meta = $this->eavModifier->modifyMeta([]); - $this->assertArrayHasKey('design', $meta); - $this->assertArrayHasKey('children', $meta['design']); - $this->assertArrayHasKey('container_custom_layout_update_file', $meta['design']['children']); - $this->assertArrayHasKey('children', $meta['design']['children']['container_custom_layout_update_file']); - $this->assertArrayHasKey( - 'custom_layout_update_file', - $meta['design']['children']['container_custom_layout_update_file']['children'] - ); - $fieldMeta = $meta['design']['children']['container_custom_layout_update_file']['children']; - $fieldMeta = $fieldMeta['custom_layout_update_file']; - $this->assertArrayHasKey('arguments', $fieldMeta); - $this->assertArrayHasKey('data', $fieldMeta['arguments']); - $this->assertArrayHasKey('config', $fieldMeta['arguments']['data']); - $this->assertArrayHasKey('options', $fieldMeta['arguments']['data']['config']); + $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ ['label' => 'No update', 'value' => '', '__disableTmpl' => true], [ @@ -173,7 +172,6 @@ public function testEntitySpecificData(): void ], ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], ]; - $list = $fieldMeta['arguments']['data']['config']['options']; sort($expectedList); sort($list); $this->assertEquals($expectedList, $list); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php index 0cd3ad644197b..8ca84c4b066fe 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php @@ -51,8 +51,8 @@ public function testGetAttributesMeta() $urlKeyData = $meta['search_engine_optimization']['children']['url_key']['arguments']['data']['config']; $this->assertEquals('text', $urlKeyData['dataType']); $this->assertEquals('input', $urlKeyData['formElement']); - $this->assertEquals('1', $urlKeyData['visible']); - $this->assertEquals('0', $urlKeyData['required']); + $this->assertEquals(true, $urlKeyData['visible']); + $this->assertEquals(false, $urlKeyData['required']); $this->assertEquals('[STORE VIEW]', $urlKeyData['scopeLabel']); } } From 51d73783d066e55790de6eadc960aa66551bfde2 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 9 Sep 2019 16:56:40 -0500 Subject: [PATCH 0514/2437] MC-18685: Remove custom layout updates from admin --- .../tests/app/Magento/Catalog/Test/Fixture/Category.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml index fafd9842cc749..c5036555b6635 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml @@ -49,7 +49,6 @@ <field name="use_parent_category_settings" is_required="" group="design" /> <field name="theme" is_required="" group="design" /> <field name="layout" is_required="" group="design" /> - <field name="layout_update_xml" is_required="" group="design" /> <field name="apply_design_to_products" is_required="" group="design" /> <field name="schedule_update_from" is_required="" group="schedule_design_update" /> <field name="schedule_update_to" is_required="" group="schedule_design_update" /> From c1054d737f482731dfd483875bffd42f9c512042 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Tue, 10 Sep 2019 17:10:09 +0300 Subject: [PATCH 0515/2437] MC-10974: Return "Is Active" toggle to Catalog Price Rule Page - Change Is Active field - Add mftf test --- ...inCreateNewCatalogPriceRuleActionGroup.xml | 1 - .../AssertCatalogPriceRuleFormActionGroup.xml | 3 +- .../CatalogPriceRuleActionGroup.xml | 8 +++-- .../AdminCatalogPriceRuleStagingSection.xml | 1 + .../AdminNewCatalogPriceRuleSection.xml | 4 +++ .../AdminApplyCatalogRuleByCategoryTest.xml | 1 + ...dminCreateInactiveCatalogPriceRuleTest.xml | 1 + ...CatalogPriceRuleByProductAttributeTest.xml | 1 + .../StorefrontInactiveCatalogRuleTest.xml | 3 +- .../ui_component/catalog_rule_form.xml | 31 +++++++++---------- .../Section/CheckoutCartProductSection.xml | 1 + .../Mftf/Section/CheckoutPaymentSection.xml | 1 + .../Section/StorefrontMiniCartSection.xml | 2 ++ .../Promo/Catalog/Edit/PromoForm.xml | 2 +- .../AssertCatalogPriceRuleInGrid.php | 2 +- .../CreateCatalogPriceRuleEntityTest.xml | 10 +++--- .../UpdateCatalogPriceRuleEntityTest.xml | 4 +-- 17 files changed, 43 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateNewCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateNewCatalogPriceRuleActionGroup.xml index 00dcb68089b73..209095e0b0195 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateNewCatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateNewCatalogPriceRuleActionGroup.xml @@ -21,7 +21,6 @@ <waitForPageLoad stepKey="waitForPageToLoad"/> <fillField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> <fillField stepKey="fillDescription" selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}"/> - <selectOption selector="{{AdminNewCatalogPriceRule.status}}" userInput="{{catalogRule.is_active}}" stepKey="selectStatus"/> <selectOption stepKey="selectWebSite" selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}"/> <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{customerGroup}}" stepKey="selectCustomerGroup"/> <scrollTo selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="scrollToActionTab"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AssertCatalogPriceRuleFormActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AssertCatalogPriceRuleFormActionGroup.xml index 77fe0f50653c7..0a4b6366d11a8 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AssertCatalogPriceRuleFormActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AssertCatalogPriceRuleFormActionGroup.xml @@ -13,7 +13,7 @@ <description>Validates that the provided Catalog Rule, Status, Websites and Customer Group details are present and correct on a Admin Catalog Price Rule creation/edit page.</description> </annotations> <arguments> - <argument name="catalogRule" defaultValue="inactiveCatalogRule"/> + <argument name="catalogRule" defaultValue="inactiveCatalogRule" /> <argument name="status" type="string" defaultValue=""/> <argument name="websites" type="string"/> <argument name="customerGroup" type="string"/> @@ -21,7 +21,6 @@ <seeInField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> <seeInField stepKey="fillDescription" selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}"/> - <seeOptionIsSelected selector="{{AdminNewCatalogPriceRule.status}}" userInput="{{status}}" stepKey="selectStatus"/> <see stepKey="seeWebSite" selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{websites}}"/> <seeOptionIsSelected selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{customerGroup}}" stepKey="selectCustomerGroup"/> <scrollTo selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="scrollToActionTab"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index a7500858fc94e..dfe8268765b03 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -25,6 +25,7 @@ <!-- Fill the form according the attributes of the entity --> <fillField stepKey="fillName" selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}"/> <fillField stepKey="fillDescription" selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}"/> + <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> <selectOption stepKey="selectSite" selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}"/> <click stepKey="clickFromCalender" selector="{{AdminNewCatalogPriceRule.fromDateButton}}"/> <click stepKey="clickFromToday" selector="{{AdminNewCatalogPriceRule.todayDate}}"/> @@ -49,9 +50,10 @@ </arguments> <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> - <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName"/> - <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription"/> - <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="{{catalogRule.website_ids}}" stepKey="selectSite"/> + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName" /> + <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription" /> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="{{catalogRule.website_ids}}" stepKey="selectSite" /> <click stepKey="openActionDropdown" selector="{{AdminNewCatalogPriceRule.actionsTab}}"/> <fillField stepKey="fillDiscountValue" selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}"/> <scrollToTopOfPage stepKey="scrollToTop"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml index bab9842caaa42..7a92829e2371e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogPriceRuleStagingSection"> <element name="status" type="select" selector=".modal-component [data-index='is_active'] select"/> + <element name="isActive" type="select" selector=".modals-wrapper input[name='is_active']+label"/> </section> </sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index ba0493d8e995b..2d4ef3786d1c6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -20,8 +20,12 @@ <element name="ruleNameNew" type="input" selector="[name='staging[name]']"/> <element name="description" type="textarea" selector="[name='description']"/> <element name="status" type="select" selector="[name='is_active']"/> + <element name="isActive" type="select" selector="input[name='is_active']+label"/> <element name="websites" type="select" selector="[name='website_ids']"/> + <element name="active" type="checkbox" selector="//div[contains(@class, 'admin__actions-switch')]/input[@name='is_active']/../label"/> + <element name="activeIsEnabled" type="checkbox" selector="(//div[contains(@class, 'admin__actions-switch')])[1]/input[@value='1']"/> + <element name="activePosition" type="checkbox" selector="//fieldset[@class='admin__fieldset']//div[4]"/> <element name="websitesOptions" type="select" selector="[name='website_ids'] option"/> <element name="customerGroups" type="select" selector="[name='customer_group_ids']"/> <element name="customerGroupsOptions" type="select" selector="[name='customer_group_ids'] option"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index 741da96179b8c..ca534ec7f5375 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -52,6 +52,7 @@ <waitForPageLoad stepKey="waitForIndividualRulePage"/> <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{_defaultCatalogRule.name}}" stepKey="fillName"/> <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{_defaultCatalogRule.description}}" stepKey="fillDescription"/> + <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{_defaultCatalogRule.website_ids[0]}}" stepKey="selectSite"/> <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml index 5223b18df4e4a..83dff1ecdcab5 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateInactiveCatalogPriceRuleTest.xml @@ -58,6 +58,7 @@ <argument name="websites" value="Main Website"/> <argument name="customerGroup" value="General"/> </actionGroup> + <dontSeeCheckboxIsChecked selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}" stepKey="verifyInactiveRule"/> <!-- Search Catalog Rule in Grid --> <actionGroup ref="AdminSearchCatalogRuleInGridActionGroup" stepKey="searchCreatedCatalogRule"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml index b7a231df5045d..dd54c0767e8e1 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml @@ -195,6 +195,7 @@ Websites: Main Website Customer Groups: NOT LOGGED IN --> <fillField userInput="{{SimpleCatalogPriceRule.name}}" selector="{{AdminCartPriceRulesFormSection.ruleName}}" stepKey="fillRuleName"/> + <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> <selectOption userInput="{{SimpleCatalogPriceRule.websites}}" selector="{{AdminCartPriceRulesFormSection.websites}}" stepKey="selectWebsite"/> <selectOption userInput="{{SimpleCatalogPriceRule.customerGroups}}" selector="{{AdminCartPriceRulesFormSection.customerGroups}}" stepKey="selectCustomerGroups"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index b486654fe9acf..1a77dcbdfee6c 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -26,7 +26,8 @@ </createData> <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> - <selectOption selector="{{AdminNewCatalogPriceRule.status}}" userInput="Inactive" stepKey="setInactive"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="setInactive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="seeSuccess"/> </before> diff --git a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml index c114f6b1d77cd..2af8bb0770b20 100644 --- a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml +++ b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml @@ -95,33 +95,30 @@ <dataScope>description</dataScope> </settings> </field> - <field name="is_active" formElement="select"> + <field name="is_active" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">catalog_rule</item> + <item name="default" xsi:type="number">0</item> </item> </argument> <settings> - <dataType>number</dataType> - <label translate="true">Status</label> - <visible>true</visible> - <dataScope>is_active</dataScope> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + <dataType>boolean</dataType> + <label translate="true">Active</label> </settings> <formElements> - <select> + <checkbox> <settings> - <options> - <option name="0" xsi:type="array"> - <item name="value" xsi:type="number">1</item> - <item name="label" xsi:type="string" translate="true">Active</item> - </option> - <option name="1" xsi:type="array"> - <item name="value" xsi:type="number">0</item> - <item name="label" xsi:type="string" translate="true">Inactive</item> - </option> - </options> + <valueMap> + <map name="false" xsi:type="number">0</map> + <map name="true" xsi:type="number">1</map> + </valueMap> + <prefer>toggle</prefer> </settings> - </select> + </checkbox> </formElements> </field> <field name="website_ids" formElement="multiselect"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index 3ab3fa5857b78..be195f564145e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -18,6 +18,7 @@ <element name="ProductRegularPriceByName" type="text" selector="//div[descendant::*[contains(text(), '{{var1}}')]]//*[contains(@class, 'subtotal')]" parameterized="true"/> + <element name="productFirstPriceByName" type="text" selector="(//tbody//a[@title='{{prodName}}']/../following-sibling::*//span[@class='price'])[1]" parameterized="true"/> <element name="ProductImageByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr//img[contains(@class, 'product-image-photo') and @alt='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 903c21d7ec0ca..8e7b16c71057e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -51,6 +51,7 @@ <element name="orderSummaryTotalIncluding" type="text" selector="//tr[@class='grand totals incl']//span[@class='price']" /> <element name="orderSummaryTotalExcluding" type="text" selector="//tr[@class='grand totals excl']//span[@class='price']" /> <element name="shippingAndBillingAddressSame" type="input" selector="#billing-address-same-as-shipping-braintree_cc_vault"/> + <element name="myShippingAndBillingAddressSame" type="input" selector=".billing-address-same-as-shipping-block"/> <element name="addressAction" type="button" selector="//span[text()='{{action}}']" parameterized="true"/> <element name="addressBook" type="button" selector="//a[text()='Address Book']"/> <element name="noQuotes" type="text" selector=".no-quotes-block"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index 3e1de2b14ba62..443b4bf649ebf 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -13,7 +13,9 @@ <element name="productCount" type="text" selector="//header//div[contains(@class, 'minicart-wrapper')]//a[contains(@class, 'showcart')]//span[@class='counter-number']"/> <element name="productLinkByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details']//a[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="productPriceByName" type="text" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//span[@class='price']" parameterized="true"/> + <element name="productPriceByItsName" type="text" selector="//a[normalize-space()='{{prodName}}']/../following-sibling::*//*[@class='price']" parameterized="true"/> <element name="productImageByName" type="text" selector="//header//ol[@id='mini-cart']//span[@class='product-image-container']//img[@alt='{{var1}}']" parameterized="true"/> + <element name="productImageByItsName" type="text" selector="//img[@alt='{{prodName}}']" parameterized="true"/> <element name="productName" type="text" selector=".product-item-name"/> <element name="productOptionsDetailsByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//span[.='See Details']" parameterized="true"/> <element name="productOptionByNameAndAttribute" type="text" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//dt[@class='label' and .='{{var2}}']/following-sibling::dd[@class='values']//span" parameterized="true"/> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml index 0ff402daca07d..4f74b7ff554db 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Block/Adminhtml/Promo/Catalog/Edit/PromoForm.xml @@ -12,7 +12,7 @@ <strategy>css selector</strategy> <fields> <is_active> - <input>select</input> + <input>switcher</input> </is_active> <website_ids> <selector>[name='website_ids']</selector> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleInGrid.php b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleInGrid.php index 8ac13d407e433..695323990063a 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleInGrid.php +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Constraint/AssertCatalogPriceRuleInGrid.php @@ -19,7 +19,7 @@ class AssertCatalogPriceRuleInGrid extends AbstractConstraint * Fields used to filter rows in the grid. * @var array */ - protected $fieldsToFilter = ['name', 'is_active']; + protected $fieldsToFilter = ['name']; /** * Assert that data in grid on Catalog Price Rules page according to fixture diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogPriceRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogPriceRuleEntityTest.xml index 49bf36b0325ba..1f16e28c067d8 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogPriceRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/CreateCatalogPriceRuleEntityTest.xml @@ -10,7 +10,7 @@ <variation name="CatalogRule_Create_Active_AdminOnly"> <data name="catalogPriceRule/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> <data name="catalogPriceRule/data/description" xsi:type="string">Catalog Price Rule Description</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Active</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">Yes</data> <data name="catalogPriceRule/data/website_ids/option_0" xsi:type="string">Main Website</data> <data name="catalogPriceRule/data/customer_group_ids/option_0" xsi:type="string">Wholesale</data> <data name="catalogPriceRule/data/simple_action" xsi:type="string">Apply as percentage of original</data> @@ -24,7 +24,7 @@ <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="catalogPriceRule/data/name" xsi:type="string">CatalogPriceRule %isolation%</data> <data name="catalogPriceRule/data/description" xsi:type="string">Catalog Price Rule Description</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Inactive</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">No</data> <data name="catalogPriceRule/data/website_ids/option_0" xsi:type="string">Main Website</data> <data name="catalogPriceRule/data/customer_group_ids/option_0" xsi:type="string">General</data> <data name="catalogPriceRule/data/condition" xsi:type="string">-</data> @@ -39,7 +39,7 @@ <variation name="CatalogRule_Create_ForGuestUsers_AdjustPriceToPercentage"> <data name="product" xsi:type="string">MAGETWO-23036</data> <data name="catalogPriceRule/data/name" xsi:type="string">rule_name%isolation%</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Active</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">Yes</data> <data name="catalogPriceRule/data/website_ids/option_0" xsi:type="string">Main Website</data> <data name="catalogPriceRule/data/customer_group_ids/option_0" xsi:type="string">NOT LOGGED IN</data> <data name="conditionEntity" xsi:type="string">category</data> @@ -54,7 +54,7 @@ <data name="customer/dataset" xsi:type="string">customer_with_new_customer_group</data> <data name="product" xsi:type="string">simple_10_dollar</data> <data name="catalogPriceRule/data/name" xsi:type="string">rule_name%isolation%</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Active</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">Yes</data> <data name="catalogPriceRule/data/website_ids/option_0" xsi:type="string">Main Website</data> <data name="conditionEntity" xsi:type="string">category</data> <data name="catalogPriceRule/data/conditions" xsi:type="string">[Category|is|%category_id%]</data> @@ -68,7 +68,7 @@ <data name="tag" xsi:type="string">test_type:extended_acceptance_test</data> <data name="product" xsi:type="string">product_with_custom_color_attribute</data> <data name="catalogPriceRule/data/name" xsi:type="string">Catalog Price Rule %isolation%</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Active</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">Yes</data> <data name="catalogPriceRule/data/website_ids/option_0" xsi:type="string">Main Website</data> <data name="catalogPriceRule/data/customer_group_ids/option_0" xsi:type="string">NOT LOGGED IN</data> <data name="conditionEntity" xsi:type="string">attribute</data> diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml index 6c8e86b24ae60..e2916432c8eb7 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/TestCase/UpdateCatalogPriceRuleEntityTest.xml @@ -10,7 +10,7 @@ <variation name="CatalogRule_Update_Name_Status"> <data name="catalogPriceRuleOriginal/dataset" xsi:type="string">active_catalog_price_rule_with_conditions</data> <data name="catalogPriceRule/data/name" xsi:type="string">New Catalog Price Rule Name %isolation%</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Inactive</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">No</data> <data name="saveAction" xsi:type="string">save</data> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleSuccessSaveMessage" /> <constraint name="Magento\CatalogRule\Test\Constraint\AssertCatalogPriceRuleNoticeMessage" /> @@ -24,7 +24,7 @@ <data name="catalogPriceRuleOriginal/dataset" xsi:type="string">active_catalog_price_rule_with_conditions</data> <data name="catalogPriceRule/data/name" xsi:type="string">New Catalog Price Rule Name %isolation%</data> <data name="catalogPriceRule/data/description" xsi:type="string">New Catalog Price Rule Description %isolation%</data> - <data name="catalogPriceRule/data/is_active" xsi:type="string">Active</data> + <data name="catalogPriceRule/data/is_active" xsi:type="string">Yes</data> <data name="catalogPriceRule/data/conditions" xsi:type="string">[Category|is|%category_1%]</data> <data name="catalogPriceRule/data/simple_action" xsi:type="string">Apply as fixed amount</data> <data name="catalogPriceRule/data/discount_amount" xsi:type="string">35</data> From e9dfb728f62d86eab3dc8db29f6eefa172b130b3 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 10 Sep 2019 11:28:23 -0500 Subject: [PATCH 0516/2437] MC-18685: Remove custom layout updates from admin --- .../Magento/Catalog/Test/Handler/Category/Curl.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php index 5c54b366b7ab4..4cfef9a6b4f66 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/Category/Curl.php @@ -251,9 +251,15 @@ protected function getBlockId($landingName) $curl->write($url, [], CurlInterface::GET); $response = $curl->read(); $curl->close(); - preg_match('~\{"value":"(\d+)","label":"' . preg_quote($landingName) . '"\}~', $response, $matches); - $id = isset($matches[1]) ? (int)$matches[1] : null; + preg_match( + '/\{[^\{]*?\\"value\\":\\"(\d+)\\"[^\{]*?\\"label\\":\\"' .preg_quote($landingName) .'\\".*?\}/', + $response, + $matches + ); + if (empty($matches[1])) { + throw new \RuntimeException('Failed to extract CMS block ID from a category page'); + } - return $id; + return (int)$matches[1]; } } From 30e3978e00194dca4eba959103ccfd4b79cb823c Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 11 Sep 2019 11:13:39 +0400 Subject: [PATCH 0517/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- ...StrorefrontElasticsearchSearchInvalidValueTest.xml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml index 55e31e91e9016..1e3badb5f1ce6 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml @@ -40,8 +40,9 @@ <comment userInput="Set configs to default" stepKey="commentSetDefault"/> <magentoCLI command="config:set catalog/search/min_query_length 3" stepKey="setMinQueryLengthPreviousState"/> <magentoCLI command="config:set catalog/search/engine mysql" stepKey="resetSearchEnginePreviousState"/> - <!--Delete create data--> - <comment userInput="Delete create data" stepKey="commentDeletedData"/> + <!--Delete created data--> + <comment userInput="Delete created data" stepKey="commentDeleteData"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> <argument name="ProductAttributeCode" value="{{textProductAttribute.attribute_code}}"/> </actionGroup> @@ -106,5 +107,11 @@ <argument name="phrase" value="? anything at all ;"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForForthSearchTerm"/> + <!--Search for the product wit special symbols--> + <comment userInput="Search for the product wit special symbols" stepKey="commentSearchForProduct"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFifthSearchTerm"> + <argument name="phrase" value="-+~/\\<>\’“:*$#@()!,.?`=%&^"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForFifthSearchTerm"/> </test> </tests> From 320aac0d8715d7de3201488afaf2cfcaa268e1a4 Mon Sep 17 00:00:00 2001 From: VaD1ke <vslesarenko@oggettoweb.com> Date: Tue, 10 Sep 2019 10:45:43 +0300 Subject: [PATCH 0518/2437] Fix doubled elastic index in alias after error --- app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index 97a76de4b995a..b5309dbdadb70 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -193,6 +193,9 @@ public function addDocs(array $documents, $storeId, $mappedIndexerId) */ public function cleanIndex($storeId, $mappedIndexerId) { + // needed to fix bug with double indices in alias because of second reindex in same process + unset($this->preparedIndex[$storeId]); + $this->checkIndex($storeId, $mappedIndexerId, true); $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex); if ($this->client->isEmptyIndex($indexName)) { From 34f7eafd372db51f9b49ad6e720cfb5bafce6734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 15:27:07 +0200 Subject: [PATCH 0519/2437] Allow use factories and OM for creating objects with variadic arguments in constructor --- .../Framework/Code/Reader/ClassReader.php | 16 +++++++++++++++- .../ObjectManager/Factory/AbstractFactory.php | 10 +++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Code/Reader/ClassReader.php b/lib/internal/Magento/Framework/Code/Reader/ClassReader.php index fe96e9cc742aa..25a7e8e2174cf 100644 --- a/lib/internal/Magento/Framework/Code/Reader/ClassReader.php +++ b/lib/internal/Magento/Framework/Code/Reader/ClassReader.php @@ -28,7 +28,8 @@ public function getConstructor($className) $parameter->getName(), $parameter->getClass() !== null ? $parameter->getClass()->getName() : null, !$parameter->isOptional() && !$parameter->isDefaultValueAvailable(), - $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, + $this->getReflectionParameterDefaultValue($parameter), + $parameter->isVariadic(), ]; } catch (\ReflectionException $e) { $message = $e->getMessage(); @@ -40,6 +41,19 @@ public function getConstructor($className) return $result; } + /** + * @param \ReflectionParameter $parameter + * @return array|mixed|null + */ + private function getReflectionParameterDefaultValue(\ReflectionParameter $parameter) + { + if ($parameter->isVariadic()) { + return []; + } + + return $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null; + } + /** * Retrieve parent relation information for type in a following format * array( diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 15c4cb098b84d..261d9bc7834ab 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -226,7 +226,7 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, { $resolvedArguments = []; foreach ($parameters as $parameter) { - list($paramName, $paramType, $paramRequired, $paramDefault) = $parameter; + list($paramName, $paramType, $paramRequired, $paramDefault, $isVariadic) = $parameter; $argument = null; if (!empty($arguments) && (isset($arguments[$paramName]) || array_key_exists($paramName, $arguments))) { $argument = $arguments[$paramName]; @@ -243,9 +243,13 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, $argument = $paramDefault; } - $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); + if ($isVariadic && is_array($argument)) { + $resolvedArguments = array_merge($resolvedArguments, $argument); + } else { + $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); + $resolvedArguments[] = $argument; + } - $resolvedArguments[] = $argument; } return $resolvedArguments; } From 47a32285ec3c5fb2032f56bdc1df8e11a8b5dece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 16:52:45 +0200 Subject: [PATCH 0520/2437] Fix Static Tests build --- .../Framework/Code/Reader/ClassReader.php | 11 +++++++--- .../ObjectManager/Factory/AbstractFactory.php | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/internal/Magento/Framework/Code/Reader/ClassReader.php b/lib/internal/Magento/Framework/Code/Reader/ClassReader.php index 25a7e8e2174cf..27e633c660689 100644 --- a/lib/internal/Magento/Framework/Code/Reader/ClassReader.php +++ b/lib/internal/Magento/Framework/Code/Reader/ClassReader.php @@ -5,12 +5,15 @@ */ namespace Magento\Framework\Code\Reader; +/** + * Class ClassReader + */ class ClassReader implements ClassReaderInterface { /** * Read class constructor signature * - * @param string $className + * @param string $className * @return array|null * @throws \ReflectionException */ @@ -42,7 +45,9 @@ public function getConstructor($className) } /** - * @param \ReflectionParameter $parameter + * Get reflection parameter default value + * + * @param \ReflectionParameter $parameter * @return array|mixed|null */ private function getReflectionParameterDefaultValue(\ReflectionParameter $parameter) @@ -63,7 +68,7 @@ private function getReflectionParameterDefaultValue(\ReflectionParameter $parame * ... * ) * - * @param string $className + * @param string $className * @return string[] */ public function getParents($className) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 261d9bc7834ab..ed530b1455930 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -11,6 +11,9 @@ use Psr\Log\LoggerInterface; use Magento\Framework\App\ObjectManager; +/** + * Class AbstractFactory + */ abstract class AbstractFactory implements \Magento\Framework\ObjectManager\FactoryInterface { /** @@ -49,10 +52,10 @@ abstract class AbstractFactory implements \Magento\Framework\ObjectManager\Facto protected $creationStack = []; /** - * @param \Magento\Framework\ObjectManager\ConfigInterface $config - * @param ObjectManagerInterface $objectManager + * @param \Magento\Framework\ObjectManager\ConfigInterface $config + * @param ObjectManagerInterface $objectManager * @param \Magento\Framework\ObjectManager\DefinitionInterface $definitions - * @param array $globalArguments + * @param array $globalArguments */ public function __construct( \Magento\Framework\ObjectManager\ConfigInterface $config, @@ -91,6 +94,8 @@ public function setArguments($arguments) } /** + * Get definitions + * * @return \Magento\Framework\ObjectManager\DefinitionInterface */ public function getDefinitions() @@ -105,7 +110,7 @@ public function getDefinitions() * Create object * * @param string $type - * @param array $args + * @param array $args * * @return object * @throws RuntimeException @@ -130,9 +135,9 @@ protected function createObject($type, $args) /** * Resolve an argument * - * @param array &$argument + * @param array &$argument * @param string $paramType - * @param mixed $paramDefault + * @param mixed $paramDefault * @param string $paramName * @param string $requestedType * @@ -214,8 +219,8 @@ protected function parseArray(&$array) * Resolve constructor arguments * * @param string $requestedType - * @param array $parameters - * @param array $arguments + * @param array $parameters + * @param array $arguments * * @return array * From 67b3f6ea2325e495c99e0d70ce4cebc0e0080f10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 17:53:28 +0200 Subject: [PATCH 0521/2437] Unit tests for variadic arguments in constructor --- .../Test/Unit/Factory/CompiledTest.php | 18 +++ .../Test/Unit/Factory/FactoryTest.php | 135 ++++++++++++++++-- .../Test/Unit/Factory/Fixture/Variadic.php | 35 +++++ 3 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Variadic.php diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php index 779a0d04ebc52..648a74b91495d 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/CompiledTest.php @@ -38,6 +38,9 @@ class CompiledTest extends \PHPUnit\Framework\TestCase /** @var ObjectManager */ private $objectManager; + /** + * Setup tests + */ protected function setUp() { $this->objectManager = new ObjectManager($this); @@ -57,6 +60,9 @@ protected function setUp() $this->objectManager->setBackwardCompatibleProperty($this->factory, 'definitions', $this->definitionsMock); } + /** + * Test create simple + */ public function testCreateSimple() { $expectedConfig = $this->getSimpleConfig(); @@ -106,6 +112,9 @@ public function testCreateSimple() $this->assertNull($result->getNullValue()); } + /** + * Test create simple configured arguments + */ public function testCreateSimpleConfiguredArguments() { $expectedConfig = $this->getSimpleNestedConfig(); @@ -170,6 +179,9 @@ public function testCreateSimpleConfiguredArguments() $this->assertNull($result->getNullValue()); } + /** + * Test create get arguments in runtime + */ public function testCreateGetArgumentsInRuntime() { // Stub OM to create test assets @@ -308,18 +320,21 @@ private function getRuntimeParameters() 1 => DependencyTesting::class, 2 => true, 3 => null, + 4 => false, ], 1 => [ 0 => 'sharedDependency', 1 => DependencySharedTesting::class, 2 => true, 3 => null, + 4 => false, ], 2 => [ 0 => 'value', 1 => null, 2 => false, 3 => 'value', + 4 => false, ], 3 => [ 0 => 'valueArray', @@ -329,18 +344,21 @@ private function getRuntimeParameters() 0 => 'default_value1', 1 => 'default_value2', ], + 4 => false, ], 4 => [ 0 => 'globalValue', 1 => null, 2 => false, 3 => '', + 4 => false, ], 5 => [ 0 => 'nullValue', 1 => null, 2 => false, 3 => null, + 4 => false, ], ]; } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php index 309bf48548ec7..c062fcee2cde3 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php @@ -10,6 +10,9 @@ use Magento\Framework\ObjectManager\Factory\Dynamic\Developer; use Magento\Framework\ObjectManager\ObjectManager; +/** + * Class FactoryTest + */ class FactoryTest extends \PHPUnit\Framework\TestCase { /** @@ -27,6 +30,9 @@ class FactoryTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * Setup tests + */ protected function setUp() { $this->config = new Config(); @@ -35,6 +41,9 @@ protected function setUp() $this->factory->setObjectManager($this->objectManager); } + /** + * Test create without args + */ public function testCreateNoArgs() { $this->assertInstanceOf('StdClass', $this->factory->create(\StdClass::class)); @@ -55,7 +64,7 @@ public function testResolveArgumentsException() $definitionsMock = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); $definitionsMock->expects($this->once())->method('getParameters') ->will($this->returnValue([[ - 'firstParam', 'string', true, 'default_val', + 'firstParam', 'string', true, 'default_val', false ]])); $this->factory = new Developer( @@ -136,16 +145,16 @@ public function testCreateUsingReflection() $definitions = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); // should be more than defined in "switch" of create() method $definitions->expects($this->once())->method('getParameters')->with($type)->will($this->returnValue([ - ['one', null, false, null], - ['two', null, false, null], - ['three', null, false, null], - ['four', null, false, null], - ['five', null, false, null], - ['six', null, false, null], - ['seven', null, false, null], - ['eight', null, false, null], - ['nine', null, false, null], - ['ten', null, false, null], + ['one', null, false, null, false], + ['two', null, false, null, false], + ['three', null, false, null, false], + ['four', null, false, null, false], + ['five', null, false, null, false], + ['six', null, false, null, false], + ['seven', null, false, null, false], + ['eight', null, false, null, false], + ['nine', null, false, null, false], + ['ten', null, false, null, false], ])); $factory = new Developer($this->config, null, $definitions); $result = $factory->create( @@ -165,4 +174,108 @@ public function testCreateUsingReflection() ); $this->assertSame(10, $result->getArg(9)); } + + /** + * Test create objects with variadic argument in constructor + * + * @param $createArgs + * @param $expectedArg0 + * @param $expectedArg1 + * @dataProvider testCreateUsingVariadicDataProvider + */ + public function testCreateUsingVariadic( + $createArgs, + $expectedArg0, + $expectedArg1 + ) { + $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic::class; + $definitions = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); + + $definitions->expects($this->once())->method('getParameters')->with($type)->will($this->returnValue([ + [ + 'oneScalars', + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class, + false, + [], + true + ], + ])); + $factory = new Developer($this->config, null, $definitions); + + + + /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic */ + $variadic = is_null($createArgs) + ? $factory->create($type) + : $factory->create($type, $createArgs); + + $this->assertSame($expectedArg0, $variadic->getOneScalarByKey(0)); + $this->assertSame($expectedArg1, $variadic->getOneScalarByKey(1)); + } + + /** + * @return array + */ + public function testCreateUsingVariadicDataProvider() { + $oneScalar1 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + $oneScalar2 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + + return [ + 'without_args' => [ + null, + null, + null, + ], + 'with_empty_args' => [ + [], + null, + null, + ], + 'with_empty_args_value' => [ + [ + 'oneScalars' => [] + ], + null, + null, + ], + 'with_args' => [ + [ + 'oneScalars' => [ + $oneScalar1, + $oneScalar2, + ] + ], + $oneScalar1, + $oneScalar2, + ], + ]; + } + + /** + * Test data can be injected into variadic arguments from di config + */ + public function testCreateVariadicFromDiConfig() + { + $oneScalar1 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + $oneScalar2 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + + // let's imitate that Variadic is configured by providing DI configuration for it + $this->config->extend( + [ + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic::class => [ + 'arguments' => [ + 'oneScalars' => [ + $oneScalar1, + $oneScalar2, + ] + ] + ], + ] + ); + /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic */ + $variadic = $this->factory->create(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic::class); + + $this->assertSame($oneScalar1, $variadic->getOneScalarByKey(0)); + $this->assertSame($oneScalar2, $variadic->getOneScalarByKey(1)); + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Variadic.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Variadic.php new file mode 100644 index 0000000000000..af26f7456fdde --- /dev/null +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/Variadic.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture; + +/** + * Constructor with variadic argument in constructor + */ +class Variadic +{ + /** + * @var OneScalar[] + */ + private $oneScalars; + + /** + * Variadic constructor. + * @param OneScalar[] ...$oneScalars + */ + public function __construct(OneScalar ...$oneScalars) + { + $this->oneScalars = $oneScalars; + } + + /** + * @param string $key + * @return mixed + */ + public function getOneScalarByKey($key) + { + return $this->oneScalars[$key] ?? null; + } +} From 2e4b9004da73012153ae03fbd49b2a986fc5a879 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 11 Sep 2019 10:56:17 -0500 Subject: [PATCH 0522/2437] MC-18685: Remove custom layout updates from admin --- lib/internal/Magento/Framework/Setup/SampleData/Executor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Setup/SampleData/Executor.php b/lib/internal/Magento/Framework/Setup/SampleData/Executor.php index de60f0378484d..0b6e21b0d2a10 100644 --- a/lib/internal/Magento/Framework/Setup/SampleData/Executor.php +++ b/lib/internal/Magento/Framework/Setup/SampleData/Executor.php @@ -49,7 +49,7 @@ public function exec(InstallerInterface $installer) try { $this->appState->emulateAreaCode(\Magento\Framework\App\Area::AREA_GLOBAL, [$installer, 'install']); $this->state->setInstalled(); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->state->setError(); $this->logger->error('Sample Data error: ' . $e->getMessage()); } From 487edadf0fee63f4c1550e1f470920a0d6f64ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 18:56:28 +0200 Subject: [PATCH 0523/2437] Fix Static Tests build --- .../ObjectManager/Factory/AbstractFactory.php | 6 +- .../Test/Unit/Factory/FactoryTest.php | 110 ++++++++++++------ 2 files changed, 77 insertions(+), 39 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index ed530b1455930..f3dc14a02c12b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -120,7 +120,9 @@ protected function createObject($type, $args) try { return new $type(...array_values($args)); } catch (\TypeError $exception) { - /** @var LoggerInterface $logger */ + /** + * @var LoggerInterface $logger + */ $logger = ObjectManager::getInstance()->get(LoggerInterface::class); $logger->critical( sprintf('Type Error occurred when creating object: %s, %s', $type, $exception->getMessage()) @@ -249,7 +251,7 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, } if ($isVariadic && is_array($argument)) { - $resolvedArguments = array_merge($resolvedArguments, $argument); + $resolvedArguments += $argument; } else { $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); $resolvedArguments[] = $argument; diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php index c062fcee2cde3..dc49cc84e23d8 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php @@ -50,22 +50,34 @@ public function testCreateNoArgs() } /** - * @expectedException \UnexpectedValueException + * @expectedException \UnexpectedValueException * @expectedExceptionMessage Invalid parameter configuration provided for $firstParam argument */ public function testResolveArgumentsException() { $configMock = $this->createMock(\Magento\Framework\ObjectManager\Config\Config::class); - $configMock->expects($this->once())->method('getArguments') - ->will($this->returnValue([ - 'firstParam' => 1, - ])); + $configMock->expects($this->once())->method('getArguments')->will( + $this->returnValue( + [ + 'firstParam' => 1, + ] + ) + ); $definitionsMock = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); - $definitionsMock->expects($this->once())->method('getParameters') - ->will($this->returnValue([[ - 'firstParam', 'string', true, 'default_val', false - ]])); + $definitionsMock->expects($this->once())->method('getParameters')->will( + $this->returnValue( + [ + [ + 'firstParam', + 'string', + true, + 'default_val', + false + ] + ] + ) + ); $this->factory = new Developer( $configMock, @@ -80,9 +92,14 @@ public function testResolveArgumentsException() ); } + /** + * Test create with one arg + */ public function testCreateOneArg() { - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar $result */ + /** + * @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar $result + */ $result = $this->factory->create( \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class, ['foo' => 'bar'] @@ -91,6 +108,9 @@ public function testCreateOneArg() $this->assertEquals('bar', $result->getFoo()); } + /** + * Test create with injectable + */ public function testCreateWithInjectable() { // let's imitate that One is injectable by providing DI configuration for it @@ -101,7 +121,9 @@ public function testCreateWithInjectable() ], ] ); - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Two $result */ + /** + * @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Two $result + */ $result = $this->factory->create(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Two::class); $this->assertInstanceOf(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Two::class, $result); $this->assertInstanceOf( @@ -113,8 +135,8 @@ public function testCreateWithInjectable() } /** - * @param string $startingClass - * @param string $terminationClass + * @param string $startingClass + * @param string $terminationClass * @dataProvider circularDataProvider */ public function testCircular($startingClass, $terminationClass) @@ -139,23 +161,30 @@ public function circularDataProvider() ]; } + /** + * Test create using reflection + */ public function testCreateUsingReflection() { $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Polymorphous::class; $definitions = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); // should be more than defined in "switch" of create() method - $definitions->expects($this->once())->method('getParameters')->with($type)->will($this->returnValue([ - ['one', null, false, null, false], - ['two', null, false, null, false], - ['three', null, false, null, false], - ['four', null, false, null, false], - ['five', null, false, null, false], - ['six', null, false, null, false], - ['seven', null, false, null, false], - ['eight', null, false, null, false], - ['nine', null, false, null, false], - ['ten', null, false, null, false], - ])); + $definitions->expects($this->once())->method('getParameters')->with($type)->will( + $this->returnValue( + [ + ['one', null, false, null, false], + ['two', null, false, null, false], + ['three', null, false, null, false], + ['four', null, false, null, false], + ['five', null, false, null, false], + ['six', null, false, null, false], + ['seven', null, false, null, false], + ['eight', null, false, null, false], + ['nine', null, false, null, false], + ['ten', null, false, null, false], + ] + ) + ); $factory = new Developer($this->config, null, $definitions); $result = $factory->create( $type, @@ -178,9 +207,9 @@ public function testCreateUsingReflection() /** * Test create objects with variadic argument in constructor * - * @param $createArgs - * @param $expectedArg0 - * @param $expectedArg1 + * @param $createArgs + * @param $expectedArg0 + * @param $expectedArg1 * @dataProvider testCreateUsingVariadicDataProvider */ public function testCreateUsingVariadic( @@ -191,20 +220,24 @@ public function testCreateUsingVariadic( $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic::class; $definitions = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); - $definitions->expects($this->once())->method('getParameters')->with($type)->will($this->returnValue([ - [ + $definitions->expects($this->once())->method('getParameters')->with($type)->will( + $this->returnValue( + [ + [ 'oneScalars', \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class, false, [], true - ], - ])); + ], + ] + ) + ); $factory = new Developer($this->config, null, $definitions); - - - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic */ + /** + * @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic + */ $variadic = is_null($createArgs) ? $factory->create($type) : $factory->create($type, $createArgs); @@ -216,7 +249,8 @@ public function testCreateUsingVariadic( /** * @return array */ - public function testCreateUsingVariadicDataProvider() { + public function testCreateUsingVariadicDataProvider() + { $oneScalar1 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); $oneScalar2 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); @@ -272,7 +306,9 @@ public function testCreateVariadicFromDiConfig() ], ] ); - /** @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic */ + /** + * @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic $variadic + */ $variadic = $this->factory->create(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\Variadic::class); $this->assertSame($oneScalar1, $variadic->getOneScalarByKey(0)); From 5d3d6aa541c7200a76709420bab76c0f84433252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 19:22:59 +0200 Subject: [PATCH 0524/2437] Fix Static Tests build --- .../Magento/Framework/ObjectManager/Factory/AbstractFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index f3dc14a02c12b..11fa0e5d8c7b0 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -137,7 +137,7 @@ protected function createObject($type, $args) /** * Resolve an argument * - * @param array &$argument + * @param array $argument * @param string $paramType * @param mixed $paramDefault * @param string $paramName From 087023562a8f3b625e35059c8705747885090732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 19:47:44 +0200 Subject: [PATCH 0525/2437] Add tests for classes with non variadic and variadic arguments in constructor --- .../ObjectManager/Factory/AbstractFactory.php | 2 +- .../Test/Unit/Factory/FactoryTest.php | 127 ++++++++++++++++++ .../Unit/Factory/Fixture/SemiVariadic.php | 53 ++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 11fa0e5d8c7b0..bb77fed4c4476 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -251,7 +251,7 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, } if ($isVariadic && is_array($argument)) { - $resolvedArguments += $argument; + $resolvedArguments = array_merge($resolvedArguments, $argument); } else { $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); $resolvedArguments[] = $argument; diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php index dc49cc84e23d8..d192bee2fbc6b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php @@ -314,4 +314,131 @@ public function testCreateVariadicFromDiConfig() $this->assertSame($oneScalar1, $variadic->getOneScalarByKey(0)); $this->assertSame($oneScalar2, $variadic->getOneScalarByKey(1)); } + + /** + * Test create objects with non variadic and variadic argument in constructor + * + * @param $createArgs + * @param $expectedFooValue + * @param $expectedArg0 + * @param $expectedArg1 + * @dataProvider testCreateUsingSemiVariadicDataProvider + */ + public function testCreateUsingSemiVariadic( + $createArgs, + $expectedFooValue, + $expectedArg0, + $expectedArg1 + ) { + $type = \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::class; + $definitions = $this->createMock(\Magento\Framework\ObjectManager\DefinitionInterface::class); + + $definitions->expects($this->once())->method('getParameters')->with($type)->will( + $this->returnValue( + [ + [ + 'foo', + null, + false, + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + false + ], + [ + 'oneScalars', + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class, + false, + [], + true + ], + ] + ) + ); + $factory = new Developer($this->config, null, $definitions); + + /** + * @var \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic $semiVariadic + */ + $semiVariadic = is_null($createArgs) + ? $factory->create($type) + : $factory->create($type, $createArgs); + + $this->assertSame($expectedFooValue, $semiVariadic->getFoo()); + $this->assertSame($expectedArg0, $semiVariadic->getOneScalarByKey(0)); + $this->assertSame($expectedArg1, $semiVariadic->getOneScalarByKey(1)); + } + + /** + * @return array + */ + public function testCreateUsingSemiVariadicDataProvider() + { + $oneScalar1 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + $oneScalar2 = $this->createMock(\Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\OneScalar::class); + + return [ + 'without_args' => [ + null, + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + null, + null, + ], + 'with_empty_args' => [ + [], + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + null, + null, + ], + 'only_with_foo_value' => [ + [ + 'foo' => 'baz' + ], + 'baz', + null, + null, + ], + 'only_with_oneScalars_empty_value' => [ + [ + 'oneScalars' => [] + ], + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + null, + null, + ], + 'only_with_oneScalars_full_value' => [ + [ + 'oneScalars' => [ + $oneScalar1, + $oneScalar2, + ] + ], + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + $oneScalar1, + $oneScalar2, + ], + 'with_all_values_defined_in_right_order' => [ + [ + 'foo' => 'baz', + 'oneScalars' => [ + $oneScalar1, + $oneScalar2, + ] + ], + 'baz', + $oneScalar1, + $oneScalar2, + ], + 'with_all_values_defined_in_reverse_order' => [ + [ + 'oneScalars' => [ + $oneScalar1, + $oneScalar2, + ], + 'foo' => 'baz', + ], + 'baz', + $oneScalar1, + $oneScalar2, + ], + ]; + } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php new file mode 100644 index 0000000000000..1074a96d31a0b --- /dev/null +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture; + +/** + * Constructor with non variadic and variadic argument in constructor + */ +class SemiVariadic +{ + const DEFAULT_FOO_VALUE = 'bar'; + /** + * @var OneScalar[] + */ + private $oneScalars; + + /** + * @var string + */ + private $foo; + + /** + * SemiVariadic constructor. + * @param string $foo + * @param OneScalar[] ...$oneScalars + */ + public function __construct( + string $foo = self::DEFAULT_FOO_VALUE, + OneScalar ...$oneScalars + ) { + $this->foo = $foo; + $this->oneScalars = $oneScalars; + } + + /** + * @param string $key + * @return mixed + */ + public function getOneScalarByKey($key) + { + return $this->oneScalars[$key] ?? null; + } + + /** + * @return string + */ + public function getFoo(): string + { + return $this->foo; + } +} From 8faaeb177fb0639f8696a4872c1fea2142dd281f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 19:49:10 +0200 Subject: [PATCH 0526/2437] Fix Static Tests build --- .../Test/Unit/Factory/Fixture/SemiVariadic.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php index 1074a96d31a0b..8773dec1c48d6 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/Fixture/SemiVariadic.php @@ -11,6 +11,7 @@ class SemiVariadic { const DEFAULT_FOO_VALUE = 'bar'; + /** * @var OneScalar[] */ @@ -23,7 +24,8 @@ class SemiVariadic /** * SemiVariadic constructor. - * @param string $foo + * + * @param string $foo * @param OneScalar[] ...$oneScalars */ public function __construct( @@ -35,7 +37,7 @@ public function __construct( } /** - * @param string $key + * @param mixed $key * @return mixed */ public function getOneScalarByKey($key) From bbe0f4500530aee94b38271acc5c88e9c804d8bb Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 11 Sep 2019 13:04:40 -0500 Subject: [PATCH 0527/2437] MC-18685: Remove custom layout updates from admin --- lib/internal/Magento/Framework/Setup/SampleData/Executor.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/internal/Magento/Framework/Setup/SampleData/Executor.php b/lib/internal/Magento/Framework/Setup/SampleData/Executor.php index 0b6e21b0d2a10..43ad676e7f534 100644 --- a/lib/internal/Magento/Framework/Setup/SampleData/Executor.php +++ b/lib/internal/Magento/Framework/Setup/SampleData/Executor.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Setup\SampleData; +/** + * Performs sample data installations. + */ class Executor { /** @@ -39,6 +42,7 @@ public function __construct( /** * Execute SampleData module installation. + * * Catch exception if it appeared and continue installation * * @param InstallerInterface $installer From 60ea1d7b4e1dcb4ad28b82980ef54fc5676634bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Wed, 11 Sep 2019 21:32:42 +0200 Subject: [PATCH 0528/2437] Avoid array_merge() in loop --- .../Framework/ObjectManager/Factory/AbstractFactory.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index bb77fed4c4476..65a1b345c353b 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -251,13 +251,14 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, } if ($isVariadic && is_array($argument)) { - $resolvedArguments = array_merge($resolvedArguments, $argument); + $resolvedArguments[] = $argument; } else { $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); - $resolvedArguments[] = $argument; + $resolvedArguments[] = [$argument]; } } - return $resolvedArguments; + + return empty($resolvedArguments) ? [] : array_merge(...$resolvedArguments); } } From d7f312a1dcc74d377e952c1ab86f5550bfbf503a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Thu, 12 Sep 2019 00:00:07 +0200 Subject: [PATCH 0529/2437] Resolve cyclomatic complexity, add support for simple parameter declaration --- .../ObjectManager/Factory/AbstractFactory.php | 54 +++++++++++-------- .../Test/Unit/Factory/FactoryTest.php | 17 +++++- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 65a1b345c353b..9279e0b3edf43 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -233,32 +233,44 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, { $resolvedArguments = []; foreach ($parameters as $parameter) { - list($paramName, $paramType, $paramRequired, $paramDefault, $isVariadic) = $parameter; - $argument = null; - if (!empty($arguments) && (isset($arguments[$paramName]) || array_key_exists($paramName, $arguments))) { - $argument = $arguments[$paramName]; - } elseif ($paramRequired) { - if ($paramType) { - $argument = ['instance' => $paramType]; - } else { - $this->creationStack = []; - throw new \BadMethodCallException( - 'Missing required argument $' . $paramName . ' of ' . $requestedType . '.' - ); - } - } else { - $argument = $paramDefault; - } + $resolvedArguments[] = $this->getResolvedArgument((string)$requestedType, $parameter, $arguments); + } - if ($isVariadic && is_array($argument)) { - $resolvedArguments[] = $argument; + return empty($resolvedArguments) ? [] : array_merge(...$resolvedArguments); + } + + /** + * Get resolved argument from parameter + * + * @param string $requestedType + * @param array $parameter + * @param array $arguments + * @return array + */ + private function getResolvedArgument(string $requestedType, array $parameter, array $arguments): array + { + list($paramName, $paramType, $paramRequired, $paramDefault, $isVariadic) = $parameter; + $argument = null; + if (!empty($arguments) && (isset($arguments[$paramName]) || array_key_exists($paramName, $arguments))) { + $argument = $arguments[$paramName]; + } elseif ($paramRequired) { + if ($paramType) { + $argument = ['instance' => $paramType]; } else { - $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); - $resolvedArguments[] = [$argument]; + $this->creationStack = []; + throw new \BadMethodCallException( + 'Missing required argument $' . $paramName . ' of ' . $requestedType . '.' + ); } + } else { + $argument = $paramDefault; + } + if ($isVariadic) { + return is_array($argument) ? $argument : [$argument]; } - return empty($resolvedArguments) ? [] : array_merge(...$resolvedArguments); + $this->resolveArgument($argument, $paramType, $paramDefault, $paramName, $requestedType); + return [$argument]; } } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php index d192bee2fbc6b..cc86d794bd80a 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Factory/FactoryTest.php @@ -272,7 +272,14 @@ public function testCreateUsingVariadicDataProvider() null, null, ], - 'with_args' => [ + 'with_single_arg' => [ + [ + 'oneScalars' => $oneScalar1 + ], + $oneScalar1, + null, + ], + 'with_full_args' => [ [ 'oneScalars' => [ $oneScalar1, @@ -404,6 +411,14 @@ public function testCreateUsingSemiVariadicDataProvider() null, null, ], + 'only_with_oneScalars_single_value' => [ + [ + 'oneScalars' => $oneScalar1 + ], + \Magento\Framework\ObjectManager\Test\Unit\Factory\Fixture\SemiVariadic::DEFAULT_FOO_VALUE, + $oneScalar1, + null, + ], 'only_with_oneScalars_full_value' => [ [ 'oneScalars' => [ From a2cbfa6a2fcb456c087ec7d96636eddec5e5945b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Mart=C3=ADnez?= <adrian.martinez@interactiv4.com> Date: Thu, 12 Sep 2019 00:05:46 +0200 Subject: [PATCH 0530/2437] Fix Static Tests build --- .../Framework/ObjectManager/Factory/AbstractFactory.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php index 9279e0b3edf43..7094b116ead3f 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php +++ b/lib/internal/Magento/Framework/ObjectManager/Factory/AbstractFactory.php @@ -242,9 +242,9 @@ protected function resolveArgumentsInRuntime($requestedType, array $parameters, /** * Get resolved argument from parameter * - * @param string $requestedType - * @param array $parameter - * @param array $arguments + * @param string $requestedType + * @param array $parameter + * @param array $arguments * @return array */ private function getResolvedArgument(string $requestedType, array $parameter, array $arguments): array From 06095be6806ff6d542af4c0eb36279e626fdcff7 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Thu, 12 Sep 2019 16:30:57 +0300 Subject: [PATCH 0531/2437] MC-17653: Cannot schedule update for catalog price rule for date attribute --- .../CatalogPriceRuleActionGroup.xml | 19 ++++++++++++++++++- ...efrontVisibilityOfDuplicateProductTest.xml | 1 + .../Model/Condition/AbstractCondition.php | 16 +++++++++++++--- .../Model/Condition/AbstractConditionTest.php | 10 +++++----- .../Section/PriceRuleConditionsSection.xml | 2 ++ .../Unit/Model/Rule/Condition/ProductTest.php | 4 ++-- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index a7500858fc94e..06dda37222067 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -47,7 +47,6 @@ <arguments> <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> </arguments> - <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName"/> <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription"/> @@ -55,9 +54,27 @@ <click stepKey="openActionDropdown" selector="{{AdminNewCatalogPriceRule.actionsTab}}"/> <fillField stepKey="fillDiscountValue" selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}"/> <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="clickSave"/> <waitForPageLoad stepKey="waitForApplied"/> </actionGroup> + <actionGroup name="AdminCreateCatalogPriceRuleWithConditionActionGroup" extends="createCatalogPriceRule"> + <arguments> + <argument name="catalogRuleType" type="entity" defaultValue="PriceRuleWithCondition"/> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad" after="addNewRule"/> + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="expandConditions" before="openActionDropdown"/> + <scrollTo selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="scrollToConditionsTab" after="expandConditions"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule" after="scrollToConditionsTab"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule" after="waitForNewRule"/> + <selectOption selector="{{PriceRuleConditionsSection.conditionsDropdown}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="selectProductAttribute" after="clickNewRule"/> + <waitForPageLoad stepKey="waitForAttributeLoad" after="selectProductAttribute"/> + <!--Assert that attribute contains today date without time--> + <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> + <generateDate date="now" format="Y-m-d" stepKey="today" after="assertDate"/> + <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> + <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> + </actionGroup> <actionGroup name="CreateCatalogPriceRuleViaTheUi"> <arguments> <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 749d1bee0661a..c37b023317bf7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -11,6 +11,7 @@ <annotations> <stories value="Duplicate Product"/> <features value="ConfigurableProduct"/> + <stories value="Duplicate Product"/> <title value="Visibility of duplicate product on the Storefront"/> <description value="Check visibility of duplicate product on the Storefront"/> <severity value="AVERAGE"/> diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index 6729fe722de56..d58af06da94cf 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Rule\Model\Condition; @@ -17,6 +18,7 @@ * @method setFormName() * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ @@ -390,7 +392,7 @@ public function getValueParsed() $value = reset($value); } if (!is_array($value) && $this->isArrayOperatorType() && $value) { - $value = preg_split('#\s*[,;]\s*#', $value, null, PREG_SPLIT_NO_EMPTY); + $value = preg_split('#\s*[,;]\s*#', (string) $value, -1, PREG_SPLIT_NO_EMPTY); } $this->setValueParsed($value); } @@ -419,8 +421,11 @@ public function getValue() { if ($this->getInputType() == 'date' && !$this->getIsValueParsed()) { // date format intentionally hard-coded + $date = $this->getData('value'); + $date = (\is_numeric($date) ? '@' : '') . $date; $this->setValue( - (new \DateTime($this->getData('value')))->format('Y-m-d H:i:s') + (new \DateTime($date, new \DateTimeZone((string) $this->_localeDate->getConfigTimezone()))) + ->format('Y-m-d H:i:s') ); $this->setIsValueParsed(true); } @@ -432,6 +437,7 @@ public function getValue() * * @return array|string * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * phpcs:disable Generic.Metrics.NestingLevel */ public function getValueName() { @@ -469,6 +475,7 @@ public function getValueName() } return $value; } + //phpcs:enable Generic.Metrics.NestingLevel /** * Get inherited conditions selectors @@ -674,6 +681,9 @@ public function getValueElement() $elementParams['placeholder'] = \Magento\Framework\Stdlib\DateTime::DATE_INTERNAL_FORMAT; $elementParams['autocomplete'] = 'off'; $elementParams['readonly'] = 'true'; + $elementParams['value_name'] = + (new \DateTime($elementParams['value'], new \DateTimeZone($this->_localeDate->getConfigTimezone()))) + ->format('Y-m-d'); } return $this->getForm()->addField( $this->getPrefix() . '__' . $this->getId() . '__value', @@ -879,7 +889,7 @@ protected function _compareValues($validatedValue, $value, $strict = true) return $validatedValue == $value; } - $validatePattern = preg_quote($validatedValue, '~'); + $validatePattern = preg_quote((string) $validatedValue, '~'); if ($strict) { $validatePattern = '^' . $validatePattern . '$'; } diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php index 52653197e3981..0ba41af04a1b3 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php @@ -55,14 +55,14 @@ public function validateAttributeDataProvider() ['0', '==', 1, false], ['1', '==', 1, true], ['x', '==', 'x', true], - ['x', '==', 0, false], + ['x', '==', '0', false], [1, '!=', 1, false], [0, '!=', 1, true], ['0', '!=', 1, true], ['1', '!=', 1, false], ['x', '!=', 'x', false], - ['x', '!=', 0, true], + ['x', '!=', '0', true], [1, '==', [1], true], [1, '!=', [1], false], @@ -164,15 +164,15 @@ public function validateAttributeArrayInputTypeDataProvider() [[1, 2, 3], '{}', '1', true, 'grid'], [[1, 2, 3], '{}', '8', false, 'grid'], - [[1, 2, 3], '{}', 5, false, 'grid'], + [[1, 2, 3], '{}', '5', false, 'grid'], [[1, 2, 3], '{}', [2, 3, 4], true, 'grid'], [[1, 2, 3], '{}', [4], false, 'grid'], [[3], '{}', [], false, 'grid'], [1, '{}', 1, false, 'grid'], [1, '!{}', [1, 2, 3], false, 'grid'], [[1], '{}', null, false, 'grid'], - [null, '{}', null, true, 'input'], - [null, '!{}', null, false, 'input'], + ['null', '{}', 'null', true, 'input'], + ['null', '!{}', 'null', false, 'input'], [null, '{}', [1], false, 'input'], [[1, 2, 3], '()', 1, true, 'select'], diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml index 9a74ced2a2c17..7e0cd323f1140 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml @@ -9,8 +9,10 @@ <element name="conditionsTab" type="text" selector="//div[@data-index='conditions']//span[contains(.,'Conditions')][1]"/> <element name="createNewRule" type="text" selector="span.rule-param.rule-param-new-child"/> <element name="rulesDropdown" type="select" selector="select[data-form-part='sales_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> + <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> <element name="addProductAttributesButton" type="text" selector="#conditions__1--1__children>li>span>a"/> <element name="productAttributesDropdown" type="select" selector="#conditions__1--1__new_child"/> + <element name="firstProductAttributeSelected" type="select" selector="#conditions__1__children .rule-param:nth-of-type(2) a:nth-child(1)"/> <element name="changeCategoriesButton" type="text" selector="#conditions__1--1__children>li>span.rule-param:nth-of-type(2)>a"/> <element name="categoriesChooser" type="text" selector="#conditions__1--1__children>li>span.rule-param:nth-of-type(2)>span>label>a"/> <element name="treeRoot" type="text" selector=".x-tree-root-ct.x-tree-lines"/> diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php index 8ca6b20db3b5a..da358372e0895 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -247,10 +247,10 @@ public function testValidateCategoriesIgnoresVisibility(): void * @param boolean $isValid * @param string $conditionValue * @param string $operator - * @param double $productPrice + * @param string $productPrice * @dataProvider localisationProvider */ - public function testQuoteLocaleFormatPrice($isValid, $conditionValue, $operator = '>=', $productPrice = 2000.00) + public function testQuoteLocaleFormatPrice($isValid, $conditionValue, $operator = '>=', $productPrice = '2000.00') { $attr = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class) ->disableOriginalConstructor() From fcc38ef4d445d96cb108e62eb0f8d17cf421347d Mon Sep 17 00:00:00 2001 From: VaD1ke <vslesarenko@oggettoweb.com> Date: Thu, 12 Sep 2019 17:22:14 +0300 Subject: [PATCH 0532/2437] Fix code sniffer violations in elasticsearch adapter --- .../Model/Adapter/Elasticsearch.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index b5309dbdadb70..7a2b382a5ad53 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -206,7 +206,7 @@ public function cleanIndex($storeId, $mappedIndexerId) // prepare new index name and increase version $indexPattern = $this->indexNameResolver->getIndexPattern($storeId, $mappedIndexerId); $version = (int)(str_replace($indexPattern, '', $indexName)); - $newIndexName = $indexPattern . ++$version; + $newIndexName = $indexPattern . (++$version); // remove index if already exists if ($this->client->indexExists($newIndexName)) { @@ -357,12 +357,14 @@ protected function prepareIndex($storeId, $indexName, $mappedIndexerId) { $this->indexBuilder->setStoreId($storeId); $settings = $this->indexBuilder->build(); - $allAttributeTypes = $this->fieldMapper->getAllAttributesTypes([ - 'entityType' => $mappedIndexerId, - // Use store id instead of website id from context for save existing fields mapping. - // In future websiteId will be eliminated due to index stored per store - 'websiteId' => $storeId - ]); + $allAttributeTypes = $this->fieldMapper->getAllAttributesTypes( + [ + 'entityType' => $mappedIndexerId, + // Use store id instead of website id from context for save existing fields mapping. + // In future websiteId will be eliminated due to index stored per store + 'websiteId' => $storeId + ] + ); $settings['index']['mapping']['total_fields']['limit'] = $this->getMappingTotalFieldsLimit($allAttributeTypes); $this->client->createIndex($indexName, ['settings' => $settings]); $this->client->addFieldsMapping( From 1d284ef7a3ef63e0a68918c613b935833a8f3794 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 13 Sep 2019 22:31:47 -0700 Subject: [PATCH 0533/2437] MC-19873: [Sample Data Function Test] Sample data test failed with Incorrect final price --- .../Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index e589c8595ce2c..944710773123f 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -101,7 +101,9 @@ public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) $scopeTz = new \DateTimeZone( $this->localeDate->getConfigTimezone(ScopeInterface::SCOPE_WEBSITE, $websiteId) ); - $fromTime = (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp(); + $fromTime = $rule->getFromDate() + ? (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp() + : 0; $toTime = $rule->getToDate() ? (new \DateTime($rule->getToDate(), $scopeTz))->getTimestamp() + IndexBuilder::SECONDS_IN_DAY - 1 : 0; From 35f317b91afbe60c27c7a6a6331a3ae4ca7dcfc1 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Sat, 14 Sep 2019 06:45:26 +0000 Subject: [PATCH 0534/2437] MC-19873: [Sample Data Function Test] Sample data test failed with Incorrect final price --- .../Controller/Adminhtml/Promo/Catalog/Save.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 4f58293d53359..996fc4e8ef3d2 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -12,6 +12,7 @@ use Magento\Framework\Registry; use Magento\Framework\Stdlib\DateTime\Filter\Date; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Save action for catalog rule @@ -25,19 +26,27 @@ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog imple */ protected $dataPersistor; + /** + * @var TimezoneInterface + */ + private $localeDate; + /** * @param Context $context * @param Registry $coreRegistry * @param Date $dateFilter * @param DataPersistorInterface $dataPersistor + * @param TimezoneInterface $localeDate */ public function __construct( Context $context, Registry $coreRegistry, Date $dateFilter, - DataPersistorInterface $dataPersistor + DataPersistorInterface $dataPersistor, + TimezoneInterface $localeDate ) { $this->dataPersistor = $dataPersistor; + $this->localeDate = $localeDate; parent::__construct($context, $coreRegistry, $dateFilter); } @@ -66,6 +75,9 @@ public function execute() ); $data = $this->getRequest()->getPostValue(); + if (!$this->getRequest()->getParam('from_date')) { + $data['from_date'] = $this->localeDate->formatDate(); + } $filterValues = ['from_date' => $this->_dateFilter]; if ($this->getRequest()->getParam('to_date')) { $filterValues['to_date'] = $this->_dateFilter; From 6d7daa454c8f45669e13e17dddddc8ecbfbdd25d Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Sat, 14 Sep 2019 05:30:16 +0000 Subject: [PATCH 0535/2437] MC-19873: [Sample Data Function Test] Sample data test failed with Incorrect final price --- .../CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 996fc4e8ef3d2..6d499b93e411f 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -55,16 +55,15 @@ public function __construct( * * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { if ($this->getRequest()->getPostValue()) { - /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ $ruleRepository = $this->_objectManager->get( \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class ); - /** @var \Magento\CatalogRule\Model\Rule $model */ $model = $this->_objectManager->create(\Magento\CatalogRule\Model\Rule::class); @@ -74,7 +73,6 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); - if (!$this->getRequest()->getParam('from_date')) { $data['from_date'] = $this->localeDate->formatDate(); } From c18a2ed64af40909f8c38c5cfd5c55621b497a20 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 11:33:11 -0300 Subject: [PATCH 0536/2437] adding new validation for future dates to admin in customer DOB --- .../Customer/view/base/ui_component/customer_form.xml | 2 +- .../Ui/view/base/web/js/lib/validation/rules.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 5fb8b17dbb8c5..9a9a79f49d142 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -264,7 +264,7 @@ </argument> <settings> <validation> - <rule name="validate-date" xsi:type="boolean">true</rule> + <rule name="validate-dob" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <visible>true</visible> diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 3402d1d1df03b..863b4a80f58cb 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -1067,6 +1067,16 @@ define([ return new RegExp(param).test(value); }, $.mage.__('This link is not allowed.') + ], + 'validate-dob': [ + function (value, params, additionalParams) { + if(value === '') { + return true; + } + + return moment(value).isBefore(moment()); + }, + $.mage.__('The Date of Birth should not be greater than today') ] }, function (data) { return { From 5163a0518069e078e5b0920c57f5fa216b216272 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 11:44:11 -0300 Subject: [PATCH 0537/2437] the the date validation that was missing on last commit --- .../Magento/Customer/view/base/ui_component/customer_form.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 9a9a79f49d142..8692a4eafdb1e 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -264,6 +264,7 @@ </argument> <settings> <validation> + <rule name="validate-date" xsi:type="boolean">true</rule> <rule name="validate-dob" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> From 8d9b2cb87066be1a3367d8604956bc12a4baab14 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 11:50:49 -0300 Subject: [PATCH 0538/2437] adding period (.) to end of line --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 863b4a80f58cb..ca40cbfc168cb 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -1076,7 +1076,7 @@ define([ return moment(value).isBefore(moment()); }, - $.mage.__('The Date of Birth should not be greater than today') + $.mage.__('The Date of Birth should not be greater than today.') ] }, function (data) { return { From 45158591b846982adda65c81c788f6039df54d6c Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 12:24:59 -0300 Subject: [PATCH 0539/2437] adding space after 'if' and removing unused arguments --- app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index ca40cbfc168cb..08f67955976c4 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -1069,8 +1069,8 @@ define([ $.mage.__('This link is not allowed.') ], 'validate-dob': [ - function (value, params, additionalParams) { - if(value === '') { + function (value) { + if (value === '') { return true; } From c5d1aee471b44cc9bfc9e259645bb4ba9c82ae56 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 13:34:56 -0300 Subject: [PATCH 0540/2437] disabling the input of greater dates from datepicker. Validation still on field for manual inputs --- .../Magento/Customer/view/base/ui_component/customer_form.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 8692a4eafdb1e..7eb1394f77a41 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -260,6 +260,9 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> + <item name="options" xsi:type="array"> + <item name="maxDate" xsi:type="string">-1d</item> + </item> </item> </argument> <settings> From 497462878f0a0bdec5becda7c92075e29a431346 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Sat, 14 Sep 2019 15:07:47 -0300 Subject: [PATCH 0541/2437] adding the validation for manual input on customer create, on frontend --- .../Magento/Customer/Block/Widget/Dob.php | 2 ++ .../view/frontend/templates/widget/dob.phtml | 8 ++++++++ .../view/frontend/web/js/validation.js | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 app/code/Magento/Customer/view/frontend/web/js/validation.js diff --git a/app/code/Magento/Customer/Block/Widget/Dob.php b/app/code/Magento/Customer/Block/Widget/Dob.php index d874729d9132e..0a60c2d634cb1 100644 --- a/app/code/Magento/Customer/Block/Widget/Dob.php +++ b/app/code/Magento/Customer/Block/Widget/Dob.php @@ -267,6 +267,8 @@ public function getHtmlExtraParams() $validators['validate-date'] = [ 'dateFormat' => $this->getDateFormat() ]; + $validators['validate-dob'] = true; + return 'data-validate="' . $this->_escaper->escapeHtml(json_encode($validators)) . '"'; } diff --git a/app/code/Magento/Customer/view/frontend/templates/widget/dob.phtml b/app/code/Magento/Customer/view/frontend/templates/widget/dob.phtml index ac4b9f93e0c54..3c2f970faadee 100644 --- a/app/code/Magento/Customer/view/frontend/templates/widget/dob.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/widget/dob.phtml @@ -35,3 +35,11 @@ $fieldCssClass .= $block->isRequired() ? ' required' : ''; <?php endif; ?> </div> </div> + +<script type="text/x-magento-init"> + { + "*": { + "Magento_Customer/js/validation": {} + } + } + </script> diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js new file mode 100644 index 0000000000000..840134d64422e --- /dev/null +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -0,0 +1,19 @@ +require([ + 'jquery', + 'moment', + 'jquery/ui', + 'jquery/validate', + 'mage/translate', +], function($, moment) { + 'use strict'; + $.validator.addMethod( + "validate-dob", + function (value) { + if (value === '') { + return true; + } + return moment(value).isBefore(moment()); + }, + $.mage.__('The Date of Birth should not be greater than today.') + ); +}); From 35e98f5fd33b4bdaa44dd4f3e982cbe7b30e3565 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 16 Sep 2019 11:40:17 +0300 Subject: [PATCH 0542/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../SaveQuoteAddressToCustomerAddressBook.php | 1 - .../Model/Cart/SetShippingAddressesOnCart.php | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php index e9aa4ba745b9f..543fe96ddec8b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php @@ -52,7 +52,6 @@ public function __construct( } /** - * * @param QuoteAddress $quoteAddress * @param int $customerId * diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 1b735aadc1101..f2e639f74cc20 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -78,6 +78,24 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } + $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); + + $this->assignShippingAddressToCart->execute($cart, $shippingAddress); + } + + /** + * @param ContextInterface $context + * @param int|null $customerAddressId + * @param array|null $addressInput + * + * @return \Magento\Quote\Model\Quote\Address + * @throws GraphQlAuthorizationException + */ + private function createShippingAddress( + ContextInterface $context, + ?int $customerAddressId, + ?array $addressInput + ) { $customerId = $context->getUserId(); if (null === $customerAddressId) { @@ -98,6 +116,6 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } - $this->assignShippingAddressToCart->execute($cart, $shippingAddress); + return $shippingAddress; } } From eaca5465e196a63dc7bd585366b6690e3294f1b7 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Mon, 16 Sep 2019 16:26:45 +0400 Subject: [PATCH 0543/2437] MC-18822: Increase test coverage for Content functional area - Automation test for MC-6192 --- .../AdminProductAttributeSetActionGroup.xml | 8 +- .../Test/Mftf/Data/ProductAttributeData.xml | 4 + .../Catalog/Test/Mftf/Data/ProductData.xml | 13 -- .../Mftf/Section/AdminProductFormSection.xml | 2 +- .../StorefrontCategorySidebarSection.xml | 1 + .../AdminAddOptionsToAttributeActionGroup.xml | 38 ---- .../AdminConfigurableProductActionGroup.xml | 41 ++-- ...reateProductConfigurationsPanelSection.xml | 2 +- ...CheckResultsOfColorAndOtherFiltersTest.xml | 186 ++++++++++++++++++ ...CheckResultsOfColorAndOtherFiltersTest.xml | 172 ---------------- 10 files changed, 210 insertions(+), 257 deletions(-) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml delete mode 100644 app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index c67c2148673a5..e3f13f32ad015 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -64,11 +64,11 @@ <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> </actionGroup> - <actionGroup name="AddUnsignedAttributeToGroup" extends="CreateDefaultAttributeSet"> + <actionGroup name="AdminAddUnsignedAttributeToGroup" extends="CreateDefaultAttributeSet"> <arguments> - <argument name="firstOption" type="string"/> - <argument name="secondOption" type="string"/> - <argument name="group" type="string"/> + <argument name="firstOption" type="string" defaultValue="color"/> + <argument name="secondOption" type="string" defaultValue="material"/> + <argument name="group" type="string" defaultValue="Product Details"/> </arguments> <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(firstOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign1"/> <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(secondOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 6bbca45741c75..bc5f2af7b9950 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -108,6 +108,10 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="multipleSelectProductAttribute" extends="productDropDownAttribute" type="ProductAttribute"> + <data key="frontend_input">multiselect</data> + <data key="frontend_input_admin">Multiple Select</data> + </entity> <entity name="productDropDownAttributeNotSearchable" type="ProductAttribute"> <data key="attribute_code" unique="suffix">attribute</data> <data key="frontend_input">select</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index b8d7aa878230a..517ab253b8238 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1165,19 +1165,6 @@ <requiredEntity type="product_extension_attribute">EavStock10</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> - <entity name="Simple1" type="product"> - <data key="sku">Simple1</data> - <data key="type_id">simple</data> - <data key="attribute_set_id">4</data> - <data key="visibility">4</data> - <data key="name">Simple1</data> - <data key="price">1.00</data> - <data key="urlKey" unique="suffix">api-simple-product</data> - <data key="status">1</data> - <data key="quantity">111</data> - <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> - <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> - </entity> <entity name="SimpleProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">simple-product_</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index b8aa4aa0ce822..fbfeac6327f90 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -8,7 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> - <element name="additionalOptions" type="select" selector="//select[@class='admin__control-multiselect']"/> + <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 406bea8d8aeab..0058d32dda303 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> + <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> <element name="filterOptions" type="text" selector=".filter-options-content .items"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml index b8bdbdfe082c5..e88f71ce23ac2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml @@ -8,44 +8,6 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateNewAttributeWithOptions"> - <arguments> - <argument name="labelName" type="string"/> - <argument name="inputType" type="string"/> - <argument name="firstOption" type="string"/> - <argument name="secondOption" type="string"/> - <argument name="thirdOption" type="string"/> - <argument name="fourthOption" type="string"/> - <argument name="useInLayer" type="string"/> - </arguments> - <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="createNewAttribute"/> - <fillField stepKey="fillDefaultLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{labelName}}"/> - <selectOption selector="{{AttributePropertiesSection.InputType}}" stepKey="checkInputType" userInput="{{inputType}}"/> - <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption6"/> - <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('1')}}" userInput="{{firstOption}}" stepKey="fillAdminValue6"/> - <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption7"/> - <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('2')}}" userInput="{{secondOption}}" stepKey="fillAdminValue7"/> - <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption8"/> - <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('3')}}" userInput="{{thirdOption}}" stepKey="fillAdminValue8"/> - <click selector="{{AttributeOptionsSection.AddOption}}" stepKey="clickAddOption9"/> - <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('4')}}" userInput="{{fourthOption}}" stepKey="fillAdminValue9"/> - <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> - <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" stepKey="selectUseInLayer" userInput="{{useInLayer}}"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="waitForGridPageLoad"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> - <actionGroup name="CreateAttributeWithOptions" extends="CreateNewAttributeWithOptions"> - <arguments> - <argument name="fifthOption" type="string"/> - </arguments> - <click selector="{{AttributeOptionsSection.AddOption}}" after="fillAdminValue9" stepKey="clickAddOption10"/> - <fillField selector="{{DropdownAttributeOptionsSection.nthOptionAdminLabel('5')}}" after="clickAddOption10" userInput="{{fifthOption}}" stepKey="fillAdminValue10"/> - </actionGroup> <actionGroup name="addOptionsToAttributeActionGroup"> <annotations> <description>Adds 5 provided Options to a new Attribute on the Configurable Product creation/edit page.</description> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml index a1042141d9373..9b77dfd043f71 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml @@ -26,40 +26,25 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> <selectOption selector="{{AdminProductFormSection.additionalOptions}}" parameterArray="{{option1}}" stepKey="searchAndMultiSelect1"/> </actionGroup> - <actionGroup name="CreateConfigurationsForOptions"> + <actionGroup name="AdminCreateConfigurationsForAttribute" extends="generateConfigurationsByAttributeCode"> <arguments> - <argument name="option2" type="string"/> <argument name="price" type="string"/> - <argument name="sku" type="string"/> </arguments> - <!-- create configurations for colors the product is available in --> - <comment userInput="create configurations for colors the product is available in" stepKey="commentCreateConfigurations"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters2"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{option2}}" stepKey="fillFilterAttributeCodeField2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton12"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton22"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="waitForNextPageOpened2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" stepKey="clickOnApplySinglePriceToAllSkus"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" stepKey="enterAttributePrice"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="{{sku}}" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextStep3"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitForNextPageOpened3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNew"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="clickOnNextButton2" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="waitForNextPageOpened2" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" before="clickOnApplySingleQuantityToEachSku" stepKey="enterAttributePrice"/> </actionGroup> - <actionGroup name="CreateConfigurableProductWithAttributeSetAndOption" extends="createConfigurationsForOptions"> + + <actionGroup name="AdminCreateConfigurableProductWithAttributeUncheckOption" extends="generateConfigurationsByAttributeCode"> <arguments> - <argument name="colorOption" type="string"/> + <argument name="attributeOption" type="string"/> + <argument name="price" type="string"/> </arguments> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeOption('colorOption')}}" after="clickOnSelectAll2" stepKey="clickToUncheckOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeOption('attributeOption')}}" after="clickOnSelectAll" stepKey="clickToUncheckOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" after="clickToUncheckOption" stepKey="clickOnNextButton22"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="clickOnNextButton22" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="waitForNextPageOpened2" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" before="clickOnApplySingleQuantityToEachSku" stepKey="enterAttributePrice"/> </actionGroup> <!--Filter the product grid and view expected products--> <actionGroup name="viewConfigurableProductInAdminGrid"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 488b227c29cbd..34feeb3b5bf3e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreateProductConfigurationsPanel"> - <element name="attributeOption" type="checkbox" selector="//li[@class='attribute-option'][@data-attribute-option-title='{{colorOption}}']" parameterized="true"/> + <element name="attributeOption" type="checkbox" selector="li[data-attribute-option-title='{{colorOption}}']" parameterized="true"/> <element name="next" type="button" selector=".steps-wizard-navigation .action-next-step" timeout="30"/> <element name="createNewAttribute" type="button" selector=".select-attributes-actions button[title='Create New Attribute']" timeout="30"/> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml new file mode 100644 index 0000000000000..2d68861b5d946 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckResultsOfColorAndOtherFiltersTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Checking filters results"/> + <title value="Checking results of color and other filters"/> + <description value="Checking results of filters: color and other filters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6192"/> + <useCaseId value="MAGETWO-91753"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create default category with subcategory --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SubCategoryWithParent" stepKey="createSubcategory"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Add first attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption4" stepKey="createConfigProductAttributeOption4"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption5" stepKey="createConfigProductAttributeOption5"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Add second attribute with options--> + <createData entity="multipleSelectProductAttribute" stepKey="createConfigProductAttribute2"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption12"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption6"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption7"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption4" stepKey="createConfigProductAttributeOption8"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <!-- Add created attributes with options to Attribute Set --> + <actionGroup ref="AdminAddUnsignedAttributeToGroup" stepKey="createDefaultAttributeSet"> + <argument name="label" value="mySet"/> + <argument name="firstOption" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="secondOption" value="$$createConfigProductAttribute2.attribute_code$$"/> + <argument name="group" value="Product Details"/> + </actionGroup> + <!-- Create three configurable products with options --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <!-- Create First configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductFirst"> + <argument name="product" value="ConfigurableProductWithAttributeSet1"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurationsForAttribute" stepKey="createConfigurationFirst"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNew"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageFiest"/> + <!-- Create Second configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductSecond"> + <argument name="product" value="ConfigurableProductWithAttributeSet2"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationSecond"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option5"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoadThird"/> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDownThird"/> + <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNewThird"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageSecond"/> + <!-- Create Third configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductThird"> + <argument name="product" value="ConfigurableProductWithAttributeSet3"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationThird"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option1"/> + </actionGroup> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSaveButton"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveConfigurableProductMessage"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPageAgain"/> + <waitForPageLoad stepKey="waitForPageLoadForth"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggleAgain"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickOnAddSimpleProduct"/> + <!-- Create Simple product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createSimpleProduct"> + <argument name="product" value="ApiSimpleProduct"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2']"/> + </actionGroup> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSave"/> + <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageThird"/> + </before> + <after> + <!-- Delete all created data --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete attribute set --> + <actionGroup ref="deleteAttributeSetByLabel" stepKey="deleteAttributeSet"> + <argument name="label" value="mySet"/> + </actionGroup> + <!-- Delete First attribute --> + <actionGroup ref="SearchAttributeByCodeOnProductAttributeGridActionGroup" stepKey="searchAttributeByCodeOnProductAttributeGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <!-- Delete Second attribute --> + <actionGroup ref="SearchAttributeByCodeOnProductAttributeGridActionGroup" stepKey="searchSecondAttributeByCodeOnProductAttributeGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute2.attribute_code$$"/> + </actionGroup> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openSecondProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute2.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteSecondProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute2.attribute_code$$"/> + </actionGroup> + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open a category on storefront --> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPage"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose First attribute filter --> + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="waitForCartRuleButton"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="expandFirstAttribute"/> + <waitForPageLoad stepKey="waitForFilterLoad"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> + <waitForPageLoad stepKey="waitForAttributeOption"/> + <see userInput="{{ConfigurableProductWithAttributeSet3.name}}" stepKey="seeFirstProduct"/> + <see userInput="{{ConfigurableProductWithAttributeSet2.name}}" stepKey="seeSecondProduct"/> + <see userInput="{{ConfigurableProductWithAttributeSet1.name}}" stepKey="seeThirdProduct"/> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose Second attribute filter --> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute2.default_frontend_label$$')}}" stepKey="expandSecondAttributeOption"/> + <waitForPageLoad stepKey="waitForFilterPageLoad"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> + <see userInput="{{ConfigurableProductWithAttributeSet1.name}}" stepKey="seeFourthProduct"/> + <see userInput="{{ConfigurableProductWithAttributeSet2.name}}" stepKey="seeFifthProduct"/> + </test> +</tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml deleted file mode 100644 index 5b16f083067ee..0000000000000 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ /dev/null @@ -1,172 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCheckResultsOfColorAndOtherFiltersTest"> - <annotations> - <features value="LayeredNavigation"/> - <stories value="Checking filters results"/> - <title value="Checking results of color and other filters"/> - <description value="Checking results of filters: color and other filters"/> - <severity value="MAJOR"/> - <testCaseId value="MC-6192"/> - <useCaseId value="MAGETWO-91753"/> - <group value="layeredNavigation"/> - </annotations> - <before> - <!-- Login as Admin --> - <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Create default category with subcategory --> - <comment userInput="Create default category with subcategory" stepKey="commentCreateCategory"/> - <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="SubCategoryWithParent" stepKey="createSubcategory"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!-- Add first attribute with options --> - <comment userInput="Add first attribute with options" stepKey="commentAddFirstAttribute"/> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> - <waitForPageLoad stepKey="waitForProductAttributes"/> - <actionGroup ref="CreateAttributeWithOptions" stepKey="createAttribute"> - <argument name="labelName" value="color2"/> - <argument name="inputType" value="Dropdown"/> - <argument name="firstOption" value="red"/> - <argument name="secondOption" value="green"/> - <argument name="thirdOption" value="blue"/> - <argument name="fourthOption" value="brown"/> - <argument name="fifthOption" value="black"/> - <argument name="useInLayer" value="Filterable (with results)"/> - </actionGroup> - <!-- Add second attribute with options--> - <comment userInput="Add second attribute with options" stepKey="commentAddSecondAttribute"/> - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes2"/> - <actionGroup ref="CreateNewAttributeWithOptions" stepKey="createSecondAttribute"> - <argument name="labelName" value="material"/> - <argument name="inputType" value="Multiple Select"/> - <argument name="firstOption" value="cotton"/> - <argument name="secondOption" value="fabric"/> - <argument name="thirdOption" value="jeans"/> - <argument name="fourthOption" value="synthetic"/> - <argument name="useInLayer" value="Filterable (with results)"/> - </actionGroup> - <actionGroup ref="AddUnsignedAttributeToGroup" stepKey="createDefaultAttributeSet"> - <argument name="label" value="mySet"/> - <argument name="firstOption" value="color2"/> - <argument name="secondOption" value="material"/> - <argument name="group" value="Product Details"/> - </actionGroup> - <!-- Create three configurable products with options --> - <comment userInput="Create three configurable products with options" stepKey="commentCreateConfigurableProducts"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="wait1"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct1"> - <argument name="product" value="ConfigurableProductWithAttributeSet1"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['jeans', 'synthetic', 'cotton', 'fabric']"/> - </actionGroup> - <actionGroup ref="CreateConfigurationsForOptions" stepKey="createConfigurations1"> - <argument name="option2" value="color2"/> - <argument name="price" value="34"/> - <argument name="sku" value="1000"/> - </actionGroup> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct2"> - <argument name="product" value="ConfigurableProductWithAttributeSet2"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['jeans','cotton', 'fabric']"/> - </actionGroup> - <actionGroup ref="CreateConfigurableProductWithAttributeSetAndOption" stepKey="createConfigurations2"> - <argument name="option2" value="color2"/> - <argument name="price" value="34"/> - <argument name="sku" value="111"/> - <argument name="colorOption" value="black"/> - </actionGroup> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProduct3"> - <argument name="product" value="ConfigurableProductWithAttributeSet3"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['jeans','synthetic', 'fabric']"/> - </actionGroup> - <actionGroup ref="CreateConfigurableProductWithAttributeSetAndOption" stepKey="createConfigurations3"> - <argument name="option2" value="color2"/> - <argument name="price" value="34"/> - <argument name="sku" value="222"/> - <argument name="colorOption" value="red"/> - </actionGroup> - <!-- Create Simple product with options --> - <comment userInput="Create Simple product with options" stepKey="commentCreateProduct"/> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createSimpleProduct"> - <argument name="product" value="Simple1"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['cotton', 'fabric']"/> - </actionGroup> - <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSave"/> - <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - <!-- Open a category on storefront --> - <comment userInput="Open a category on storefront" stepKey="commentOpenCategoryOnStorefront"/> - <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPage"> - <argument name="categoryName" value="$$createCategory.name$$"/> - </actionGroup> - <!-- Choose color filter --> - <comment userInput="Choose color filter" stepKey="commentChooseColorFilter"/> - <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('color2')}}" stepKey="waitForCartRuleButton"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('color2')}}" stepKey="expandColorAttribute"/> - <waitForPageLoad stepKey="waitForFilterLoad"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('green')}}" stepKey="expandGreenAttribute"/> - <see userInput="Pants" stepKey="seeFirstProduct"/> - <see userInput="Cardigan" stepKey="seeSecondProduct"/> - <see userInput="Jacket" stepKey="seeThirdProduct"/> - <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> - <argument name="categoryName" value="$$createCategory.name$$"/> - </actionGroup> - <!-- Choose material filter --> - <comment userInput="Choose material filter" stepKey="commentChooseMaterialFilter"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('material')}}" stepKey="expandMaterialAttribute"/> - <waitForPageLoad stepKey="waitForFilterPageLoad"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('cotton')}}" stepKey="expandCottonAttribute"/> - <see userInput="Cardigan" stepKey="seeFourthProduct"/> - <see userInput="Jacket" stepKey="seeFifthProduct"/> - </before> - <after> - <!-- Delete created data --> - <comment userInput="Delete created data" stepKey="commentDeleteData"/> - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - <waitForPageLoad stepKey="wait1"/> - <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{ConfigurableProductWithAttributeSet1.attribute_set_name}}" stepKey="filterByName"/> - <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> - <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="wait2"/> - <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="delete"/> - <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="wait3"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageAgain"> - <argument name="ProductAttribute" value="color2"/> - </actionGroup> - <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOk" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletionAttribute"/> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageAttribute"> - <argument name="ProductAttribute" value="material"/> - </actionGroup> - <click stepKey="clickDeleteAgain" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> - <click stepKey="clickOkForTheSecondTime" selector="{{AttributeDeleteModalSection.confirm}}"/> - <waitForPageLoad stepKey="waitForDeletion"/> - <!-- Log out --> - <comment userInput="Log out" stepKey="commentLogOut"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - </test> -</tests> From 9cd6e22de237acb09cd1afaba5d2b95704ff0988 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Tue, 17 Sep 2019 13:35:04 +0400 Subject: [PATCH 0544/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6411 --- .../AdminOrderSelectShippingMethodActionGroup.xml | 8 ++++---- .../Section/AdminInvoicePaymentShippingSection.xml | 2 +- ...eOrderCustomStoreShippingMethodTableRatesTest.xml | 12 ++---------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml index 87f0c4d7e470e..cfed144184e40 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml @@ -10,14 +10,14 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOrderSelectShippingMethodActionGroup"> <arguments> - <argument name="methodTitle" type="string" defaultValue="Flat Rate"/> - <argument name="methodName" type="string" defaultValue="Fixed"/> + <argument name="methodTitle" type="string" defaultValue="flatrate"/> + <argument name="methodName" type="string" defaultValue="fixed"/> </arguments> <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" stepKey="waitForShippingMethodsOpen"/> <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" stepKey="openShippingMethod"/> <conditionalClick selector="{{AdminInvoicePaymentShippingSection.getShippingMethod}}" dependentSelector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" visible="false" stepKey="openShippingMethodSecondTime"/> - <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" stepKey="waitForShippingMethod"/> - <click selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodTitle, methodName)}}" stepKey="chooseShippingMethod"/> + <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodName, methodTitle)}}" stepKey="waitForShippingMethod"/> + <click selector="{{AdminInvoicePaymentShippingSection.fixedPriceShippingMethod(methodName, methodTitle)}}" stepKey="chooseShippingMethod"/> <waitForPageLoad stepKey="waitForPageToLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml index a0b93c66f3a59..32e987bea919b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoicePaymentShippingSection.xml @@ -17,7 +17,7 @@ <element name="CreateShipment" type="checkbox" selector=".order-shipping-address input[name='invoice[do_shipment]']"/> <element name="getShippingMethodAndRates" type="button" selector="//span[text()='Get shipping methods and rates']" timeout="60"/> <element name="shippingMethod" type="button" selector="//label[contains(text(), 'Fixed')]" timeout="60"/> - <element name="fixedPriceShippingMethod" type="button" selector="//*[contains(text(), '{{methodTitle}}')]/parent::dl//label[contains(text(), '{{methodName}}')]" parameterized="true"/> + <element name="fixedPriceShippingMethod" type="button" selector="#s_method_{{methodName}}_{{methodTitle}}" parameterized="true"/> <element name="getShippingMethod" type="button" selector="#order-shipping-method-summary a"/> </section> </sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index bfb94c4293135..67885ccfc99d8 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -21,12 +21,10 @@ </annotations> <before> <!--Create product and customer--> - <comment userInput="Create product and customer" stepKey="commentCreateProductAndCustomer"/> <createData entity="SimpleProduct2" stepKey="createProduct"/> <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create website, store group and store view--> - <comment userInput="Create website, store group and store view" stepKey="commentCreateWebsiteStoreAndView"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> <argument name="newWebsiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> @@ -41,7 +39,6 @@ <argument name="customStore" value="customStore"/> </actionGroup> <!--Enable Table Rate method and import csv file--> - <comment userInput="Enable Table Rate method and import csv file" stepKey="commentEnableTableRates"/> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite"> <argument name="website" value="_defaultWebsite"/> @@ -64,7 +61,6 @@ </before> <after> <!--Delete created data--> - <comment userInput="Delete created data" stepKey="commetnDeleteCreatedData"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteSecondCreatedCustomer"> @@ -76,7 +72,6 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!--Assign product to custom website--> - <comment userInput="Assign product to custom website" stepKey="commentAssignProductToWebsite"/> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="unassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> @@ -87,7 +82,6 @@ </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!--Assign customer to custom website--> - <comment userInput="Assign customer to custom website" stepKey="commentAssignCustomerToWebsite"/> <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> @@ -96,7 +90,6 @@ </actionGroup> <actionGroup ref="AdminSaveCustomerAndAssertSuccessMessage" stepKey="saveAndCheckSuccessMessage"/> <!--Create order--> - <comment userInput="Create order" stepKey="commentCreateOrder"/> <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$createCustomer$$"/> <argument name="storeView" value="customStore"/> @@ -109,10 +102,9 @@ <argument name="address" value="US_Address_TX"/> </actionGroup> <!--Choose Best Way shipping Method--> - <comment userInput="Choose Best Way shipping Method" stepKey="commentChooseShippingMethod"/> <actionGroup ref="AdminOrderSelectShippingMethodActionGroup" stepKey="chooseBestWayMethod"> - <argument name="methodTitle" value="Best Way"/> - <argument name="methodName" value="Table Rate"/> + <argument name="methodTitle" value="bestway"/> + <argument name="methodName" value="tablerate"/> </actionGroup> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> </test> From 99cb4475b89d51e53413ad9418d9bcc10bfe7d09 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 17 Sep 2019 16:26:36 +0300 Subject: [PATCH 0545/2437] MC-20195: Move test MC-13104 to infrastructure --- ...product_simple_with_custom_file_option.php | 94 +++++++++++++++++++ ...imple_with_custom_file_option_rollback.php | 33 +++++++ .../Checkout/_files/ValidatorFileMock.php | 32 ++++--- 3 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php new file mode 100644 index 0000000000000..5c0c024ef4c39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +Bootstrap::getInstance()->reinitialize(); + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); + +/** @var $product Product */ +$product = $objectManager->create(Product::class); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple_with_custom_file_option') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setDescription('Description with <b>html tag</b>') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setCanSaveCustomOptions(true) + ->setHasOptions(true); + +$options = [ + [ + 'title' => 'file option', + 'type' => 'file', + 'is_require' => true, + 'sort_order' => 1, + 'price' => 30.0, + 'price_type' => 'percent', + 'sku' => 'sku3', + 'file_extension' => 'jpg, png, gif', + 'image_size_x' => 100, + 'image_size_y' => 100, + + ], +]; + +$customOptions = []; + +/** @var ProductCustomOptionInterfaceFactory $customOptionFactory */ +$customOptionFactory = $objectManager->create(ProductCustomOptionInterfaceFactory::class); + +foreach ($options as $option) { + /** @var ProductCustomOptionInterface $customOption */ + $customOption = $customOptionFactory->create(['data' => $option]); + $customOption->setProductSku($product->getSku()); + + $customOptions[] = $customOption; +} + +$product->setOptions($customOptions); + +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$productRepository->save($product); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php new file mode 100644 index 0000000000000..7f53552174518 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple_with_custom_file_option', false, null, true); + $productRepository->delete($product); +} +catch (NoSuchEntityException $e) +{ +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php index 440f437e74e7c..9b5650b1826c3 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\_files; use Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile; @@ -14,27 +16,29 @@ class ValidatorFileMock extends \PHPUnit\Framework\TestCase { /** * Returns mock. - * + * @param array|null $fileData * @return ValidatorFile|\PHPUnit_Framework_MockObject_MockObject */ - public function getInstance() + public function getInstance($fileData = null) { - $userValue = [ - 'type' => 'image/jpeg', - 'title' => "test.jpg", - 'quote_path' => "custom_options/quote/s/t/4624d2.jpg", - 'order_path' => "custom_options/order/s/t/89d25b4624d2.jpg", - "fullpath" => "pub/media/custom_options/quote/s/t/e47389d25b4624d2.jpg", - "size"=> "71901", - "width" => 5, - "height" => 5, - "secret_key" => "10839ec1631b77e5e473", - ]; + if (empty($fileData)) { + $fileData = [ + 'type' => 'image/jpeg', + 'title' => "test.jpg", + 'quote_path' => "custom_options/quote/s/t/4624d2.jpg", + 'order_path' => "custom_options/order/s/t/89d25b4624d2.jpg", + "fullpath" => "pub/media/custom_options/quote/s/t/e47389d25b4624d2.jpg", + "size" => "71901", + "width" => 5, + "height" => 5, + "secret_key" => "10839ec1631b77e5e473", + ]; + } $instance = $this->getMockBuilder(ValidatorFile::class) ->disableOriginalConstructor() ->getMock(); $instance->method('SetProduct')->willReturnSelf(); - $instance->method('validate')->willReturn($userValue); + $instance->method('validate')->willReturn($fileData); return $instance; } From 9363ec3134e5358c7aa77872d4a79715040dfc58 Mon Sep 17 00:00:00 2001 From: Aliaksei Yakimovich2 <aliaksei_yakimovich2@epam.com> Date: Tue, 17 Sep 2019 16:44:11 +0300 Subject: [PATCH 0546/2437] MC-15523: Watermark is possible to set up for swatch image type - Fixed merge conflict with 2.3-develop; --- .../Theme/Test/Mftf/Section/AdminDesignConfigSection.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index cf420598ca44e..762537ba426f2 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -32,5 +32,7 @@ <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> <element name="imageWatermarkType" type="text" selector="//div[contains(@class, 'fieldset-wrapper-title')]//span[contains(text(), '{{watermarkType}}')]" parameterized="true"/> + <element name="appliedTheme" type="select" selector="select[name='theme_theme_id']"/> + <element name="scopeEditLinkByName" type="button" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[normalize-space(.)= '{{scope}}']/preceding-sibling::th)+1][contains(.,'{{scopeName}}')]/..//a[contains(@class, 'action-menu-item')]" timeout="30" parameterized="true"/> </section> </sections> From 3e890e6b38574383f45114097ec99d14169bebb4 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Tue, 17 Sep 2019 16:50:38 +0300 Subject: [PATCH 0547/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../Sales/Model/Order/ShipmentTest.php | 27 +++++++++++- .../testsuite/Magento/Sales/_files/order.php | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 9c0024480d164..5b5c2365bfac4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -173,7 +173,30 @@ public function testGetTracksCollection() $shipment->addTrack($track); $this->shipmentRepository->save($shipment); - $saved = $shipment->getTracksCollection(); - self::assertTrue(in_array($track->getId(), $saved->getColumnValues('id'))); + + $secondOrder = $this->getOrder('100000002'); + $secondOrderItems = []; + foreach ($secondOrder->getItems() as $item) { + $secondOrderItems[$item->getId()] = $item->getQtyOrdered(); + } + /** @var \Magento\Sales\Model\Order\Shipment $secondOrderShipment */ + $secondOrderShipment = $this->objectManager->get(ShipmentFactory::class) + ->create($secondOrder, $secondOrderItems); + + /** @var ShipmentTrackInterface $secondShipmentTrack */ + $secondShipmentTrack = $this->objectManager->create(ShipmentTrackInterface::class); + $secondShipmentTrack->setNumber('Test Number2') + ->setTitle('Test Title2') + ->setCarrierCode('Test CODE2'); + + $secondOrderShipment->addTrack($secondShipmentTrack); + $this->shipmentRepository->save($secondOrderShipment); + + self::assertEmpty( + array_intersect( + $shipment->getTracksCollection()->getColumnValues('id'), + $secondOrderShipment->getTracksCollection()->getColumnValues('id') + ) + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php index 9ea85aae56cbb..8d2132dadd69b 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php @@ -68,3 +68,44 @@ /** @var OrderRepositoryInterface $orderRepository */ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); $orderRepository->save($order); + +/** @var Payment $payment */ +$payment2 = $objectManager->create(Payment::class); +$payment2->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem2 = $objectManager->create(OrderItem::class); +$orderItem2->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setName($product->getName()) + ->setSku($product->getSku()); + +/** @var Order $order */ +$order2 = $objectManager->create(Order::class); +$order2->setIncrementId('100000002') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order2->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem2) + ->setPayment($payment2); +$orderRepository->save($order2); From c61eb1036181f459a86dfa7ce6b51b4a10b803c6 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Tue, 17 Sep 2019 18:26:27 +0400 Subject: [PATCH 0548/2437] MC-18822: Increase test coverage for Content functional area - Automation test for MC-6192 --- .../Section/StorefrontCategorySidebarSection.xml | 1 - ...AdminCheckResultsOfColorAndOtherFiltersTest.xml | 14 +++++++------- .../Test/Mftf/Section/LayeredNavigationSection.xml | 1 + 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 0058d32dda303..406bea8d8aeab 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -8,7 +8,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> - <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> <element name="filterOptions" type="text" selector=".filter-options-content .items"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml index 2d68861b5d946..097251c844c40 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -168,19 +168,19 @@ <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="waitForCartRuleButton"/> <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="expandFirstAttribute"/> <waitForPageLoad stepKey="waitForFilterLoad"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> <waitForPageLoad stepKey="waitForAttributeOption"/> - <see userInput="{{ConfigurableProductWithAttributeSet3.name}}" stepKey="seeFirstProduct"/> - <see userInput="{{ConfigurableProductWithAttributeSet2.name}}" stepKey="seeSecondProduct"/> - <see userInput="{{ConfigurableProductWithAttributeSet1.name}}" stepKey="seeThirdProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet3.name)}}" stepKey="seeFirstProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet2.name)}}" stepKey="seeSecondProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet1.name)}}" stepKey="seeThirdProduct"/> <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> <!-- Choose Second attribute filter --> <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute2.default_frontend_label$$')}}" stepKey="expandSecondAttributeOption"/> <waitForPageLoad stepKey="waitForFilterPageLoad"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> - <see userInput="{{ConfigurableProductWithAttributeSet1.name}}" stepKey="seeFourthProduct"/> - <see userInput="{{ConfigurableProductWithAttributeSet2.name}}" stepKey="seeFifthProduct"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet1.name)}}" stepKey="seeFourthProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet2.name)}}" stepKey="seeFifthProduct"/> </test> </tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml index 1e4137beacd88..b3e0c430b12e7 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="LayeredNavigationSection"> + <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredNavigation" type="select" selector="#catalog_layered_navigation-head"/> <element name="layeredNavigationBlock" type="block" selector="#catalog_layered_navigation"/> <element name="CheckIfTabExpand" type="button" selector="#catalog_layered_navigation-head:not(.open)"/> From 3c4607344e44dcebc864ca6e9dc0309f26b0b1ee Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Tue, 17 Sep 2019 18:09:27 +0400 Subject: [PATCH 0549/2437] MC-17653: Cannot schedule update for catalog price rule for date attribute - Updated automated test script. --- .../Catalog/Test/Mftf/Section/AdminProductFormSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 80b4159167453..b2992a20814e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> + <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> <element name="attributeSetFilterResult" type="input" selector="div[data-index='attribute_set_id'] .action-menu-item._last" timeout="30"/> From 4350ca0e10454faac94f3c274972dcacdece9bde Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 17 Sep 2019 22:12:18 +0300 Subject: [PATCH 0550/2437] MC-20195: Move test MC-13104 to infrastructure --- .../product_simple_with_custom_file_option_rollback.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php index 7f53552174518..87321b2a080c0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php @@ -24,9 +24,7 @@ try { $product = $productRepository->get('simple_with_custom_file_option', false, null, true); $productRepository->delete($product); -} -catch (NoSuchEntityException $e) -{ +} catch (NoSuchEntityException $e) { } $registry->unregister('isSecureArea'); From 8f0c2252ecf8b43f725502c7badec1779e144576 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Tue, 17 Sep 2019 22:57:13 -0300 Subject: [PATCH 0551/2437] adding validation to unit test, line before return statement, and remove unused jquery/ui from validation --- .../Customer/Test/Unit/Block/Widget/DobTest.php | 12 ++++++------ .../view/base/ui_component/customer_form.xml | 12 +++++++++--- .../Customer/view/frontend/web/js/validation.js | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index 8bfddac3cef8f..da5e089f93ecb 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -521,8 +521,8 @@ public function testGetHtmlExtraParamsWithoutRequiredOption() { $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"validate-date":{"dateFormat":"M\/d\/Y"}}') - ->will($this->returnValue('{"validate-date":{"dateFormat":"M\/d\/Y"}}')); + ->with('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') + ->will($this->returnValue('{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}')); $this->attribute->expects($this->once()) ->method("isRequired") @@ -530,7 +530,7 @@ public function testGetHtmlExtraParamsWithoutRequiredOption() $this->assertEquals( $this->_block->getHtmlExtraParams(), - 'data-validate="{"validate-date":{"dateFormat":"M\/d\/Y"}}"' + 'data-validate="{"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"' ); } @@ -544,13 +544,13 @@ public function testGetHtmlExtraParamsWithRequiredOption() ->willReturn(true); $this->escaper->expects($this->any()) ->method('escapeHtml') - ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"}}') - ->will($this->returnValue('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"}}')); + ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') + ->will($this->returnValue('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}')); $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($this->escaper)); $this->assertEquals( - 'data-validate="{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"}}"', + 'data-validate="{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}"', $this->_block->getHtmlExtraParams() ); } diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 7eb1394f77a41..954b44ec19bbb 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -260,9 +260,6 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> - <item name="options" xsi:type="array"> - <item name="maxDate" xsi:type="string">-1d</item> - </item> </item> </argument> <settings> @@ -273,6 +270,15 @@ <dataType>text</dataType> <visible>true</visible> </settings> + <formElements> + <date> + <settings> + <options> + <option name="maxDate" xsi:type="string">-1d</option> + </options> + </settings> + </date> + </formElements> </field> <field name="taxvat" formElement="input"> <argument name="data" xsi:type="array"> diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 840134d64422e..14ae69b020809 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -1,7 +1,6 @@ require([ 'jquery', 'moment', - 'jquery/ui', 'jquery/validate', 'mage/translate', ], function($, moment) { @@ -12,6 +11,7 @@ require([ if (value === '') { return true; } + return moment(value).isBefore(moment()); }, $.mage.__('The Date of Birth should not be greater than today.') From 49cfa88c88462f946715b6f1d4065d0609729b72 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 18 Sep 2019 11:20:19 +0300 Subject: [PATCH 0552/2437] graphQl-892: fixed billing address return implementation --- .../Model/Resolver/BillingAddress.php | 8 ++++++- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- .../GetSpecifiedBillingAddressTest.php | 24 +------------------ .../Guest/GetSpecifiedBillingAddressTest.php | 23 +----------------- 4 files changed, 10 insertions(+), 47 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php index a6bb0b0d04df1..c88a0d524c509 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php @@ -44,7 +44,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $value['model']; $billingAddress = $cart->getBillingAddress(); - if (null === $billingAddress) { + if (null === $billingAddress || + null === $billingAddress->getFirstname() || + null === $billingAddress->getLastname() || + null === $billingAddress->getCity() || + null === $billingAddress->getCountry() || + null === $billingAddress->getTelephone() || + null === $billingAddress->getStreet()) { return null; } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 009b18e29c05b..46ee6ee335252 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -194,7 +194,7 @@ type Cart { applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon") email: String @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartEmail") shipping_addresses: [ShippingCartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") - billing_address: BillingCartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") + billing_address: BillingCartAddress @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") available_payment_methods: [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") selected_payment_method: SelectedPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SelectedPaymentMethod") prices: CartPrices @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartPrices") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedBillingAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedBillingAddressTest.php index b6dde46871cbb..e5353fc841c5d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedBillingAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSpecifiedBillingAddressTest.php @@ -91,29 +91,7 @@ public function testGetSpecifiedBillingAddressIfBillingAddressIsNotSet() $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); self::assertArrayHasKey('billing_address', $response['cart']); - - $expectedBillingAddressData = [ - 'firstname' => null, - 'lastname' => null, - 'company' => null, - 'street' => [ - '' - ], - 'city' => null, - 'region' => [ - 'code' => null, - 'label' => null, - ], - 'postcode' => null, - 'country' => [ - 'code' => null, - 'label' => null, - ], - 'telephone' => null, - '__typename' => 'BillingCartAddress', - 'customer_notes' => null, - ]; - self::assertEquals($expectedBillingAddressData, $response['cart']['billing_address']); + self::assertNull($response['cart']['billing_address']); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSpecifiedBillingAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSpecifiedBillingAddressTest.php index 8ddf1641ede8a..cb1565879a81e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSpecifiedBillingAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSpecifiedBillingAddressTest.php @@ -81,28 +81,7 @@ public function testGetSpecifiedBillingAddressIfBillingAddressIsNotSet() $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); self::assertArrayHasKey('billing_address', $response['cart']); - - $expectedBillingAddressData = [ - 'firstname' => null, - 'lastname' => null, - 'company' => null, - 'street' => [ - '' - ], - 'city' => null, - 'region' => [ - 'code' => null, - 'label' => null, - ], - 'postcode' => null, - 'country' => [ - 'code' => null, - 'label' => null, - ], - 'telephone' => null, - '__typename' => 'BillingCartAddress', - ]; - self::assertEquals($expectedBillingAddressData, $response['cart']['billing_address']); + self::assertNull($response['cart']['billing_address']); } /** From b5bc8480ca7becfe4d1cb30b98866e9ff683bb5d Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Wed, 18 Sep 2019 11:33:50 +0300 Subject: [PATCH 0553/2437] magento/magento2#22297 Fix elasticsearch issue over secure connection --- .../Model/Client/Elasticsearch.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php index 34129a5af0012..e4018196c845d 100644 --- a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -103,21 +103,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } From dcdf3643a30612d879aa4d8ff4927782046e2f1e Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Wed, 18 Sep 2019 11:34:31 +0300 Subject: [PATCH 0554/2437] magento/magento2#22297 Fix elasticsearch issue over secure connection Apply changes for ES5 client --- .../Model/Client/Elasticsearch.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index 93f4caa10adf9..b9102bc5e00c4 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -108,21 +108,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } From 2c46bd50269d99fbf3f1837b7ff8adc10cc8105d Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Wed, 18 Sep 2019 11:34:57 +0300 Subject: [PATCH 0555/2437] magento/magento2#22297 Fix elasticsearch issue over secure connection Apply changes for default ES client --- .../Model/Client/Elasticsearch.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php index f9b827304446d..d933d8bb5d0b5 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php @@ -103,21 +103,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } From 103cef8e9676c1a3e835bc8bb6d0c283df45aa3a Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 18 Sep 2019 14:50:52 +0300 Subject: [PATCH 0556/2437] MC-17149: UI components configuration incorrect (overlapping text labels) --- .../Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 3ea6a3cdfe656..7c86d1604943b 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -203,7 +203,7 @@ protected function preparePriceFields($fieldCode) * * @return $this */ - private function customizePrice() + private function customizePrice(): self { $pathFrom = $this->arrayManager->findPath('price', $this->meta, null, 'children'); From 095d7075a9f2c7267233f0f1ecbce2abb559eab1 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 18 Sep 2019 16:17:45 +0300 Subject: [PATCH 0557/2437] graphQl-903: deprecated use_for_shipping in billing address schema --- .../Model/Cart/AssignBillingAddressToCart.php | 6 +++--- .../Model/Cart/SetBillingAddressOnCart.php | 10 +++++----- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 3 ++- .../Quote/Customer/SetBillingAddressOnCartTest.php | 12 ++++++------ .../Quote/Guest/SetBillingAddressOnCartTest.php | 12 ++++++------ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php index dd6478b4873c6..64a5a16cb84a2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php @@ -39,17 +39,17 @@ public function __construct( * * @param CartInterface $cart * @param AddressInterface $billingAddress - * @param bool $useForShipping + * @param bool $sameAsShipping * @throws GraphQlInputException * @throws GraphQlNoSuchEntityException */ public function execute( CartInterface $cart, AddressInterface $billingAddress, - bool $useForShipping + bool $sameAsShipping ): void { try { - $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $useForShipping); + $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $sameAsShipping); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); } catch (LocalizedException $e) { diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 673debefd0874..08aeb56e4cd09 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -56,8 +56,8 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; - $useForShipping = isset($billingAddressInput['use_for_shipping']) - ? (bool)$billingAddressInput['use_for_shipping'] : false; + $sameAsshipping = isset($billingAddressInput['same_as_shipping']) + ? (bool)$billingAddressInput['same_as_shipping'] : false; if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( @@ -72,15 +72,15 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b } $addresses = $cart->getAllShippingAddresses(); - if ($useForShipping && count($addresses) > 1) { + if ($sameAsshipping && count($addresses) > 1) { throw new GraphQlInputException( - __('Using the "use_for_shipping" option with multishipping is not possible.') + __('Using the "same_as_shipping" option with multishipping is not possible.') ); } $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); - $this->assignBillingAddressToCart->execute($cart, $billingAddress, $useForShipping); + $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsshipping); } /** diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index a86eea46aa864..ae0a1bc34866a 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -96,7 +96,8 @@ input SetBillingAddressOnCartInput { input BillingAddressInput { customer_address_id: Int address: CartAddressInput - use_for_shipping: Boolean + use_for_shipping: Boolean @doc(description: "Deprecated. Use same_as_shipping") + same_as_shipping: Boolean } input CartAddressInput { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 011930e723273..ec4ab012d37dc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -120,7 +120,7 @@ public function testSetNewBillingAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ - public function testSetNewBillingAddressWithUseForShippingParameter() + public function testSetNewBillingAddressWithSameAsShippingParameter() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -142,7 +142,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() telephone: "88776655" save_in_address_book: false } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -337,7 +337,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() input: { cart_id: "$maskedQuoteId" billing_address: { - use_for_shipping: true + same_as_shipping: true } } ) { @@ -363,7 +363,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_multishipping_with_two_shipping_addresses.php */ - public function testSetNewBillingAddressWithUseForShippingAndMultishipping() + public function testSetNewBillingAddressWithSameAsShippingAndMultishipping() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -385,7 +385,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() telephone: "88776655" save_in_address_book: false } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -399,7 +399,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() QUERY; self::expectExceptionMessage( - 'Using the "use_for_shipping" option with multishipping is not possible.' + 'Using the "same_as_shipping" option with multishipping is not possible.' ); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 730e65b4ba8aa..8e500510494c2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -90,7 +90,7 @@ public function testSetNewBillingAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ - public function testSetNewBillingAddressWithUseForShippingParameter() + public function testSetNewBillingAddressWithSameAsShippingParameter() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -112,7 +112,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() telephone: "88776655" save_in_address_book: false } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -346,7 +346,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() input: { cart_id: "$maskedQuoteId" billing_address: { - use_for_shipping: true + same_as_shipping: true } } ) { @@ -371,7 +371,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_multishipping_with_two_shipping_addresses.php */ - public function testSetNewBillingAddressWithUseForShippingAndMultishipping() + public function testSetNewBillingAddressWithSameAsShippingAndMultishipping() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -393,7 +393,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() telephone: "88776655" save_in_address_book: false } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -407,7 +407,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() QUERY; self::expectExceptionMessage( - 'Using the "use_for_shipping" option with multishipping is not possible.' + 'Using the "same_as_shipping" option with multishipping is not possible.' ); $this->graphQlMutation($query); } From 5f33b53d11a44c9b0af75c0932958e235b08ccb3 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 18 Sep 2019 16:37:07 +0300 Subject: [PATCH 0558/2437] Working on the test --- .../AdminUpdateCmsPageRewriteEntityTest.xml | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml new file mode 100644 index 0000000000000..07ec8e0fd71a2 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageRewriteEntityTest"> + <annotations> + <stories value="Update CMS Page URL Redirect"/> + <title value="Update CMS Page URL Redirect"/> + <description value="Login as Admin and tried to update the create URL Rewrite for CMS page"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create Custom Store View--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"/> + + <!-- Open CMS Edit Page, Get the CMS ID and Modify Store View Option to All Store Views --> + <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + <actionGroup ref="AddStoreViewToCmsPage" stepKey="updateStoreViewForCmsPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + <argument name="storeViewName" value="All Store Views"/> + </actionGroup> + + <!--Create CMS Page URL Redirect--> + <actionGroup ref="AdminAddCustomUrlRewrite" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="created-new-cms-page"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + <argument name="description" value="Created New CMS Page"/> + </actionGroup> + + <!--Search created CMS page url rewrite in grid--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewrite"> + <argument name="requestPath" value="created-new-cms-page"/> + </actionGroup> + + <!-- Update URL Rewrite for CMS Page First Attempt --> + <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateUrlRewriteFirstAttempt"> + <argument name="storeValue" value="{{customStore.name}}"/> + <argument name="requestPath" value="newrequestpath"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="test_description_custom_store"/> + </actionGroup> + + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <!--<amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/>--> + <!--<waitForPageLoad stepKey="waitForStorefrontPageLoad"/>--> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> + <argument name="storeView" value="customStore"/> + </actionGroup> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> + <argument name="page" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> + + </test> +</tests> From 944fa474707066d7147b63968e9219dedfa3ea73 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Wed, 18 Sep 2019 16:39:39 +0300 Subject: [PATCH 0559/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Cart/Address/SaveQuoteAddressToCustomerAddressBook.php | 2 ++ .../QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php | 4 +++- .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 1 - .../GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php | 4 ++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php index 543fe96ddec8b..5c773d44e6a1d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php @@ -52,6 +52,8 @@ public function __construct( } /** + * Save Address to Customer Address Book. + * * @param QuoteAddress $quoteAddress * @param int $customerId * diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index f2e639f74cc20..ccb35e884061c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -78,12 +78,14 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } - $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); + $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); $this->assignShippingAddressToCart->execute($cart, $shippingAddress); } /** + * Create shipping address. + * * @param ContextInterface $context * @param int|null $customerAddressId * @param array|null $addressInput diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 9a9c50c140571..40ec237c8b233 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -709,7 +709,6 @@ public function testSetBillingAddressWithLowerCaseCountry() $this->assertNewAddressFields($billingAddressResponse); } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index a1afc0677429b..95c8f4118adb9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -700,6 +700,7 @@ public function testSetNewShippingAddressWithSaveInAddressBook() telephone: "88776655" save_in_address_book: true } + customer_notes: "Test note" } ] } @@ -718,6 +719,7 @@ public function testSetNewShippingAddressWithSaveInAddressBook() label } __typename + customer_notes } } } @@ -769,6 +771,7 @@ public function testSetNewShippingAddressWithNotSaveInAddressBook() telephone: "88776655" save_in_address_book: false } + customer_notes: "Test note" } ] } @@ -787,6 +790,7 @@ public function testSetNewShippingAddressWithNotSaveInAddressBook() label } __typename + customer_notes } } } From 1334d8ad1a7e3bb07468880f7fb4a40dc3087eb7 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Wed, 18 Sep 2019 16:51:58 +0300 Subject: [PATCH 0560/2437] Update the test --- .../AdminUpdateCmsPageRewriteEntityTest.xml | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml index 07ec8e0fd71a2..40a38dc689241 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml @@ -66,8 +66,6 @@ <argument name="message" value="The URL Rewrite has been saved."/> </actionGroup> - <!--<amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/>--> - <!--<waitForPageLoad stepKey="waitForStorefrontPageLoad"/>--> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> <argument name="storeView" value="customStore"/> @@ -83,9 +81,38 @@ <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> </actionGroup> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <!--Search created CMS page url rewrite in grid--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewriteSecondAttempt"> <argument name="requestPath" value="newrequestpath"/> </actionGroup> + <!-- Update URL Rewrite for CMS Page Second Attempt --> + <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateUrlRewriteSecondAttempt"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="temporaryrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="test description_302"/> + </actionGroup> + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondAttempt"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepageSecondAttempt"/> + <actionGroup ref="StorefrontSwitchDefaultStoreViewActionGroup" stepKey="switchToDefualtStoreView"/> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFrontSecondAttempt"> + <argument name="page" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPageSecondAttempt"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> </test> From 5ccd367ebc628b588a77cb2032faeabd6005b039 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 18 Sep 2019 17:07:38 +0300 Subject: [PATCH 0561/2437] MC-20193: Invoice Sales Email not send --- .../Magento/Quote/Observer/SubmitObserver.php | 16 +++- .../Test/Unit/Observer/SubmitObserverTest.php | 73 ++++++++++++++++++- .../Adminhtml/Order/Invoice/Save.php | 15 +++- .../Adminhtml/Order/Invoice/SaveTest.php | 2 +- 4 files changed, 98 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Quote/Observer/SubmitObserver.php b/app/code/Magento/Quote/Observer/SubmitObserver.php index 1213636e5966b..2f57bf4e4ff07 100644 --- a/app/code/Magento/Quote/Observer/SubmitObserver.php +++ b/app/code/Magento/Quote/Observer/SubmitObserver.php @@ -5,8 +5,10 @@ */ namespace Magento\Quote\Observer; +use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Framework\Event\ObserverInterface; +use Magento\Sales\Model\Order\Invoice; class SubmitObserver implements ObserverInterface { @@ -20,16 +22,23 @@ class SubmitObserver implements ObserverInterface */ private $orderSender; + /** + * @var InvoiceSender + */ + private $invoiceSender; + /** * @param \Psr\Log\LoggerInterface $logger * @param OrderSender $orderSender */ public function __construct( \Psr\Log\LoggerInterface $logger, - OrderSender $orderSender + OrderSender $orderSender, + InvoiceSender $invoiceSender ) { $this->logger = $logger; $this->orderSender = $orderSender; + $this->invoiceSender = $invoiceSender; } /** @@ -51,6 +60,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) if (!$redirectUrl && $order->getCanSendNewEmailFlag()) { try { $this->orderSender->send($order); + foreach ($order->getInvoiceCollection()->getItems() as $invoice) { + if ($invoice->getState() === Invoice::STATE_PAID) { + $this->invoiceSender->send($invoice); + } + } } catch (\Exception $e) { $this->logger->critical($e); } diff --git a/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php index c19606a7b8f5d..f0ce3132bc66b 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php @@ -5,6 +5,11 @@ */ namespace Magento\Quote\Test\Unit\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; +use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection; + class SubmitObserverTest extends \PHPUnit\Framework\TestCase { /** @@ -42,6 +47,21 @@ class SubmitObserverTest extends \PHPUnit\Framework\TestCase */ protected $paymentMock; + /** + * @var InvoiceSender|\PHPUnit_Framework_MockObject_MockObject + */ + private $invoiceSenderMock; + + /** + * @var Invoice|\PHPUnit_Framework_MockObject_MockObject + */ + private $invoiceMock; + + /** + * @var InvoiceCollection|\PHPUnit_Framework_MockObject_MockObject + */ + private $invoiceCollectionMock; + protected function setUp() { $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); @@ -59,9 +79,18 @@ protected function setUp() $eventMock->expects($this->once())->method('getQuote')->willReturn($this->quoteMock); $eventMock->expects($this->once())->method('getOrder')->willReturn($this->orderMock); $this->quoteMock->expects($this->once())->method('getPayment')->willReturn($this->paymentMock); - $this->model = new \Magento\Quote\Observer\SubmitObserver( - $this->loggerMock, - $this->orderSenderMock + $this->invoiceSenderMock = $this->createMock(InvoiceSender::class); + $this->invoiceMock = $this->createMock(Invoice::class); + $this->invoiceCollectionMock = $this->createMock(InvoiceCollection::class); + $objectManager = new ObjectManager($this); + + $this->model = $objectManager->getObject( + \Magento\Quote\Observer\SubmitObserver::class, + [ + 'logger' => $this->loggerMock, + 'orderSender' => $this->orderSenderMock, + 'invoiceSender' => $this->invoiceSenderMock, + ] ); } @@ -70,6 +99,10 @@ public function testSendEmail() $this->paymentMock->expects($this->once())->method('getOrderPlaceRedirectUrl')->willReturn(''); $this->orderMock->expects($this->once())->method('getCanSendNewEmailFlag')->willReturn(true); $this->orderSenderMock->expects($this->once())->method('send')->willReturn(true); + $this->orderMock->expects($this->once()) + ->method('getInvoiceCollection') + ->willReturn($this->invoiceCollectionMock); + $this->invoiceCollectionMock->expects($this->once())->method('getItems')->willReturn([]); $this->loggerMock->expects($this->never())->method('critical'); $this->model->execute($this->observerMock); } @@ -93,4 +126,38 @@ public function testSendEmailWhenRedirectUrlExists() $this->loggerMock->expects($this->never())->method('critical'); $this->model->execute($this->observerMock); } + + public function testSendEmailWithPaidInvoice() + { + $this->prepareDataForSendInvoice(); + $this->invoiceMock->expects($this->once())->method('getState')->willReturn(Invoice::STATE_PAID); + $this->invoiceSenderMock->expects($this->once()) + ->method('send') + ->with($this->invoiceMock) + ->willReturn(true); + $this->loggerMock->expects($this->never())->method('critical'); + + $this->model->execute($this->observerMock); + } + + public function testSendEmailWithNotPaidInvoice() + { + $this->prepareDataForSendInvoice(); + $this->invoiceMock->expects($this->once())->method('getState')->willReturn(Invoice::STATE_OPEN); + $this->invoiceSenderMock->expects($this->never())->method('send'); + $this->loggerMock->expects($this->never())->method('critical'); + + $this->model->execute($this->observerMock); + } + + private function prepareDataForSendInvoice() + { + $this->paymentMock->expects($this->once())->method('getOrderPlaceRedirectUrl')->willReturn(''); + $this->orderMock->expects($this->once())->method('getCanSendNewEmailFlag')->willReturn(true); + $this->orderSenderMock->expects($this->once())->method('send')->willReturn(true); + $this->orderMock->expects($this->once()) + ->method('getInvoiceCollection') + ->willReturn($this->invoiceCollectionMock); + $this->invoiceCollectionMock->expects($this->once())->method('getItems')->willReturn([$this->invoiceMock]); + } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php index 67a0dc469163b..12f9712adb56a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php @@ -16,6 +16,7 @@ use Magento\Sales\Model\Order\ShipmentFactory; use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Service\InvoiceService; +use Magento\Sales\Helper\Data as SalesData; /** * Save invoice controller. @@ -56,6 +57,11 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac */ private $invoiceService; + /** + * @var SalesData + */ + private $salesData; + /** * @param Action\Context $context * @param Registry $registry @@ -63,6 +69,7 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac * @param ShipmentSender $shipmentSender * @param ShipmentFactory $shipmentFactory * @param InvoiceService $invoiceService + * @param SalesData $salesData */ public function __construct( Action\Context $context, @@ -70,7 +77,8 @@ public function __construct( InvoiceSender $invoiceSender, ShipmentSender $shipmentSender, ShipmentFactory $shipmentFactory, - InvoiceService $invoiceService + InvoiceService $invoiceService, + SalesData $salesData = null ) { $this->registry = $registry; $this->invoiceSender = $invoiceSender; @@ -78,6 +86,7 @@ public function __construct( $this->shipmentFactory = $shipmentFactory; $this->invoiceService = $invoiceService; parent::__construct($context); + $this->salesData = $salesData ?? $this->_objectManager->get(SalesData::class); } /** @@ -199,7 +208,7 @@ public function execute() // send invoice/shipment emails try { - if (!empty($data['send_email'])) { + if (!empty($data['send_email']) || $this->salesData->canSendNewInvoiceEmail()) { $this->invoiceSender->send($invoice); } } catch (\Exception $e) { @@ -208,7 +217,7 @@ public function execute() } if ($shipment) { try { - if (!empty($data['send_email'])) { + if (!empty($data['send_email']) || $this->salesData->canSendNewShipmentEmail()) { $this->shipmentSender->send($shipment); } } catch (\Exception $e) { diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index 40540f3126899..2dc5f5adc86d2 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -28,7 +28,7 @@ class SaveTest extends AbstractInvoiceControllerTest */ public function testSendEmailOnInvoiceSave(): void { - $order = $this->prepareRequest(['invoice' => ['send_email' => true]]); + $order = $this->prepareRequest(); $this->dispatch('backend/sales/order_invoice/save'); $this->assertSessionMessages( From 9b2b07568a42748e1a27e09d32db05fc867500ba Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 18 Sep 2019 17:10:06 +0300 Subject: [PATCH 0562/2437] MC-17149: UI components configuration incorrect (overlapping text labels) --- .../Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 7c86d1604943b..b8164eeb459d6 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -203,7 +203,7 @@ protected function preparePriceFields($fieldCode) * * @return $this */ - private function customizePrice(): self + private function customizePrice(): AdvancedPricing { $pathFrom = $this->arrayManager->findPath('price', $this->meta, null, 'children'); From 87fa1f3334390deeb16aa77559679e8c69051628 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 18 Sep 2019 10:31:51 -0500 Subject: [PATCH 0563/2437] enable elastic --- app/code/Magento/CatalogSearch/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index 7ea15c6caa590..911d9da2fa5e6 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -12,7 +12,7 @@ <search_terms>1</search_terms> </seo> <search> - <engine>mysql</engine> + <engine>elasticsearch6</engine> <min_query_length>3</min_query_length> <max_query_length>128</max_query_length> <max_count_cacheable_search_terms>100</max_count_cacheable_search_terms> From 953e034762cb79c5a187426f979cceeec2617b11 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 18 Sep 2019 18:53:02 +0300 Subject: [PATCH 0564/2437] MC-20193: Invoice Sales Email not send --- app/code/Magento/Quote/Observer/SubmitObserver.php | 6 ++++++ .../Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Quote/Observer/SubmitObserver.php b/app/code/Magento/Quote/Observer/SubmitObserver.php index 2f57bf4e4ff07..9a3217cc7d6b6 100644 --- a/app/code/Magento/Quote/Observer/SubmitObserver.php +++ b/app/code/Magento/Quote/Observer/SubmitObserver.php @@ -10,6 +10,9 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Sales\Model\Order\Invoice; +/** + * Class responsive for sending order and invoice emails when it's created through storefront. + */ class SubmitObserver implements ObserverInterface { /** @@ -30,6 +33,7 @@ class SubmitObserver implements ObserverInterface /** * @param \Psr\Log\LoggerInterface $logger * @param OrderSender $orderSender + * @param InvoiceSender $invoiceSender */ public function __construct( \Psr\Log\LoggerInterface $logger, @@ -42,6 +46,8 @@ public function __construct( } /** + * Send order and invoice email. + * * @param \Magento\Framework\Event\Observer $observer * * @return void diff --git a/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php index f0ce3132bc66b..5c106876a2c13 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/SubmitObserverTest.php @@ -10,6 +10,10 @@ use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection; +/** + * Test for \Magento\Quote\Observer\SubmitObserver class. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class SubmitObserverTest extends \PHPUnit\Framework\TestCase { /** From 06ebc664bb2351e7128f4f8e55e1014e5d548f4e Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Wed, 18 Sep 2019 11:41:42 -0500 Subject: [PATCH 0565/2437] MC-20235: [2.3.3]Revert unapproved PR --- .../Adminhtml/Promo/Catalog/Save.php | 18 ++++-------------- .../Model/Indexer/ReindexRuleProduct.php | 4 +--- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 6d499b93e411f..4f58293d53359 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -12,7 +12,6 @@ use Magento\Framework\Registry; use Magento\Framework\Stdlib\DateTime\Filter\Date; use Magento\Framework\App\Request\DataPersistorInterface; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Save action for catalog rule @@ -26,27 +25,19 @@ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog imple */ protected $dataPersistor; - /** - * @var TimezoneInterface - */ - private $localeDate; - /** * @param Context $context * @param Registry $coreRegistry * @param Date $dateFilter * @param DataPersistorInterface $dataPersistor - * @param TimezoneInterface $localeDate */ public function __construct( Context $context, Registry $coreRegistry, Date $dateFilter, - DataPersistorInterface $dataPersistor, - TimezoneInterface $localeDate + DataPersistorInterface $dataPersistor ) { $this->dataPersistor = $dataPersistor; - $this->localeDate = $localeDate; parent::__construct($context, $coreRegistry, $dateFilter); } @@ -55,15 +46,16 @@ public function __construct( * * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { if ($this->getRequest()->getPostValue()) { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ $ruleRepository = $this->_objectManager->get( \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class ); + /** @var \Magento\CatalogRule\Model\Rule $model */ $model = $this->_objectManager->create(\Magento\CatalogRule\Model\Rule::class); @@ -73,9 +65,7 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); - if (!$this->getRequest()->getParam('from_date')) { - $data['from_date'] = $this->localeDate->formatDate(); - } + $filterValues = ['from_date' => $this->_dateFilter]; if ($this->getRequest()->getParam('to_date')) { $filterValues['to_date'] = $this->_dateFilter; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index 944710773123f..e589c8595ce2c 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -101,9 +101,7 @@ public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) $scopeTz = new \DateTimeZone( $this->localeDate->getConfigTimezone(ScopeInterface::SCOPE_WEBSITE, $websiteId) ); - $fromTime = $rule->getFromDate() - ? (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp() - : 0; + $fromTime = (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp(); $toTime = $rule->getToDate() ? (new \DateTime($rule->getToDate(), $scopeTz))->getTimestamp() + IndexBuilder::SECONDS_IN_DAY - 1 : 0; From f92ba2213fefcb3e75299894cd49d75a4efe32aa Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 18 Sep 2019 13:25:01 -0500 Subject: [PATCH 0566/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Model/Indexer/ProductPriceIndexFilter.php | 83 +++++++++---------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index f9a49d4f8d121..170710ce09698 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -9,11 +9,11 @@ use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\CatalogInventory\Model\ResourceModel\Stock\Item; +use Magento\CatalogInventory\Model\Stock; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceModifierInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure; use Magento\Framework\App\ResourceConnection; use Magento\Framework\App\ObjectManager; -use Magento\Framework\DB\Query\Generator; /** * Class for filter product price index. @@ -40,38 +40,22 @@ class ProductPriceIndexFilter implements PriceModifierInterface */ private $connectionName; - /** - * @var Generator - */ - private $batchQueryGenerator; - - /** - * @var int - */ - private $batchSize; - /** * @param StockConfigurationInterface $stockConfiguration * @param Item $stockItem * @param ResourceConnection $resourceConnection * @param string $connectionName - * @param Generator $batchQueryGenerator - * @param int $batchSize */ public function __construct( StockConfigurationInterface $stockConfiguration, Item $stockItem, ResourceConnection $resourceConnection = null, - $connectionName = 'indexer', - Generator $batchQueryGenerator = null, - $batchSize = 100 + $connectionName = 'indexer' ) { $this->stockConfiguration = $stockConfiguration; $this->stockItem = $stockItem; $this->resourceConnection = $resourceConnection ?: ObjectManager::getInstance()->get(ResourceConnection::class); $this->connectionName = $connectionName; - $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get(Generator::class); - $this->batchSize = $batchSize; } /** @@ -80,9 +64,7 @@ public function __construct( * @param IndexTableStructure $priceTable * @param array $entityIds * @return void - * * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = []) : void { @@ -91,38 +73,47 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = } $connection = $this->resourceConnection->getConnection($this->connectionName); - $select = $connection->select(); - - $select->from( - ['stock_item' => $this->stockItem->getMainTable()], - ['stock_item.product_id', 'MAX(stock_item.is_in_stock) as max_is_in_stock'] - ); + $stockSelect = $connection->select(); if ($this->stockConfiguration->getManageStock()) { - $select->where('stock_item.use_config_manage_stock = 1 OR stock_item.manage_stock = 1'); + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 0', + Stock::STOCK_IN_STOCK, + 'is_in_stock' + ); } else { - $select->where('stock_item.use_config_manage_stock = 0 AND stock_item.manage_stock = 1'); + $stockStatus = $connection->getCheckSql( + 'use_config_manage_stock = 0 AND manage_stock = 1', + 'is_in_stock', + Stock::STOCK_IN_STOCK + ); } + $stockStatus = new \Zend_Db_Expr('MAX(' . $stockStatus . ')'); + $stockSelect->from( + $this->stockItem->getMainTable(), + [ + 'product_id' => 'product_id', + 'stock_status' => $stockStatus, + ] + ); + if (!empty($entityIds)) { + $stockSelect->where('product_id IN (?)', $entityIds); + } + $stockSelect->group('product_id'); - $select->group('stock_item.product_id'); - $select->having('max_is_in_stock = 0'); - - $batchSelectIterator = $this->batchQueryGenerator->generate( - 'product_id', - $select, - $this->batchSize, - \Magento\Framework\DB\Query\BatchIteratorInterface::UNIQUE_FIELD_ITERATOR + $select = $connection->select(); + $select->from( + ['price_index' => $priceTable->getTableName()], + [] ); + $select->joinInner( + ['stock_item' => $stockSelect], + 'stock_item.product_id = price_index.' . $priceTable->getEntityField(), + [] + ); + $select->where('stock_item.stock_status = ?', Stock::STOCK_OUT_OF_STOCK); - foreach ($batchSelectIterator as $select) { - $productIds = null; - foreach ($connection->query($select)->fetchAll() as $row) { - $productIds[] = $row['product_id']; - } - if ($productIds !== null) { - $where = [$priceTable->getEntityField() .' IN (?)' => $productIds]; - $connection->delete($priceTable->getTableName(), $where); - } - } + $query = $select->deleteFromSelect('price_index'); + $connection->query($query); } } From bacaf137b0fd15c20aad7395feb92b4b15b5ad96 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 18 Sep 2019 13:47:13 -0500 Subject: [PATCH 0567/2437] MC-19451: Adapt functional tests to Elasticsearch * Configuring Elasticsearch as default search engine --- app/code/Magento/CatalogSearch/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index 7ea15c6caa590..911d9da2fa5e6 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -12,7 +12,7 @@ <search_terms>1</search_terms> </seo> <search> - <engine>mysql</engine> + <engine>elasticsearch6</engine> <min_query_length>3</min_query_length> <max_query_length>128</max_query_length> <max_count_cacheable_search_terms>100</max_count_cacheable_search_terms> From bfbbe0ebf39617f0a2b75d032f339c1937e06866 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Wed, 18 Sep 2019 18:47:21 -0300 Subject: [PATCH 0568/2437] adjusting PHP CS for 120 lines and invalid quote at validation.js --- app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php | 4 +++- app/code/Magento/Customer/view/frontend/web/js/validation.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index da5e089f93ecb..f92545b0b7676 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -545,7 +545,9 @@ public function testGetHtmlExtraParamsWithRequiredOption() $this->escaper->expects($this->any()) ->method('escapeHtml') ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') - ->will($this->returnValue('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}')); + ->will($this->returnValue( + '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' + )); $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($this->escaper)); diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 14ae69b020809..676fd18d26820 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -6,7 +6,7 @@ require([ ], function($, moment) { 'use strict'; $.validator.addMethod( - "validate-dob", + 'validate-dob', function (value) { if (value === '') { return true; From c60896cabe8bb7610401cf05ae5504345c2a076d Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 18 Sep 2019 17:01:36 -0500 Subject: [PATCH 0569/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- app/code/Magento/CatalogRule/etc/indexer.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogRule/etc/indexer.xml b/app/code/Magento/CatalogRule/etc/indexer.xml index e648ea567631c..340918ed63531 100644 --- a/app/code/Magento/CatalogRule/etc/indexer.xml +++ b/app/code/Magento/CatalogRule/etc/indexer.xml @@ -17,6 +17,7 @@ <indexer id="catalog_product_price"> <dependencies> <indexer id="catalogrule_rule" /> + <indexer id="catalogrule_product" /> </dependencies> </indexer> </config> From db5e65f5489ade4f4bfa024975ef6e78d1ae2c8a Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Wed, 18 Sep 2019 19:15:32 -0300 Subject: [PATCH 0570/2437] PHP CS fixing --- .../Magento/Customer/Test/Unit/Block/Widget/DobTest.php | 8 +++++--- .../Magento/Customer/view/frontend/web/js/validation.js | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index f92545b0b7676..9f49cbdce1f96 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -9,13 +9,13 @@ use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\ValidationRuleInterface; +use Magento\Customer\Block\Widget\Dob; use Magento\Customer\Helper\Address; use Magento\Framework\App\CacheInterface; use Magento\Framework\Cache\FrontendInterface; use Magento\Framework\Data\Form\FilterFactory; use Magento\Framework\Escaper; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Customer\Block\Widget\Dob; use Magento\Framework\Locale\Resolver; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\Stdlib\DateTime\Timezone; @@ -545,9 +545,11 @@ public function testGetHtmlExtraParamsWithRequiredOption() $this->escaper->expects($this->any()) ->method('escapeHtml') ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') - ->will($this->returnValue( + ->will( + $this->returnValue( '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' - )); + ) + ); $this->context->expects($this->any())->method('getEscaper')->will($this->returnValue($this->escaper)); diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 676fd18d26820..a6c058236a107 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -5,6 +5,7 @@ require([ 'mage/translate', ], function($, moment) { 'use strict'; + $.validator.addMethod( 'validate-dob', function (value) { From b300c94f4dc39c60c3421db4531fdeb2fe277221 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Wed, 18 Sep 2019 11:25:09 -0500 Subject: [PATCH 0571/2437] MC-19870: Update Regions list for China country - Add data patch for china regions --- .../Customer/Test/Mftf/Data/AddressData.xml | 15 +++ ...orefrontUpdateCustomerAddressChinaTest.xml | 51 ++++++++ .../Setup/Patch/Data/AddDataForChina.php | 117 ++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForChina.php diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4d7a39b3246e1..437eea10994d9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -239,6 +239,21 @@ <data key="state">Côtes-d'Armor</data> <data key="postcode">12345</data> </entity> + <entity name="updateCustomerChinaAddress" type="address"> + <data key="firstname">Xian</data> + <data key="lastname">Shai</data> + <data key="company">Hunan Fenmian</data> + <data key="telephone">+86 851 8410 4337</data> + <array key="street"> + <item>Nanyuan Rd, Wudang</item> + <item>Hunan Fenmian</item> + </array> + <data key="country_id">CN</data> + <data key="country">China</data> + <data key="city">Guiyang</data> + <data key="state">Guizhou Sheng</data> + <data key="postcode">550002</data> + </entity> <entity name="updateCustomerNoXSSInjection" type="address"> <data key="firstname">Jany</data> <data key="lastname">Doe</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml new file mode 100644 index 0000000000000..285de8d777b48 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressChinaTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerAddressChinaTest"> + <annotations> + <stories value="Update Regions list for China country"/> + <title value="Update customer address on storefront with china address"/> + <description value="Update customer address on storefront with china address and verify you can select a region"/> + <testCaseId value="MC-20234"/> + <severity value="AVERAGE"/> + <group value="customer"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + </before> + <after> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Update customer address in storefront--> + <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <argument name="Address" value="updateCustomerChinaAddress"/> + </actionGroup> + <!--Verify customer address save success message--> + <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> + + <!--Verify customer default billing address--> + <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <argument name="address" value="updateCustomerChinaAddress"/> + </actionGroup> + + <!--Verify customer default shipping address--> + <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <argument name="address" value="updateCustomerChinaAddress"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForChina.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForChina.php new file mode 100644 index 0000000000000..0750f8056c4d7 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForChina.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add China States + */ +class AddDataForChina implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var \Magento\Directory\Setup\DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForChina() + ); + } + + /** + * China states data. + * + * @return array + */ + private function getDataForChina() + { + return [ + ['CN', 'CN-AH', 'Anhui Sheng'], + ['CN', 'CN-BJ', 'Beijing Shi'], + ['CN', 'CN-CQ', 'Chongqing Shi'], + ['CN', 'CN-FJ', 'Fujian Sheng'], + ['CN', 'CN-GS', 'Gansu Sheng'], + ['CN', 'CN-GD', 'Guangdong Sheng'], + ['CN', 'CN-GX', 'Guangxi Zhuangzu Zizhiqu'], + ['CN', 'CN-GZ', 'Guizhou Sheng'], + ['CN', 'CN-HI', 'Hainan Sheng'], + ['CN', 'CN-HE', 'Hebei Sheng'], + ['CN', 'CN-HL', 'Heilongjiang Sheng'], + ['CN', 'CN-HA', 'Henan Sheng'], + ['CN', 'CN-HK', 'Hong Kong SAR'], + ['CN', 'CN-HB', 'Hubei Sheng'], + ['CN', 'CN-HN', 'Hunan Sheng'], + ['CN', 'CN-JS', 'Jiangsu Sheng'], + ['CN', 'CN-JX', 'Jiangxi Sheng'], + ['CN', 'CN-JL', 'Jilin Sheng'], + ['CN', 'CN-LN', 'Liaoning Sheng'], + ['CN', 'CN-MO', 'Macao SAR'], + ['CN', 'CN-NM', 'Nei Mongol Zizhiqu'], + ['CN', 'CN-NX', 'Ningxia Huizi Zizhiqu'], + ['CN', 'CN-QH', 'Qinghai Sheng'], + ['CN', 'CN-SN', 'Shaanxi Sheng'], + ['CN', 'CN-SD', 'Shandong Sheng'], + ['CN', 'CN-SH', 'Shanghai Shi'], + ['CN', 'CN-SX', 'Shanxi Sheng'], + ['CN', 'CN-SC', 'Sichuan Sheng'], + ['CN', 'CN-TW', 'Taiwan Sheng'], + ['CN', 'CN-TJ', 'Tianjin Shi'], + ['CN', 'CN-XJ', 'Xinjiang Uygur Zizhiqu'], + ['CN', 'CN-XZ', 'Xizang Zizhiqu'], + ['CN', 'CN-YN', 'Yunnan Sheng'], + ['CN', 'CN-ZJ', 'Zhejiang Sheng'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} From 1de4b8cff4988e63f6e84b19004402426a7999b3 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Wed, 18 Sep 2019 19:38:51 -0300 Subject: [PATCH 0572/2437] fixing static check errors --- app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php | 2 +- app/code/Magento/Customer/view/frontend/web/js/validation.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index 9f49cbdce1f96..11e828d18e570 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -547,7 +547,7 @@ public function testGetHtmlExtraParamsWithRequiredOption() ->with('{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}') ->will( $this->returnValue( - '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' + '{"required":true,"validate-date":{"dateFormat":"M\/d\/Y"},"validate-dob":true}' ) ); diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index a6c058236a107..672681393015e 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -3,7 +3,7 @@ require([ 'moment', 'jquery/validate', 'mage/translate', -], function($, moment) { +], function ($, moment) { 'use strict'; $.validator.addMethod( From e481fe849332df380f9ab3833c41ca6c0d569209 Mon Sep 17 00:00:00 2001 From: Tiago <tiagooctp@gmail.com> Date: Wed, 18 Sep 2019 20:25:11 -0300 Subject: [PATCH 0573/2437] remove last comma --- app/code/Magento/Customer/view/frontend/web/js/validation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/frontend/web/js/validation.js b/app/code/Magento/Customer/view/frontend/web/js/validation.js index 672681393015e..56b60c3a08105 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/validation.js +++ b/app/code/Magento/Customer/view/frontend/web/js/validation.js @@ -2,7 +2,7 @@ require([ 'jquery', 'moment', 'jquery/validate', - 'mage/translate', + 'mage/translate' ], function ($, moment) { 'use strict'; From db62db02e6505f2d43146437ef87dd09d59d142b Mon Sep 17 00:00:00 2001 From: "dzmitry_tabusheu@epam.com" <dzmitry_tabusheu@epam.com> Date: Thu, 19 Sep 2019 07:31:23 +0300 Subject: [PATCH 0574/2437] MAGETWO-72172: [2.3] Disabled variation of configurable product can be added to shopping cart via admin - Disabled skipping if product is saleable check --- .../Block/Product/View/Type/Configurable.php | 3 +- .../Product/View/Type/ConfigurableTest.php | 37 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index 71db9d32aa593..55c0c8f6ca4ce 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -182,11 +182,10 @@ public function getAllowProducts() { if (!$this->hasAllowProducts()) { $products = []; - $skipSaleableCheck = $this->catalogProduct->getSkipSaleableCheck(); $allProducts = $this->getProduct()->getTypeInstance()->getUsedProducts($this->getProduct(), null); /** @var $product \Magento\Catalog\Model\Product */ foreach ($allProducts as $product) { - if ($skipSaleableCheck || ((int) $product->getStatus()) === Status::STATUS_ENABLED) { + if ((int) $product->getStatus() === Status::STATUS_ENABLED) { $products[] = $product; } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index c5c2368720b98..36fda4ef3245c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -233,9 +233,7 @@ public function cacheKeyProvider(): array public function testGetCacheKeyInfo(array $expected, string $priceCurrency = null, string $customerGroupId = null) { $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->setMethods([ - 'getCurrentCurrency', - ]) + ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); $storeMock->expects($this->any()) ->method('getCode') @@ -270,9 +268,7 @@ public function testGetJsonConfig() $amountMock = $this->getAmountMock($amount); $priceMock = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) - ->setMethods([ - 'getAmount', - ]) + ->setMethods(['getAmount']) ->getMockForAbstractClass(); $priceMock->expects($this->any())->method('getAmount')->willReturn($amountMock); $tierPriceMock = $this->getTierPriceMock($amountMock, $priceQty, $percentage); @@ -287,22 +283,25 @@ public function testGetJsonConfig() ->getMock(); $priceInfoMock->expects($this->any()) ->method('getPrice') - ->willReturnMap([ - ['regular_price', $priceMock], - ['final_price', $priceMock], - ['tier_price', $tierPriceMock], - ]); + ->willReturnMap( + [ + ['regular_price', $priceMock], + ['final_price', $priceMock], + ['tier_price', $tierPriceMock], + ] + ); $productMock->expects($this->any())->method('getTypeInstance')->willReturn($productTypeMock); $productMock->expects($this->any())->method('getPriceInfo')->willReturn($priceInfoMock); $productMock->expects($this->any())->method('isSaleable')->willReturn(true); $productMock->expects($this->any())->method('getId')->willReturn($productId); + $productMock->expects($this->any())->method('getStatus') + ->willReturn(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED); $this->helper->expects($this->any()) ->method('getOptions') ->with($productMock, [$productMock]) ->willReturn([]); - $this->product->expects($this->any())->method('getSkipSaleableCheck')->willReturn(true); $attributesData = [ 'attributes' => [], @@ -421,9 +420,7 @@ private function getProductTypeMock(\PHPUnit_Framework_MockObject_MockObject $pr ->willReturn('%s'); $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->setMethods([ - 'getCurrentCurrency', - ]) + ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); $storeMock->expects($this->any()) ->method('getCurrentCurrency') @@ -475,10 +472,7 @@ protected function mockContextObject() protected function getAmountMock($amount): \PHPUnit_Framework_MockObject_MockObject { $amountMock = $this->getMockBuilder(\Magento\Framework\Pricing\Amount\AmountInterface::class) - ->setMethods([ - 'getValue', - 'getBaseAmount', - ]) + ->setMethods(['getValue', 'getBaseAmount']) ->getMockForAbstractClass(); $amountMock->expects($this->any()) ->method('getValue') @@ -506,10 +500,7 @@ protected function getTierPriceMock(\PHPUnit_Framework_MockObject_MockObject $am ]; $tierPriceMock = $this->getMockBuilder(\Magento\Catalog\Pricing\Price\TierPriceInterface::class) - ->setMethods([ - 'getTierPriceList', - 'getSavePercent', - ]) + ->setMethods(['getTierPriceList', 'getSavePercent']) ->getMockForAbstractClass(); $tierPriceMock->expects($this->any()) ->method('getTierPriceList') From 8245222bf55c03f69828674c2817bf025f3b7325 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Thu, 19 Sep 2019 07:32:28 +0300 Subject: [PATCH 0575/2437] MAGETWO-72172: [2.3] Disabled variation of configurable product can be added to shopping cart via admin - Added automated test script --- ...vailableToConfigureDisabledProductTest.xml | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml new file mode 100644 index 0000000000000..ed521cef2a411 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NoOptionAvailableToConfigureDisabledProductTest"> + <annotations> + <title value="Disabled variation of configurable product can't be added to shopping cart via admin"/> + <description value="Disabled variation of configurable product can't be added to shopping cart via admin"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-17373"/> + <useCaseId value="MAGETWO-72172"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!--Create category--> + <comment userInput="Create category" stepKey="commentCreateCategory"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <!-- Create the configurable product based on the data in the data folder --> + <comment userInput="Create the configurable product based on the data in the data folder" stepKey="createConfigurableProduct"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create the configurable product with two options based on the default attribute set --> + <comment userInput="Create the configurable product with two options based on the default attribute set" stepKey="configurableProductWithTwoOptions"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!-- Create the 3 children that will be a part of the configurable product --> + <comment userInput="Create the 3 children that will be a part of the configurable product" stepKey="createTwoChildrenProducts"/> + <createData entity="ApiSimpleProductWithPrice50" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleProductWithPrice60" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiSimpleProductWithPrice70" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + <!-- Assign 3 products to the configurable product --> + <comment userInput="Assign 3 products to the configurable product" stepKey="assignToConfigurableProduct"/> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + <!-- Create Customer --> + <comment userInput="Create customer" stepKey="commentCreateCustomer"/> + <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> + </before> + <after> + <!-- Delete created data --> + <comment userInput="Delete created data" stepKey="deleteData"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Disable child product --> + <comment userInput="Disable child product" stepKey="disableChildProduct"/> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToEditPage"/> + <waitForPageLoad stepKey="waitForChildProductPageLoad"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <!-- Set the second product out of stock --> + <comment userInput="Set the second product out of stock" stepKey="outOfStockChildProduct"/> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondProductEditPage"/> + <waitForPageLoad stepKey="waitForSecondChildProductPageLoad"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="outOfStockStatus"/> + <actionGroup ref="saveProductForm" stepKey="saveSecondProductForm"/> + <!-- Go to created customer page --> + <comment userInput="Go to created customer page" stepKey="goToCreatedCustomerPage"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="createNewOrder"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickToAddProduct"/> + <waitForPageLoad stepKey="waitForProductsOpened"/> + <!-- Find created configurable product and click on "Configure" link --> + <comment userInput="Find created configurable product and click on Configure link" stepKey="goToConfigurableLink"/> + <click selector="{{AdminOrderFormConfigureProductSection.configure($$createConfigProduct.id$$)}}" stepKey="clickOnConfigure"/> + <!-- Click on attribute drop-down and check no option 1 is available --> + <comment userInput="Click on attribute drop-down and check no option 1 is available" stepKey="commentNoOptionIsAvailable"/> + <waitForElement selector="{{AdminOrderFormConfigureProductSection.selectOption}}" stepKey="waitForShippingSectionLoaded"/> + <click selector="{{AdminOrderFormConfigureProductSection.selectOption}}" stepKey="clickToSelectOption"/> + <dontSee userInput="$$createConfigProductAttributeOption1.option[store_labels][1][label]$$" stepKey="dontSeeOption1"/> + <!-- Go to created customer page again --> + <comment userInput="Go to created customer page again" stepKey="goToCreatedCustomerPageAgain"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="createNewOrderAgain"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickToAddProductAgain"/> + <waitForPageLoad stepKey="waitForProductsOpenedAgain"/> + <fillField selector="{{AdminOrderFormItemsSection.idFilter}}" userInput="$$createConfigChildProduct2.id$$" stepKey="idFilter"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectConfigurableProduct"/> + <!-- Add product to order --> + <comment userInput="Add product to order" stepKey="addProductToOrder"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickToAddProductToOrder"/> + <waitForPageLoad stepKey="waitForNewOrderPageLoad"/> + <see userInput="This product is out of stock." stepKey="seeTheErrorMessageDisplayed"/> + <!-- Select shipping method --> + <comment userInput="Select shipping method" stepKey="selectShippingMethod"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <waitForPageLoad stepKey="waitForSuccess"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + </test> +</tests> From eb0706bec1dd3b948787cb41ed13ffb5a2b1eb00 Mon Sep 17 00:00:00 2001 From: "dzmitry_tabusheu@epam.com" <dzmitry_tabusheu@epam.com> Date: Thu, 19 Sep 2019 07:34:54 +0300 Subject: [PATCH 0576/2437] MAGETWO-72172: [2.3] Disabled variation of configurable product can be added to shopping cart via admin - Fixed autotest --- .../Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml index 8c093891ac46d..4f065ec7eb748 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormConfigureProductSection"> + <element name="configure" type="button" selector="//a[@product_id='{{productId}}']" parameterized="true"/> <element name="optionSelect" type="select" selector="//div[contains(@class,'product-options')]//select[//label[text() = '{{option}}']]" parameterized="true"/> <element name="optionSelectNew" type="select" selector="//label[text()='{{option1}}']/following-sibling::div/select" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> From f556ab303836db16edc2c45ab1aa22ea32f5c736 Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Thu, 19 Sep 2019 09:07:10 +0300 Subject: [PATCH 0577/2437] Convert UpdateCmsPageRewriteEntityTest to MFTF --- .../AdminUpdateCmsPageRewriteEntityTest.xml | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml index 40a38dc689241..f772c62489a98 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityTest.xml @@ -11,7 +11,7 @@ <annotations> <stories value="Update CMS Page URL Redirect"/> <title value="Update CMS Page URL Redirect"/> - <description value="Login as Admin and tried to update the create URL Rewrite for CMS page"/> + <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> <group value="cMSContent"/> <group value="mtf_migrated"/> </annotations> @@ -66,12 +66,11 @@ <argument name="message" value="The URL Rewrite has been saved."/> </actionGroup> + <!-- Assert Url Rewrite Cms Page Redirect --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchToCustomStoreView"> <argument name="storeView" value="customStore"/> </actionGroup> - - <!-- Assert Url Rewrite Cms Page Redirect --> <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> <argument name="page" value="newrequestpath"/> </actionGroup> @@ -81,10 +80,11 @@ <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> </actionGroup> - <!--Search created CMS page url rewrite in grid--> + <!--Search created CMS page url rewrite in grid second attempt--> <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewriteSecondAttempt"> <argument name="requestPath" value="newrequestpath"/> </actionGroup> + <!-- Update URL Rewrite for CMS Page Second Attempt --> <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateUrlRewriteSecondAttempt"> <argument name="storeValue" value="Default Store View"/> @@ -92,15 +92,15 @@ <argument name="redirectTypeValue" value="Temporary (302)"/> <argument name="description" value="test description_302"/> </actionGroup> + <!-- Assert Url Rewrite Save Message --> <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondAttempt"> <argument name="message" value="The URL Rewrite has been saved."/> </actionGroup> - <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepageSecondAttempt"/> - <actionGroup ref="StorefrontSwitchDefaultStoreViewActionGroup" stepKey="switchToDefualtStoreView"/> - <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepageSecondAttempt"/> + <actionGroup ref="StorefrontSwitchDefaultStoreViewActionGroup" stepKey="switchToDefaultStoreView"/> <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFrontSecondAttempt"> <argument name="page" value="temporaryrequestpath.html"/> </actionGroup> @@ -110,9 +110,37 @@ <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> </actionGroup> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <!--Search created CMS page url rewrite in grid third attempt--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewriteThirdAttempt"> <argument name="requestPath" value="temporaryrequestpath.html"/> </actionGroup> + + <!-- Update URL Rewrite for CMS Page Third Attempt --> + <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateUrlRewriteThirdAttempt"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="permanentrequestpath.htm"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="test_description_301"/> + </actionGroup> + + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageThirdAttempt"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFrontThirdAttempt"> + <argument name="page" value="permanentrequestpath.htm"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPageThirdAttempt"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="permanentrequestpath.htm"/> + </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> </test> From fb9709dfd3dbe98663282f375e6593f179dc7bcd Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Thu, 19 Sep 2019 09:10:37 +0300 Subject: [PATCH 0578/2437] Update UpdateCmsPageRewriteEntityTest.xml file --- .../Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml index 6ff68599beeb3..1125ce8d916c1 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\UpdateCmsPageRewriteEntityTest" summary="Update Cms Page URL Rewrites " ticketId="MAGETWO-26173"> <variation name="UpdateCmsPageRewriteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_no_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/%default%</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%</data> @@ -18,7 +18,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_temporary_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.html</data> @@ -28,7 +28,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_permanent_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.htm</data> From 927f94070da8e13ec814da08deec47f02dbbd516 Mon Sep 17 00:00:00 2001 From: Mikalai Shostka <Mikalai_Shostka@epam.com> Date: Thu, 19 Sep 2019 10:14:51 +0300 Subject: [PATCH 0579/2437] MC-18215: Error message while creating shipping label - Revert echo url in location --- .../view/adminhtml/templates/order/packaging/popup.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml index cd25cb919adb5..28322d9534926 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml @@ -31,7 +31,7 @@ $girthEnabled = $block->isDisplayGirthValue() && $block->isGirthAllowed() ? 1 : packaging.sendCreateLabelRequest(); }); packaging.setLabelCreatedCallback(function(response){ - setLocation("<?php $block->escapeJs($block->escapeUrl($block->getUrl( + setLocation("<?= $block->escapeJs($block->escapeUrl($block->getUrl( 'sales/order/view', ['order_id' => $block->getShipment()->getOrderId()] ))); ?>"); From 950b8a3ce350d8cc43b2849494d475a8c9f08bf4 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 19 Sep 2019 10:42:09 +0300 Subject: [PATCH 0580/2437] MC-20071: Fix Skipped MFTF Tests From MC-17140: MAGETWO-98211, MC-56, MC-88 --- ...CustomizableOptionToProductWithSKUTest.xml | 5 +-- ...UpdateProductAttributesGlobalScopeTest.xml | 12 +++--- .../AdminConfigurableProductUpdateTest.xml | 41 +++++++++---------- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index b1f00a2f51a95..e29a23fe4f18f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -18,9 +18,6 @@ <testCaseId value="MAGETWO-98211"/> <useCaseId value="MAGETWO-70232"/> <group value="catalog"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -79,6 +76,6 @@ <!--Save product and check sku changed message--> <comment userInput="Save product and check sku changed message" stepKey="commentSAveProductAndCheck"/> <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> - <see userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 87e0bf3d2e9a0..83b64990b22e7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -18,9 +18,6 @@ <testCaseId value="MC-56"/> <group value="Catalog"/> <group value="Product Attributes"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -38,7 +35,7 @@ <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Search and select products --> @@ -58,10 +55,11 @@ <!-- Switch store view --> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> <!-- Update attribute --> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> + <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> <fillField selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="$$createProductOne.price$$0" stepKey="fillAttributeNameField"/> <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> @@ -77,6 +75,7 @@ <argument name="priceTo" value="$$createProductOne.price$$0"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <waitForElementVisible selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="waitForSearchResultInDefaultView"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> <!-- Assert on storefront custom view --> @@ -88,6 +87,7 @@ <argument name="priceTo" value="$$createProductOne.price$$0"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <waitForElementVisible selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="waitForSearchResultInCustomView"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 3a6a20de3ed90..88bd48909e3d1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -17,34 +17,33 @@ <testCaseId value="MC-88"/> <group value="ConfigurableProduct"/> <severity value="AVERAGE"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> - <createData entity="ApiConfigurableProduct" stepKey="createProduct1"> + <createData entity="ApiConfigurableProduct" stepKey="createFirstProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct2"> + <createData entity="ApiConfigurableProduct" stepKey="createSecondProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <createData entity="ApiConfigurableProduct" stepKey="createProduct3"> + <createData entity="ApiConfigurableProduct" stepKey="createThirdProduct"> <requiredEntity createDataKey="createCategory"/> </createData> <actionGroup ref="LoginAsAdmin" stepKey="login"/> </before> <after> - <amOnPage url="admin/admin/auth/logout/" stepKey="logout"/> + <actionGroup ref="logout" stepKey="logout"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> </after> <!-- Search for prefix of the 3 products we created via api --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> - <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> </actionGroup> @@ -54,30 +53,28 @@ <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickBulkUpdate"/> - <waitForPageLoad stepKey="wait2"/> + <waitForPageLoad stepKey="waitForUpdateAttributesPageLoad"/> <!-- Update the description --> <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> + <magentoCLI command="cron:run" stepKey="runCronFirstTime"/> + <magentoCLI command="cron:run" stepKey="runCronSecondTime"/> <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> + <waitForPageLoad stepKey="waitFormToReload"/> <!-- Check storefront for description --> - <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoProduct1"/> - <waitForPageLoad stepKey="wait3"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription1"/> - <amOnPage url="$$createProduct2.sku$$.html" stepKey="gotoProduct2"/> - <waitForPageLoad stepKey="wait4"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription2"/> - <amOnPage url="$$createProduct3.sku$$.html" stepKey="gotoProduct3"/> - <waitForPageLoad stepKey="wait5"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription3"/> + <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createSecondProduct.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createThirdProduct.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> </test> <test name="AdminConfigurableProductRemoveAnOptionTest"> From 452800a7a3c803ba64cb5aef9308eedc74315545 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 19 Sep 2019 12:19:18 +0300 Subject: [PATCH 0581/2437] MC-20195: Move test MC-13104 to infrastructure --- ...product_simple_with_custom_file_option.php | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php index 5c0c024ef4c39..77a9871764cee 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php @@ -53,34 +53,27 @@ ->setCanSaveCustomOptions(true) ->setHasOptions(true); -$options = [ - [ - 'title' => 'file option', - 'type' => 'file', - 'is_require' => true, - 'sort_order' => 1, - 'price' => 30.0, - 'price_type' => 'percent', - 'sku' => 'sku3', - 'file_extension' => 'jpg, png, gif', - 'image_size_x' => 100, - 'image_size_y' => 100, - - ], +$option = [ + 'title' => 'file option', + 'type' => 'file', + 'is_require' => true, + 'sort_order' => 1, + 'price' => 30.0, + 'price_type' => 'percent', + 'sku' => 'sku3', + 'file_extension' => 'jpg, png, gif', + 'image_size_x' => 100, + 'image_size_y' => 100, ]; $customOptions = []; /** @var ProductCustomOptionInterfaceFactory $customOptionFactory */ $customOptionFactory = $objectManager->create(ProductCustomOptionInterfaceFactory::class); - -foreach ($options as $option) { - /** @var ProductCustomOptionInterface $customOption */ - $customOption = $customOptionFactory->create(['data' => $option]); - $customOption->setProductSku($product->getSku()); - - $customOptions[] = $customOption; -} +/** @var ProductCustomOptionInterface $customOption */ +$customOption = $customOptionFactory->create(['data' => $option]); +$customOption->setProductSku($product->getSku()); +$customOptions[] = $customOption; $product->setOptions($customOptions); From db7a3a1369d0c76422c56af179ab61f5b5d679e5 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Wed, 18 Sep 2019 17:06:04 -0500 Subject: [PATCH 0582/2437] MC-19894: Customer Address attribute code is displayed instead of attribute label during checkout - changes updated --- .../address-renderer/default.js | 27 ++++++++++++++++++- .../web/js/view/shipping-address/list.js | 3 ++- .../address-renderer/default.html | 17 ++---------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js index 54381ad96b0b9..939a2af1a25aa 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/address-renderer/default.js @@ -7,12 +7,13 @@ define([ 'jquery', 'ko', 'uiComponent', + 'underscore', 'Magento_Checkout/js/action/select-shipping-address', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/shipping-address/form-popup-state', 'Magento_Checkout/js/checkout-data', 'Magento_Customer/js/customer-data' -], function ($, ko, Component, selectShippingAddressAction, quote, formPopUpState, checkoutData, customerData) { +], function ($, ko, Component, _, selectShippingAddressAction, quote, formPopUpState, checkoutData, customerData) { 'use strict'; var countryData = customerData.get('directory-data'); @@ -47,6 +48,30 @@ define([ return countryData()[countryId] != undefined ? countryData()[countryId].name : ''; //eslint-disable-line }, + /** + * Get customer attribute label + * + * @param {*} attribute + * @returns {*} + */ + getCustomAttributeLabel: function (attribute) { + var resultAttribute; + + if (typeof attribute === 'string') { + return attribute; + } + + if (attribute.label) { + return attribute.label; + } + + resultAttribute = _.findWhere(this.source.get('customAttributes')[attribute['attribute_code']], { + value: attribute.value + }); + + return resultAttribute && resultAttribute.label || attribute.value; + }, + /** Set selected customer shipping address */ selectAddress: function () { selectShippingAddressAction(this.address()); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/list.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/list.js index 4f4fc3de3e1a5..2bdfd063cb6fb 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/list.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-address/list.js @@ -16,7 +16,8 @@ define([ var defaultRendererTemplate = { parent: '${ $.$data.parentName }', name: '${ $.$data.name }', - component: 'Magento_Checkout/js/view/shipping-address/address-renderer/default' + component: 'Magento_Checkout/js/view/shipping-address/address-renderer/default', + provider: 'checkoutProvider' }; return Component.extend({ diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html index cf64c0140b955..b14f4da3f5f7d 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping-address/address-renderer/default.html @@ -13,21 +13,8 @@ <a if="address().telephone" attr="'href': 'tel:' + address().telephone" text="address().telephone"></a><br/> <each args="data: address().customAttributes, as: 'element'"> - <each args="data: Object.keys(element), as: 'attribute'"> - <if args="typeof element[attribute] === 'object'"> - <if args="element[attribute].label"> - <text args="element[attribute].label"/> - </if> - <ifnot args="element[attribute].label"> - <if args="element[attribute].value"> - <text args="element[attribute].value"/> - </if> - </ifnot> - </if> - <if args="typeof element[attribute] === 'string'"> - <text args="element[attribute]"/> - </if><br/> - </each> + <text args="$parent.getCustomAttributeLabel(element)"/> + <br/> </each> <button visible="address().isEditable()" type="button" From 928e966f3d8f79a3bb5b22d60c63e6a31cda3e58 Mon Sep 17 00:00:00 2001 From: mahesh <mahesh721@webkul.com> Date: Thu, 19 Sep 2019 21:18:56 +0530 Subject: [PATCH 0583/2437] Issue #24225 fixed: FPT / Tax Totals Sorting Not Correct --- .../Weee/view/adminhtml/layout/sales_order_creditmemo_new.xml | 2 +- .../Weee/view/adminhtml/layout/sales_order_creditmemo_view.xml | 2 +- .../Weee/view/adminhtml/layout/sales_order_invoice_new.xml | 2 +- .../Weee/view/adminhtml/layout/sales_order_invoice_view.xml | 2 +- .../Magento/Weee/view/adminhtml/layout/sales_order_view.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_new.xml b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_new.xml index 94a77534d94e8..04522be9cb625 100644 --- a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_new.xml +++ b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -10,7 +10,7 @@ <referenceBlock name="creditmemo_totals"> <block class="Magento\Weee\Block\Sales\Order\Totals" name="weee_cm_totals"> <action method="setBeforeCondition"> - <argument name="condition" xsi:type="string">tax</argument> + <argument name="condition" xsi:type="string">grand_total</argument> </action> </block> </referenceBlock> diff --git a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_view.xml b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_view.xml index 94a77534d94e8..04522be9cb625 100644 --- a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_view.xml +++ b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_creditmemo_view.xml @@ -10,7 +10,7 @@ <referenceBlock name="creditmemo_totals"> <block class="Magento\Weee\Block\Sales\Order\Totals" name="weee_cm_totals"> <action method="setBeforeCondition"> - <argument name="condition" xsi:type="string">tax</argument> + <argument name="condition" xsi:type="string">grand_total</argument> </action> </block> </referenceBlock> diff --git a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_new.xml b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_new.xml index d14bba1395385..8a89806c429c9 100644 --- a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_new.xml +++ b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_new.xml @@ -10,7 +10,7 @@ <referenceBlock name="invoice_totals"> <block class="Magento\Weee\Block\Sales\Order\Totals" name="weee_inv_totals"> <action method="setBeforeCondition"> - <argument name="condition" xsi:type="string">tax</argument> + <argument name="condition" xsi:type="string">grand_total</argument> </action> </block> </referenceBlock> diff --git a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_view.xml b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_view.xml index d14bba1395385..8a89806c429c9 100644 --- a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_view.xml +++ b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_invoice_view.xml @@ -10,7 +10,7 @@ <referenceBlock name="invoice_totals"> <block class="Magento\Weee\Block\Sales\Order\Totals" name="weee_inv_totals"> <action method="setBeforeCondition"> - <argument name="condition" xsi:type="string">tax</argument> + <argument name="condition" xsi:type="string">grand_total</argument> </action> </block> </referenceBlock> diff --git a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_view.xml b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_view.xml index f31acedf94447..5be6eba2d8b12 100644 --- a/app/code/Magento/Weee/view/adminhtml/layout/sales_order_view.xml +++ b/app/code/Magento/Weee/view/adminhtml/layout/sales_order_view.xml @@ -10,7 +10,7 @@ <referenceBlock name="order_totals"> <block class="Magento\Weee\Block\Sales\Order\Totals" name="weee_ord_totals"> <action method="setBeforeCondition"> - <argument name="condition" xsi:type="string">tax</argument> + <argument name="condition" xsi:type="string">grand_total</argument> </action> </block> </referenceBlock> From 025f6c15dd390fca1c0bab68dece4acd8eb99334 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 19 Sep 2019 13:23:42 -0500 Subject: [PATCH 0584/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Cms/etc/db_schema.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/etc/db_schema.xml b/app/code/Magento/Cms/etc/db_schema.xml index e0cf534e8354c..9ff3153098482 100644 --- a/app/code/Magento/Cms/etc/db_schema.xml +++ b/app/code/Magento/Cms/etc/db_schema.xml @@ -69,7 +69,7 @@ <column xsi:type="text" name="custom_layout_update_xml" nullable="true" comment="Page Custom Layout Update Content"/> <column xsi:type="varchar" name="layout_update_selected" nullable="true" - length="128" comment="Custom Layout Update File"/> + length="128" comment="Page Custom Layout File"/> <column xsi:type="date" name="custom_theme_from" comment="Page Custom Theme Active From Date"/> <column xsi:type="date" name="custom_theme_to" comment="Page Custom Theme Active To Date"/> <column xsi:type="varchar" name="meta_title" nullable="true" length="255" comment="Page Meta Title"/> From ce9fdb065ec69ae203d8b5f1539607c27489e742 Mon Sep 17 00:00:00 2001 From: Ani Tumanyan <ani_tumanyan@epam.com> Date: Fri, 20 Sep 2019 10:50:12 +0400 Subject: [PATCH 0585/2437] MC-18822: Increase test coverage for Content functional area - Automation test for MC-6192 --- .../AdminProductAttributeSetActionGroup.xml | 2 +- ...CheckResultsOfColorAndOtherFiltersTest.xml | 150 ++++++++---------- ...CheckResultsOfColorAndOtherFiltersTest.xml | 34 ++++ 3 files changed, 99 insertions(+), 87 deletions(-) create mode 100644 app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index e3f13f32ad015..a7d5a3864c052 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -64,7 +64,7 @@ <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> </actionGroup> - <actionGroup name="AdminAddUnsignedAttributeToGroup" extends="CreateDefaultAttributeSet"> + <actionGroup name="AdminAddUnassignedAttributeToGroup" extends="CreateDefaultAttributeSet"> <arguments> <argument name="firstOption" type="string" defaultValue="color"/> <argument name="secondOption" type="string" defaultValue="material"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml index 097251c844c40..3af1ad8aa3ae8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -58,77 +58,12 @@ <requiredEntity createDataKey="createConfigProductAttribute2"/> </createData> <!-- Add created attributes with options to Attribute Set --> - <actionGroup ref="AdminAddUnsignedAttributeToGroup" stepKey="createDefaultAttributeSet"> + <actionGroup ref="AdminAddUnassignedAttributeToGroup" stepKey="createDefaultAttributeSet"> <argument name="label" value="mySet"/> <argument name="firstOption" value="$$createConfigProductAttribute.attribute_code$$"/> <argument name="secondOption" value="$$createConfigProductAttribute2.attribute_code$$"/> <argument name="group" value="Product Details"/> </actionGroup> - <!-- Create three configurable products with options --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="wait1"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> - <!-- Create First configurable product with options --> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductFirst"> - <argument name="product" value="ConfigurableProductWithAttributeSet1"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['option1', 'option2', 'option3', 'option4']"/> - </actionGroup> - <actionGroup ref="AdminCreateConfigurationsForAttribute" stepKey="createConfigurationFirst"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - <argument name="price" value="34"/> - </actionGroup> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> - <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNew"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageFiest"/> - <!-- Create Second configurable product with options --> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductSecond"> - <argument name="product" value="ConfigurableProductWithAttributeSet2"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['option1', 'option2', 'option3']"/> - </actionGroup> - <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationSecond"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - <argument name="price" value="34"/> - <argument name="attributeOption" value="option5"/> - </actionGroup> - <waitForPageLoad stepKey="waitForPageLoadThird"/> - <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDownThird"/> - <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNewThird"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageSecond"/> - <!-- Create Third configurable product with options --> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductThird"> - <argument name="product" value="ConfigurableProductWithAttributeSet3"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['option2', 'option3', 'option4']"/> - </actionGroup> - <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationThird"> - <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> - <argument name="price" value="34"/> - <argument name="attributeOption" value="option1"/> - </actionGroup> - <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSaveButton"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveConfigurableProductMessage"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPageAgain"/> - <waitForPageLoad stepKey="waitForPageLoadForth"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggleAgain"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickOnAddSimpleProduct"/> - <!-- Create Simple product with options --> - <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createSimpleProduct"> - <argument name="product" value="ApiSimpleProduct"/> - <argument name="category" value="$$createCategory$$"/> - <argument name="label" value="mySet"/> - <argument name="option1" value="['option1', 'option2']"/> - </actionGroup> - <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSave"/> - <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageThird"/> </before> <after> <!-- Delete all created data --> @@ -160,27 +95,70 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> - <!-- Open a category on storefront --> - <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPage"> - <argument name="categoryName" value="$$createCategory.name$$"/> + <!-- Create three configurable products with options --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <!-- Create First configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductFirst"> + <argument name="product" value="ConfigurableProductWithAttributeSet1"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurationsForAttribute" stepKey="createConfigurationFirst"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDown"/> + <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNew"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageFiest"/> + <!-- Create Second configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductSecond"> + <argument name="product" value="ConfigurableProductWithAttributeSet2"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationSecond"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option5"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoadThird"/> + <click selector="{{AdminProductFormActionSection.saveArrow}}" stepKey="openSaveDropDownThird"/> + <click selector="{{AdminGridMainControls.saveAndNew}}" stepKey="clickToSaveAndNewThird"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageSecond"/> + <!-- Create Third configurable product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createProductThird"> + <argument name="product" value="ConfigurableProductWithAttributeSet3"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOption" stepKey="createConfigurationThird"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option1"/> </actionGroup> - <!-- Choose First attribute filter --> - <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="waitForCartRuleButton"/> - <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="expandFirstAttribute"/> - <waitForPageLoad stepKey="waitForFilterLoad"/> - <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> - <waitForPageLoad stepKey="waitForAttributeOption"/> - <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet3.name)}}" stepKey="seeFirstProduct"/> - <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet2.name)}}" stepKey="seeSecondProduct"/> - <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet1.name)}}" stepKey="seeThirdProduct"/> - <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> - <argument name="categoryName" value="$$createCategory.name$$"/> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSaveButton"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveConfigurableProductMessage"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPageAgain"/> + <waitForPageLoad stepKey="waitForPageLoadForth"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggleAgain"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickOnAddSimpleProduct"/> + <!-- Create Simple product with options --> + <actionGroup ref="CreateConfigurableProductWithAttributeSet" stepKey="createSimpleProduct"> + <argument name="product" value="ApiSimpleProduct"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2']"/> </actionGroup> - <!-- Choose Second attribute filter --> - <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute2.default_frontend_label$$')}}" stepKey="expandSecondAttributeOption"/> - <waitForPageLoad stepKey="waitForFilterPageLoad"/> - <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> - <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet1.name)}}" stepKey="seeFourthProduct"/> - <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo(ConfigurableProductWithAttributeSet2.name)}}" stepKey="seeFifthProduct"/> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSave"/> + <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageThird"/> </test> </tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml new file mode 100644 index 0000000000000..32325f948e07b --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckResultsOfColorAndOtherFiltersTest"> + <!-- Open a category on storefront --> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" after="seeSaveProductMessageThird" stepKey="goToCategoryPage"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose First attribute filter --> + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="waitForCartRuleButton"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="expandFirstAttribute"/> + <waitForPageLoad stepKey="waitForFilterLoad"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> + <waitForPageLoad stepKey="waitForAttributeOption"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo('Pants')}}" stepKey="seeFirstProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo('Cardigan')}}" stepKey="seeSecondProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo('Jacket')}}" stepKey="seeThirdProduct"/> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose Second attribute filter --> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute2.default_frontend_label$$')}}" stepKey="expandSecondAttributeOption"/> + <waitForPageLoad stepKey="waitForFilterPageLoad"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo('Jacket')}}" stepKey="seeFourthProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo('Cardigan')}}" stepKey="seeFifthProduct"/> + </test> +</tests> From ad23daf3d4c915e05f7e28fdef7e8aef1ebbb2a6 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 20 Sep 2019 13:30:12 +0400 Subject: [PATCH 0586/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6411 --- ...EditCustomerInformationFromActionGroup.xml | 7 ------ ...ustomStoreShippingMethodTableRatesTest.xml | 17 +++++++------- .../AdminWebsitePageActionGroup.xml | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml index fa8931bc53141..09033955ecc60 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminEditCustomerInformationFromActionGroup.xml @@ -30,11 +30,4 @@ <waitForPageLoad stepKey="wait"/> <scrollToTopOfPage stepKey="scrollToTop"/> </actionGroup> - <actionGroup name="AdminAssociateCustomerToCustomWebsiteActionGroup"> - <arguments> - <argument name="websiteName" type="string" defaultValue="secondWebsite"/> - </arguments> - <conditionalClick selector="{{AdminCustomerAccountInformationSection.accountInformationTab}}" dependentSelector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" visible="false" stepKey="goToAccountInformation"/> - <selectOption selector="{{AdminCustomerAccountInformationSection.associateToWebsite}}" userInput="{{websiteName}}" stepKey="selectWebSite"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index 67885ccfc99d8..6f9327d68d451 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -22,7 +22,6 @@ <before> <!--Create product and customer--> <createData entity="SimpleProduct2" stepKey="createProduct"/> - <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create website, store group and store view--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> @@ -38,6 +37,14 @@ <argument name="StoreGroup" value="customStoreGroup"/> <argument name="customStore" value="customStore"/> </actionGroup> + <!--Create customer associated to website--> + <actionGroup ref="AdminGoCreatedWebsitePageActionGroup" stepKey="DeleteWebsite"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + <grabFromCurrentUrl regex="~/website_id/(\d+)/~" stepKey="grabWebsiteIdFromURL"/> + <createData entity="Simple_Customer_Without_Address" stepKey="createCustomer"> + <field key="website_id">$grabWebsiteIdFromURL</field> + </createData> <!--Enable Table Rate method and import csv file--> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="switchDefaultWebsite"> @@ -81,14 +88,6 @@ <argument name="website" value="{{customWebsite.name}}"/> </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - <!--Assign customer to custom website--> - <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> - <argument name="customerId" value="$$createCustomer.id$$"/> - </actionGroup> - <actionGroup ref="AdminAssociateCustomerToCustomWebsiteActionGroup" stepKey="associateCustomerToWebsite"> - <argument name="websiteName" value="{{customWebsite.name}}"/> - </actionGroup> - <actionGroup ref="AdminSaveCustomerAndAssertSuccessMessage" stepKey="saveAndCheckSuccessMessage"/> <!--Create order--> <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$createCustomer$$"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml new file mode 100644 index 0000000000000..fc54101c3a5f4 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGoCreatedWebsitePageActionGroup"> + <arguments> + <argument name="websiteName" type="string" defaultValue="SecondWebsite"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="verifyThatCorrectWebsiteFound"/> + <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + </actionGroup> +</actionGroups> From b19b72b00364753f948cb0e28085763eea29e445 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Fri, 20 Sep 2019 12:35:42 +0300 Subject: [PATCH 0587/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../Sales/Model/Order/ShipmentTest.php | 2 + .../testsuite/Magento/Sales/_files/order.php | 41 ------- .../_files/two_orders_with_order_items.php | 111 ++++++++++++++++++ 3 files changed, 113 insertions(+), 41 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 5b5c2365bfac4..0c9728b71c82d 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -150,6 +150,8 @@ private function getOrder(string $incrementId): OrderInterface /** * Check that getTracksCollection() returns only order related tracks. + * + * @magentoDataFixture Magento/Sales/_files/two_orders_with_order_items.php */ public function testGetTracksCollection() { diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php index 8d2132dadd69b..9ea85aae56cbb 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order.php @@ -68,44 +68,3 @@ /** @var OrderRepositoryInterface $orderRepository */ $orderRepository = $objectManager->create(OrderRepositoryInterface::class); $orderRepository->save($order); - -/** @var Payment $payment */ -$payment2 = $objectManager->create(Payment::class); -$payment2->setMethod('checkmo') - ->setAdditionalInformation('last_trans_id', '11122') - ->setAdditionalInformation( - 'metadata', - [ - 'type' => 'free', - 'fraudulent' => false, - ] - ); - -/** @var OrderItem $orderItem */ -$orderItem2 = $objectManager->create(OrderItem::class); -$orderItem2->setProductId($product->getId()) - ->setQtyOrdered(2) - ->setBasePrice($product->getPrice()) - ->setPrice($product->getPrice()) - ->setRowTotal($product->getPrice()) - ->setProductType('simple') - ->setName($product->getName()) - ->setSku($product->getSku()); - -/** @var Order $order */ -$order2 = $objectManager->create(Order::class); -$order2->setIncrementId('100000002') - ->setState(Order::STATE_PROCESSING) - ->setStatus($order2->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) - ->setSubtotal(100) - ->setGrandTotal(100) - ->setBaseSubtotal(100) - ->setBaseGrandTotal(100) - ->setCustomerIsGuest(true) - ->setCustomerEmail('customer@null.com') - ->setBillingAddress($billingAddress) - ->setShippingAddress($shippingAddress) - ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) - ->addItem($orderItem2) - ->setPayment($payment2); -$orderRepository->save($order2); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php new file mode 100644 index 0000000000000..8d2132dadd69b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; + +require 'default_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; +/** @var \Magento\Catalog\Model\Product $product */ + +$addressData = include __DIR__ . '/address_data.php'; + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setName($product->getName()) + ->setSku($product->getSku()); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); + +/** @var Payment $payment */ +$payment2 = $objectManager->create(Payment::class); +$payment2->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem2 = $objectManager->create(OrderItem::class); +$orderItem2->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setName($product->getName()) + ->setSku($product->getSku()); + +/** @var Order $order */ +$order2 = $objectManager->create(Order::class); +$order2->setIncrementId('100000002') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order2->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem2) + ->setPayment($payment2); +$orderRepository->save($order2); From 9371d8e4650dcee19b96ee20dc95e38ff38447cc Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Fri, 20 Sep 2019 15:34:37 +0300 Subject: [PATCH 0588/2437] MC-20071: Fix Skipped MFTF Tests From MC-17140: MAGETWO-98211, MC-56, MC-88 --- .../Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 83b64990b22e7..5c1a97721201d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -29,8 +29,11 @@ <createData entity="ApiSimpleProduct" stepKey="createProductTwo"> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" /> </before> <after> + <magentoCLI stepKey="setIndexersMode" command="indexer:set-mode" arguments="realtime" /> + <magentoCLI stepKey="indexerReindex" command="indexer:reindex" /> <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> From 50e60e18484088c05aab85944e8fa80655c3d953 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 20 Sep 2019 11:11:01 -0500 Subject: [PATCH 0589/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Model/Indexer/IndexBuilder.php | 67 +++++++++---------- .../Model/Indexer/ReindexRuleProductPrice.php | 7 +- .../Indexer/RuleProductsSelectBuilder.php | 13 ++-- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index e12eabba76401..10abf356fbef2 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -275,11 +275,21 @@ protected function doReindexByIds($ids) { $this->cleanProductIndex($ids); - $products = $this->productLoader->getProducts($ids); - $activeRules = $this->getActiveRules(); - foreach ($products as $product) { - $this->applyRules($activeRules, $product); + /** @var Rule[] $activeRules */ + $activeRules = $this->getActiveRules()->getItems(); + foreach ($activeRules as $rule) { + $rule->setProductsFilter($ids); + $productIds = $rule->getMatchingProductIds(); + foreach ($productIds as $productId) { + $this->assignProductToRule($rule, $productId); + } + } + + $this->cleanProductPriceIndex($ids); + foreach ($ids as $productId) { + $this->reindexRuleProductPrice->execute($this->batchCount, $productId); } + $this->reindexRuleGroupWebsite->execute(); } @@ -320,7 +330,7 @@ protected function doReindexFull() [ $this->getTable('catalogrule_product'), $this->getTable('catalogrule_product_price'), - $this->getTable('catalogrule_group_website') + $this->getTable('catalogrule_group_website'), ] ); } @@ -365,27 +375,25 @@ protected function cleanByIds($productIds) * Assign product to rule * * @param Rule $rule - * @param Product $product + * @param int $productId * @return void */ - private function assignProductToRule(Rule $rule, Product $product): void + private function assignProductToRule(Rule $rule, int $productId): void { - if (!$rule->validate($product)) { - return; - } - $ruleId = (int) $rule->getId(); - $productEntityId = (int) $product->getId(); $ruleProductTable = $this->getTable('catalogrule_product'); $this->connection->delete( $ruleProductTable, [ 'rule_id = ?' => $ruleId, - 'product_id = ?' => $productEntityId, + 'product_id = ?' => $productId, ] ); - $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); + $websiteIds = $rule->getWebsiteIds(); + if (!is_array($websiteIds)) { + $websiteIds = explode(',', $websiteIds); + } $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -404,7 +412,7 @@ private function assignProductToRule(Rule $rule, Product $product): void 'to_time' => $toTime, 'website_id' => $websiteId, 'customer_group_id' => $customerGroupId, - 'product_id' => $productEntityId, + 'product_id' => $productId, 'action_operator' => $actionOperator, 'action_amount' => $actionAmount, 'action_stop' => $actionStop, @@ -422,6 +430,8 @@ private function assignProductToRule(Rule $rule, Product $product): void } } + + /** * Apply rule * @@ -433,30 +443,15 @@ private function assignProductToRule(Rule $rule, Product $product): void */ protected function applyRule(Rule $rule, $product) { - $this->assignProductToRule($rule, $product); - $this->reindexRuleProductPrice->execute($this->batchCount, $product); + if ($rule->validate($product)) { + $this->assignProductToRule($rule, $product->getId()); + } + $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); $this->reindexRuleGroupWebsite->execute(); return $this; } - /** - * Apply rules - * - * @param RuleCollection $ruleCollection - * @param Product $product - * @return void - */ - private function applyRules(RuleCollection $ruleCollection, Product $product): void - { - foreach ($ruleCollection as $rule) { - $this->assignProductToRule($rule, $product); - } - - $this->cleanProductPriceIndex([$product->getId()]); - $this->reindexRuleProductPrice->execute($this->batchCount, $product); - } - /** * Retrieve table name * @@ -507,7 +502,7 @@ protected function updateRuleProductData(Rule $rule) */ protected function applyAllRules(Product $product = null) { - $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); $this->reindexRuleGroupWebsite->execute(); return $this; } @@ -562,7 +557,7 @@ protected function calcRuleProductPrice($ruleData, $productData = null) */ protected function getRuleProductsStmt($websiteId, Product $product = null) { - return $this->ruleProductsSelectBuilder->build($websiteId, $product); + return $this->ruleProductsSelectBuilder->build((int) $websiteId, (int) $product->getId()); } /** diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php index 11ba87730bec1..51869f1accbb3 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php @@ -6,7 +6,6 @@ namespace Magento\CatalogRule\Model\Indexer; -use Magento\Catalog\Model\Product; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Store\Model\StoreManagerInterface; @@ -65,19 +64,19 @@ public function __construct( * Reindex product prices. * * @param int $batchCount - * @param Product|null $product + * @param int|null $productId * @param bool $useAdditionalTable * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function execute($batchCount, Product $product = null, $useAdditionalTable = false) + public function execute(int $batchCount, ?int $productId = null, bool $useAdditionalTable = false) { /** * Update products rules prices per each website separately * because for each website date in website's timezone should be used */ foreach ($this->storeManager->getWebsites() as $website) { - $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $product, $useAdditionalTable); + $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $productId, $useAdditionalTable); $dayPrices = []; $stopFlags = []; $prevKey = null; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php index 6989a33535ad8..31fe9112ed279 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php @@ -74,15 +74,12 @@ public function __construct( * Build select for indexer according passed parameters. * * @param int $websiteId - * @param \Magento\Catalog\Model\Product|null $product + * @param int|null $productId * @param bool $useAdditionalTable * @return \Zend_Db_Statement_Interface */ - public function build( - $websiteId, - \Magento\Catalog\Model\Product $product = null, - $useAdditionalTable = false - ) { + public function build(int $websiteId, ?int $productId = null, bool $useAdditionalTable = false) + { $connection = $this->resource->getConnection(); $indexTable = $this->resource->getTableName('catalogrule_product'); if ($useAdditionalTable) { @@ -107,8 +104,8 @@ public function build( ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id'] ); - if ($product && $product->getEntityId()) { - $select->where('rp.product_id=?', $product->getEntityId()); + if ($productId) { + $select->where('rp.product_id=?', $productId); } /** From 13cea66ec53f20188d5dd06fe915920b8bb7803f Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 20 Sep 2019 13:16:51 -0500 Subject: [PATCH 0590/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Model/Indexer/ProductPriceIndexFilter.php | 42 +-- .../Model/Indexer/IndexBuilder.php | 17 +- .../Indexer/RuleProductsSelectBuilder.php | 8 +- .../Unit/Model/Indexer/IndexBuilderTest.php | 289 ------------------ .../Indexer/ReindexRuleProductPriceTest.php | 6 +- .../Indexer/RuleProductsSelectBuilderTest.php | 200 ------------ 6 files changed, 31 insertions(+), 531 deletions(-) delete mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php delete mode 100644 app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php index 170710ce09698..32fb85f270b9c 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/ProductPriceIndexFilter.php @@ -73,8 +73,17 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = } $connection = $this->resourceConnection->getConnection($this->connectionName); - $stockSelect = $connection->select(); + $stockSelect->from( + $this->stockItem->getMainTable(), + [ + 'product_id', + ] + ); + if (!empty($entityIds)) { + $stockSelect->where('product_id IN (?)', $entityIds); + } + $stockSelect->group('product_id'); if ($this->stockConfiguration->getManageStock()) { $stockStatus = $connection->getCheckSql( 'use_config_manage_stock = 0 AND manage_stock = 0', @@ -89,31 +98,12 @@ public function modifyPrice(IndexTableStructure $priceTable, array $entityIds = ); } $stockStatus = new \Zend_Db_Expr('MAX(' . $stockStatus . ')'); - $stockSelect->from( - $this->stockItem->getMainTable(), - [ - 'product_id' => 'product_id', - 'stock_status' => $stockStatus, - ] - ); - if (!empty($entityIds)) { - $stockSelect->where('product_id IN (?)', $entityIds); - } - $stockSelect->group('product_id'); + $stockSelect->having($stockStatus . ' = ' . Stock::STOCK_OUT_OF_STOCK); + $productIds = $connection->fetchCol($stockSelect); - $select = $connection->select(); - $select->from( - ['price_index' => $priceTable->getTableName()], - [] - ); - $select->joinInner( - ['stock_item' => $stockSelect], - 'stock_item.product_id = price_index.' . $priceTable->getEntityField(), - [] - ); - $select->where('stock_item.stock_status = ?', Stock::STOCK_OUT_OF_STOCK); - - $query = $select->deleteFromSelect('price_index'); - $connection->query($query); + if (!empty($productIds)) { + $where = [$priceTable->getEntityField() .' IN (?)' => $productIds]; + $connection->delete($priceTable->getTableName(), $where); + } } } diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 10abf356fbef2..6391171be0a35 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -280,8 +280,9 @@ protected function doReindexByIds($ids) foreach ($activeRules as $rule) { $rule->setProductsFilter($ids); $productIds = $rule->getMatchingProductIds(); - foreach ($productIds as $productId) { - $this->assignProductToRule($rule, $productId); + foreach ($productIds as $productId => $result) { + $websiteIds = array_keys(array_filter($result)); + $this->assignProductToRule($rule, $productId, $websiteIds); } } @@ -376,9 +377,10 @@ protected function cleanByIds($productIds) * * @param Rule $rule * @param int $productId + * @param array $websiteIds * @return void */ - private function assignProductToRule(Rule $rule, int $productId): void + private function assignProductToRule(Rule $rule, int $productId, array $websiteIds): void { $ruleId = (int) $rule->getId(); $ruleProductTable = $this->getTable('catalogrule_product'); @@ -390,10 +392,6 @@ private function assignProductToRule(Rule $rule, int $productId): void ] ); - $websiteIds = $rule->getWebsiteIds(); - if (!is_array($websiteIds)) { - $websiteIds = explode(',', $websiteIds); - } $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -430,8 +428,6 @@ private function assignProductToRule(Rule $rule, int $productId): void } } - - /** * Apply rule * @@ -444,7 +440,8 @@ private function assignProductToRule(Rule $rule, int $productId): void protected function applyRule(Rule $rule, $product) { if ($rule->validate($product)) { - $this->assignProductToRule($rule, $product->getId()); + $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); + $this->assignProductToRule($rule, $product->getId(), $websiteIds); } $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); $this->reindexRuleGroupWebsite->execute(); diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php index 31fe9112ed279..e15bf6b3b1faa 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php @@ -156,9 +156,11 @@ public function build(int $websiteId, ?int $productId = null, bool $useAdditiona sprintf($joinCondition, $tableAlias, $storeId), [] ); - $select->columns([ - 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), - ]); + $select->columns( + [ + 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), + ] + ); return $connection->query($select); } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php deleted file mode 100644 index 78668366bccdc..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php +++ /dev/null @@ -1,289 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogRule\Test\Unit\Model\Indexer; - -use Magento\CatalogRule\Model\Indexer\IndexBuilder\ProductLoader; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.TooManyFields) - */ -class IndexBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\CatalogRule\Model\Indexer\IndexBuilder - */ - protected $indexBuilder; - - /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resource; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManager; - - /** - * @var \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $ruleCollectionFactory; - - /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $logger; - - /** - * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $priceCurrency; - - /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject - */ - protected $eavConfig; - - /** - * @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dateFormat; - - /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dateTime; - - /** - * @var \Magento\Catalog\Model\ProductFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productFactory; - - /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connection; - - /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject - */ - protected $metadataPool; - - /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject - */ - protected $select; - - /** - * @var \Zend_Db_Statement_Interface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $db; - - /** - * @var \Magento\Store\Model\Website|\PHPUnit_Framework_MockObject_MockObject - */ - protected $website; - - /** - * @var \Magento\Rule\Model\Condition\Combine|\PHPUnit_Framework_MockObject_MockObject - */ - protected $combine; - - /** - * @var \Magento\CatalogRule\Model\Rule|\PHPUnit_Framework_MockObject_MockObject - */ - protected $rules; - - /** - * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject - */ - protected $product; - - /** - * @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject - */ - protected $attribute; - - /** - * @var \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend|\PHPUnit_Framework_MockObject_MockObject - */ - protected $backend; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $reindexRuleProductPrice; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $reindexRuleGroupWebsite; - - /** - * @var ProductLoader|\PHPUnit_Framework_MockObject_MockObject - */ - private $productLoader; - - /** - * Set up test - * - * @return void - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function setUp() - { - $this->resource = $this->createPartialMock( - \Magento\Framework\App\ResourceConnection::class, - ['getConnection', 'getTableName'] - ); - $this->ruleCollectionFactory = $this->createPartialMock( - \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory::class, - ['create'] - ); - $this->backend = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class); - $this->select = $this->createMock(\Magento\Framework\DB\Select::class); - $this->metadataPool = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $metadata = $this->createMock(\Magento\Framework\EntityManager\EntityMetadata::class); - $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata); - $this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $this->db = $this->createMock(\Zend_Db_Statement_Interface::class); - $this->website = $this->createMock(\Magento\Store\Model\Website::class); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->combine = $this->createMock(\Magento\Rule\Model\Condition\Combine::class); - $this->rules = $this->createMock(\Magento\CatalogRule\Model\Rule::class); - $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->attribute = $this->createMock(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class); - $this->priceCurrency = $this->createMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class); - $this->dateFormat = $this->createMock(\Magento\Framework\Stdlib\DateTime::class); - $this->dateTime = $this->createMock(\Magento\Framework\Stdlib\DateTime\DateTime::class); - $this->eavConfig = $this->createPartialMock(\Magento\Eav\Model\Config::class, ['getAttribute']); - $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); - $this->productFactory = $this->createPartialMock(\Magento\Catalog\Model\ProductFactory::class, ['create']); - $this->connection->expects($this->any())->method('select')->will($this->returnValue($this->select)); - $this->connection->expects($this->any())->method('query')->will($this->returnValue($this->db)); - $this->select->expects($this->any())->method('distinct')->will($this->returnSelf()); - $this->select->expects($this->any())->method('where')->will($this->returnSelf()); - $this->select->expects($this->any())->method('from')->will($this->returnSelf()); - $this->select->expects($this->any())->method('order')->will($this->returnSelf()); - $this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($this->connection)); - $this->resource->expects($this->any())->method('getTableName')->will($this->returnArgument(0)); - $this->storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$this->website])); - $this->storeManager->expects($this->any())->method('getWebsite')->will($this->returnValue($this->website)); - $this->rules->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->rules->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); - $this->rules->expects($this->any())->method('getCustomerGroupIds')->will($this->returnValue([1])); - - $ruleCollection = $this->createMock(\Magento\CatalogRule\Model\ResourceModel\Rule\Collection::class); - $this->ruleCollectionFactory->expects($this->once()) - ->method('create') - ->willReturn($ruleCollection); - $ruleCollection->expects($this->once()) - ->method('addFieldToFilter') - ->willReturnSelf(); - $ruleIterator = new \ArrayIterator([$this->rules]); - $ruleCollection->method('getIterator') - ->willReturn($ruleIterator); - - $this->product->expects($this->any())->method('load')->will($this->returnSelf()); - $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->product->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); - - $this->rules->expects($this->any())->method('validate')->with($this->product)->willReturn(true); - $this->attribute->expects($this->any())->method('getBackend')->will($this->returnValue($this->backend)); - $this->productFactory->expects($this->any())->method('create')->will($this->returnValue($this->product)); - $this->productLoader = $this->getMockBuilder(ProductLoader::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->indexBuilder = (new ObjectManager($this))->getObject( - \Magento\CatalogRule\Model\Indexer\IndexBuilder::class, - [ - 'ruleCollectionFactory' => $this->ruleCollectionFactory, - 'priceCurrency' => $this->priceCurrency, - 'resource' => $this->resource, - 'storeManager' => $this->storeManager, - 'logger' => $this->logger, - 'eavConfig' => $this->eavConfig, - 'dateFormat' => $this->dateFormat, - 'dateTime' => $this->dateTime, - 'productFactory' => $this->productFactory, - 'productLoader' => $this->productLoader, - ] - ); - - $this->reindexRuleProductPrice = $this->createMock( - \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class - ); - $this->reindexRuleGroupWebsite = $this->createMock( - \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class - ); - $this->setProperties( - $this->indexBuilder, - [ - 'metadataPool' => $this->metadataPool, - 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, - 'reindexRuleGroupWebsite' => $this->reindexRuleGroupWebsite, - ] - ); - } - - /** - * Test UpdateCatalogRuleGroupWebsiteData - * - * @covers \Magento\CatalogRule\Model\Indexer\IndexBuilder::updateCatalogRuleGroupWebsiteData - * @return void - */ - public function testUpdateCatalogRuleGroupWebsiteData() - { - $priceAttrMock = $this->createPartialMock(\Magento\Catalog\Model\Entity\Attribute::class, ['getBackend']); - $backendModelMock = $this->createPartialMock( - \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice::class, - ['getResource'] - ); - $resourceMock = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice::class, - ['getMainTable'] - ); - $resourceMock->expects($this->any()) - ->method('getMainTable') - ->will($this->returnValue('catalog_product_entity_tier_price')); - $backendModelMock->expects($this->any()) - ->method('getResource') - ->will($this->returnValue($resourceMock)); - $priceAttrMock->expects($this->any()) - ->method('getBackend') - ->will($this->returnValue($backendModelMock)); - - $iterator = [$this->product]; - $this->productLoader->expects($this->once()) - ->method('getProducts') - ->willReturn($iterator); - - $this->reindexRuleProductPrice->expects($this->once())->method('execute')->willReturn(true); - $this->reindexRuleGroupWebsite->expects($this->once())->method('execute')->willReturn(true); - - $this->indexBuilder->reindexByIds([1]); - } - - /** - * @param $object - * @param array $properties - */ - private function setProperties($object, $properties = []) - { - $reflectionClass = new \ReflectionClass(get_class($object)); - foreach ($properties as $key => $value) { - if ($reflectionClass->hasProperty($key)) { - $reflectionProperty = $reflectionClass->getProperty($key); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($object, $value); - } - } - } -} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php index 5f63283df6760..d0f266d574945 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php @@ -71,6 +71,7 @@ public function testExecute() $websiteId = 234; $defaultGroupId = 11; $defaultStoreId = 22; + $productId = 55; $websiteMock = $this->createMock(WebsiteInterface::class); $websiteMock->expects($this->once()) @@ -93,11 +94,10 @@ public function testExecute() ->with($defaultGroupId) ->willReturn($groupMock); - $productMock = $this->createMock(Product::class); $statementMock = $this->createMock(\Zend_Db_Statement_Interface::class); $this->ruleProductsSelectBuilderMock->expects($this->once()) ->method('build') - ->with($websiteId, $productMock, true) + ->with($websiteId, $productId, true) ->willReturn($statementMock); $ruleData = [ @@ -126,6 +126,6 @@ public function testExecute() $this->pricesPersistorMock->expects($this->once()) ->method('execute'); - $this->assertTrue($this->model->execute(1, $productMock, true)); + $this->assertTrue($this->model->execute(1, $productId, true)); } } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php deleted file mode 100644 index e43fe41dc2127..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogRule\Test\Unit\Model\Indexer; - -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; -use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface; -use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Framework\DB\Select; -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; -use Magento\Framework\EntityManager\EntityMetadataInterface; -use Magento\Store\Api\Data\WebsiteInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class RuleProductsSelectBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder - */ - private $model; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceMock; - - /** - * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject - */ - private $activeTableSwitcherMock; - - /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject - */ - private $eavConfigMock; - - /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject - */ - private $metadataPoolMock; - - /** - * @var IndexerTableSwapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $tableSwapperMock; - - protected function setUp() - { - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->getMockForAbstractClass(); - $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $this->activeTableSwitcherMock = $this->getMockBuilder(ActiveTableSwitcher::class) - ->disableOriginalConstructor() - ->getMock(); - $this->eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) - ->disableOriginalConstructor() - ->getMock(); - $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) - ->disableOriginalConstructor() - ->getMock(); - $this->tableSwapperMock = $this->getMockForAbstractClass( - IndexerTableSwapperInterface::class - ); - - $this->model = new \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder( - $this->resourceMock, - $this->eavConfigMock, - $this->storeManagerMock, - $this->metadataPoolMock, - $this->activeTableSwitcherMock, - $this->tableSwapperMock - ); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testBuild() - { - $websiteId = 55; - $ruleTable = 'catalogrule_product'; - $rplTable = 'catalogrule_product_replica'; - $prTable = 'catalog_product_entity'; - $wsTable = 'catalog_product_website'; - $productMock = $this->getMockBuilder(Product::class)->disableOriginalConstructor()->getMock(); - $productMock->expects($this->exactly(2))->method('getEntityId')->willReturn(500); - - $connectionMock = $this->getMockBuilder(AdapterInterface::class)->disableOriginalConstructor()->getMock(); - $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); - - $this->tableSwapperMock->expects($this->once()) - ->method('getWorkingTableName') - ->with($ruleTable) - ->willReturn($rplTable); - - $this->resourceMock->expects($this->at(1))->method('getTableName')->with($ruleTable)->willReturn($ruleTable); - $this->resourceMock->expects($this->at(2))->method('getTableName')->with($rplTable)->willReturn($rplTable); - $this->resourceMock->expects($this->at(3))->method('getTableName')->with($prTable)->willReturn($prTable); - $this->resourceMock->expects($this->at(4))->method('getTableName')->with($wsTable)->willReturn($wsTable); - - $selectMock = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); - $connectionMock->expects($this->once())->method('select')->willReturn($selectMock); - $selectMock->expects($this->at(0))->method('from')->with(['rp' => $rplTable])->willReturnSelf(); - $selectMock->expects($this->at(1)) - ->method('order') - ->with(['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id']) - ->willReturnSelf(); - $selectMock->expects($this->at(2))->method('where')->with('rp.product_id=?', 500)->willReturnSelf(); - - $attributeMock = $this->getMockBuilder(AbstractAttribute::class)->disableOriginalConstructor()->getMock(); - $this->eavConfigMock->expects($this->once()) - ->method('getAttribute') - ->with(Product::ENTITY, 'price') - ->willReturn($attributeMock); - $backendMock = $this->getMockBuilder(AbstractBackend::class)->disableOriginalConstructor()->getMock(); - $backendMock->expects($this->once())->method('getTable')->willReturn('price_table'); - $attributeMock->expects($this->once())->method('getBackend')->willReturn($backendMock); - $attributeMock->expects($this->once())->method('getId')->willReturn(200); - - $metadataMock = $this->getMockBuilder(EntityMetadataInterface::class)->disableOriginalConstructor()->getMock(); - $this->metadataPoolMock->expects($this->once()) - ->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) - ->willReturn($metadataMock); - $metadataMock->expects($this->once())->method('getLinkField')->willReturn('link_field'); - - $selectMock->expects($this->at(3)) - ->method('join') - ->with(['e' => $prTable], 'e.entity_id = rp.product_id', []) - ->willReturnSelf(); - $selectMock->expects($this->at(4)) - ->method('join') - ->with( - ['pp_default' => 'price_table'], - 'pp_default.link_field=e.link_field AND (pp_default.attribute_id=200) and pp_default.store_id=0', - [] - )->willReturnSelf(); - $websiteMock = $this->getMockBuilder(WebsiteInterface::class) - ->setMethods(['getDefaultGroup']) - ->getMockForAbstractClass(); - $this->storeManagerMock->expects($this->once()) - ->method('getWebsite') - ->with($websiteId) - ->willReturn($websiteMock); - - $groupMock = $this->getMockBuilder(\Magento\Store\Model\Group::class) - ->setMethods(['getDefaultStoreId']) - ->disableOriginalConstructor() - ->getMock(); - $websiteMock->expects($this->once())->method('getDefaultGroup')->willReturn($groupMock); - $groupMock->expects($this->once())->method('getDefaultStoreId')->willReturn(700); - - $selectMock->expects($this->at(5)) - ->method('joinInner') - ->with( - ['product_website' => $wsTable], - 'product_website.product_id=rp.product_id ' - . 'AND product_website.website_id = rp.website_id ' - . 'AND product_website.website_id=' - . $websiteId, - [] - )->willReturnSelf(); - $selectMock->expects($this->at(6)) - ->method('joinLeft') - ->with( - ['pp' . $websiteId => 'price_table'], - 'pp55.link_field=e.link_field AND (pp55.attribute_id=200) and pp55.store_id=700', - [] - )->willReturnSelf(); - - $connectionMock->expects($this->once()) - ->method('getIfNullSql') - ->with('pp55.value', 'pp_default.value') - ->willReturn('IF NULL SQL'); - $selectMock->expects($this->at(7)) - ->method('columns') - ->with(['default_price' => 'IF NULL SQL']) - ->willReturnSelf(); - $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class) - ->disableOriginalConstructor() - ->getMock(); - $connectionMock->expects($this->once())->method('query')->with($selectMock)->willReturn($statementMock); - - $this->assertEquals($statementMock, $this->model->build($websiteId, $productMock, true)); - } -} From 94829efa4f48cc16f4261e43a28d3be9911869de Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 20 Sep 2019 14:04:30 -0500 Subject: [PATCH 0591/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Catalog/Model/Category.php | 2 +- .../Catalog/Model/Category/Authorization.php | 56 ++++++++++------- .../Catalog/Model/Product/Authorization.php | 20 +++++- .../Magento/Cms/Model/Page/Authorization.php | 55 +++++++++++++--- .../Controller/Adminhtml/CategoryTest.php | 63 +++++++++++++++++++ .../Controller/Adminhtml/ProductTest.php | 55 +++++++++++++++- .../Controller/Adminhtml/PageDesignTest.php | 41 +++++++++++- 7 files changed, 254 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index 421922b4eb69c..b97f092c2f2b0 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -483,7 +483,7 @@ public function getProductCollection() * Retrieve all customer attributes * * @param bool $noDesignAttributes - * @return array + * @return \Magento\Eav\Api\Data\AttributeInterface[] * @todo Use with Flat Resource * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ diff --git a/app/code/Magento/Catalog/Model/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php index a40db89e35906..d2d37226e7006 100644 --- a/app/code/Magento/Catalog/Model/Category/Authorization.php +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -41,6 +41,37 @@ public function __construct(AuthorizationInterface $authorization, CategoryFacto $this->categoryFactory = $factory; } + /** + * Determine whether a category has design properties changed. + * + * @param CategoryModel $category + * @param CategoryModel|null $oldCategory + * @return bool + */ + private function hasChanges(CategoryModel $category, ?CategoryModel $oldCategory): bool + { + foreach ($category->getDesignAttributes() as $designAttribute) { + $oldValues = [null]; + if ($oldCategory) { + //New value must match saved value exactly + $oldValues = [$oldCategory->getData($designAttribute->getAttributeCode())]; + } else { + //New value can be either empty or default value. + $oldValues[] = $designAttribute->getDefaultValue(); + } + $newValue = $category->getData($designAttribute->getAttributeCode()); + if (empty($newValue)) { + $newValue = null; + } + + if (!in_array($newValue, $oldValues, true)) { + return true; + } + } + + return false; + } + /** * Authorize saving of a category. * @@ -52,36 +83,17 @@ public function __construct(AuthorizationInterface $authorization, CategoryFacto public function authorizeSavingOf(CategoryInterface $category): void { if (!$this->authorization->isAllowed('Magento_Catalog::edit_category_design')) { - $notAllowed = false; - $designAttributeCodes = array_map( - function (AttributeInterface $attribute) { - return $attribute->getAttributeCode(); - }, - $category->getDesignAttributes() - ); - if (!$category->getId()) { - foreach ($designAttributeCodes as $attribute) { - if ($category->getData($attribute)) { - $notAllowed = true; - break; - } - } - } else { + $savedCategory = null; + if ($category->getId()) { /** @var CategoryModel $savedCategory */ $savedCategory = $this->categoryFactory->create(); $savedCategory->load($category->getId()); if (!$savedCategory->getName()) { throw NoSuchEntityException::singleField('id', $category->getId()); } - foreach ($designAttributeCodes as $attribute) { - if ($category->getData($attribute) != $savedCategory->getData($attribute)) { - $notAllowed = true; - break; - } - } } - if ($notAllowed) { + if ($this->hasChanges($category, $savedCategory)) { throw new AuthorizationException(__('Not allowed to edit the category\'s design attributes')); } } diff --git a/app/code/Magento/Catalog/Model/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php index 9a5e8fc396dd5..8147ecf38c84a 100644 --- a/app/code/Magento/Catalog/Model/Product/Authorization.php +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -58,9 +58,25 @@ private function hasProductChanged(ProductModel $product, ?ProductModel $oldProd 'custom_design_to', 'custom_layout_update_file' ]; + $attributes = null; + if (!$oldProduct) { + //For default values. + $attributes = $product->getAttributes(); + } foreach ($designAttributes as $designAttribute) { - $oldValue = $oldProduct ? $oldProduct->getData($designAttribute) : null; - if ($product->getData($designAttribute) != $oldValue) { + $oldValues = [null]; + if ($oldProduct) { + //New value may only be the saved value + $oldValues = [$oldProduct->getData($designAttribute)]; + } elseif (array_key_exists($designAttribute, $attributes)) { + //New value can be empty or default + $oldValues[] = $attributes[$designAttribute]->getDefaultValue(); + } + $newValue = $product->getData($designAttribute); + if (empty($newValue)) { + $newValue = null; + } + if (!in_array($newValue, $oldValues, true)) { return true; } } diff --git a/app/code/Magento/Cms/Model/Page/Authorization.php b/app/code/Magento/Cms/Model/Page/Authorization.php index 0ab63bb4901bc..9075141ce15b5 100644 --- a/app/code/Magento/Cms/Model/Page/Authorization.php +++ b/app/code/Magento/Cms/Model/Page/Authorization.php @@ -12,7 +12,9 @@ use Magento\Cms\Api\PageRepositoryInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use \Magento\Store\Model\StoreManagerInterface; /** * Authorization for saving a page. @@ -29,16 +31,32 @@ class Authorization */ private $authorization; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param PageRepositoryInterface $pageRepository * @param AuthorizationInterface $authorization + * @param ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager */ public function __construct( PageRepositoryInterface $pageRepository, - AuthorizationInterface $authorization + AuthorizationInterface $authorization, + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager ) { $this->pageRepository = $pageRepository; $this->authorization = $authorization; + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; } /** @@ -47,24 +65,41 @@ public function __construct( * @param PageInterface $page * @param PageInterface|null $oldPage * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ private function hasPageChanged(PageInterface $page, ?PageInterface $oldPage): bool { + if (!$oldPage) { + $oldPageLayout = $this->scopeConfig->getValue( + 'web/default_layouts/default_cms_layout', + ScopeInterface::SCOPE_STORE, + $this->storeManager->getStore() + ); + if ($page->getPageLayout() && $page->getPageLayout() !== $oldPageLayout) { + //If page layout is set and it's not a default value - design attributes are changed. + return true; + } + //Otherwise page layout is empty and is OK to save. + $oldPageLayout = $page->getPageLayout(); + } else { + //Compare page layout to saved value. + $oldPageLayout = $oldPage->getPageLayout(); + } + //Compare new values to saved values or require them to be empty $oldUpdateXml = $oldPage ? $oldPage->getLayoutUpdateXml() : null; - $oldPageLayout = $oldPage ? $oldPage->getPageLayout() : null; $oldCustomTheme = $oldPage ? $oldPage->getCustomTheme() : null; $oldLayoutUpdate = $oldPage ? $oldPage->getCustomLayoutUpdateXml() : null; $oldThemeFrom = $oldPage ? $oldPage->getCustomThemeFrom() : null; $oldThemeTo = $oldPage ? $oldPage->getCustomThemeTo() : null; - if ($page->getLayoutUpdateXml() !== $oldUpdateXml - || $page->getPageLayout() !== $oldPageLayout - || $page->getCustomTheme() !== $oldCustomTheme - || $page->getCustomLayoutUpdateXml() !== $oldLayoutUpdate - || $page->getCustomThemeFrom() !== $oldThemeFrom - || $page->getCustomThemeTo() !== $oldThemeTo + if ($page->getLayoutUpdateXml() != $oldUpdateXml + || $page->getPageLayout() != $oldPageLayout + || $page->getCustomTheme() != $oldCustomTheme + || $page->getCustomLayoutUpdateXml() != $oldLayoutUpdate + || $page->getCustomThemeFrom() != $oldThemeFrom + || $page->getCustomThemeTo() != $oldThemeTo ) { return true; } @@ -78,7 +113,7 @@ private function hasPageChanged(PageInterface $page, ?PageInterface $oldPage): b * @param PageInterface $page * @return void * @throws AuthorizationException - * @throws LocalizedException When it is impossible to perform authorization for given page. + * @throws \Magento\Framework\Exception\LocalizedException When it is impossible to perform authorization. */ public function authorizeFor(PageInterface $page): void { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index 1faff4bc03ffa..b1cbc1f544109 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -8,6 +8,7 @@ use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Registry; use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\Store; @@ -647,6 +648,68 @@ public function testSaveDesign(): void ); } + /** + * Save design attributes with default values without design permissions. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @return void + * @throws \Throwable + */ + public function testSaveDesignWithDefaults(): void + { + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $attributes = $category->getAttributes(); + $attributes['custom_design']->setDefaultValue('1'); + $attributes['custom_design']->save(); + $requestData = [ + 'name' => 'Test name', + 'parent_id' => '2', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category-test', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + 'custom_design' => '1', + 'custom_apply_to_products' => '0' + ]; + $uri = 'backend/catalog/category/save'; + + //Updating the category's design settings without proper permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->dispatch($uri); + + //Verifying that category was saved. + /** @var Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(Registry::class); + $id = $registry->registry('current_category')->getId(); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($id); + $this->assertNotEmpty($category->getId()); + $this->assertEquals('1', $category->getData('custom_design')); + } + /** * Test custom update files functionality. * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index c5537c89b78d0..d6f82ccaea648 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Catalog\Controller\Adminhtml; -use Magento\Catalog\Model\Category as CategoryModel; use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; @@ -13,9 +12,9 @@ use Magento\Catalog\Model\ProductRepository; use Magento\Catalog\Model\ProductRepositoryFactory; use Magento\Framework\Message\MessageInterface; -use Magento\Store\Model\Store; use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; /** * @magentoAppArea adminhtml @@ -32,6 +31,11 @@ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendControl */ private $repositoryFactory; + /** + * @var ProductResource + */ + private $resourceModel; + /** * @inheritDoc */ @@ -41,6 +45,7 @@ protected function setUp() $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); $this->repositoryFactory = Bootstrap::getObjectManager()->get(ProductRepositoryFactory::class); + $this->resourceModel = Bootstrap::getObjectManager()->get(ProductResource::class); } /** @@ -446,6 +451,52 @@ public function testSaveDesign(): void ); } + /** + * Save design without the permissions but with default values. + * + * @magentoDbIsolation enabled + * @throws \Throwable + * @return void + */ + public function testSaveDesignWithDefaults(): void + { + $optionsContainerDefault = $this->resourceModel->getAttribute('options_container')->getDefaultValue(); + $requestData = [ + 'product' => [ + 'type' => 'simple', + 'sku' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'product' => [], + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]', + 'options_container' => $optionsContainerDefault + ] + ]; + $uri = 'backend/catalog/product/save'; + + //Updating product's design settings without proper permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($uri); + + //Validating saved entity. + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertNotNull($product->getData('options_container')); + $this->assertEquals($optionsContainerDefault, $product->getData('options_container')); + } + /** * Test custom update files functionality. * diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php index 1d47ebc8aeca6..a13dc4e1f200b 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php @@ -13,6 +13,7 @@ use Magento\Cms\Model\Page; use Magento\Cms\Model\PageFactory; use Magento\Framework\Acl\Builder; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -50,6 +51,11 @@ class PageDesignTest extends AbstractBackendController */ private $pageRetriever; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @inheritDoc */ @@ -59,6 +65,7 @@ protected function setUp() $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); $this->pageRetriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); + $this->scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class); } /** @@ -76,7 +83,8 @@ public function testSaveDesign(): void $requestData = [ PageInterface::IDENTIFIER => $id, PageInterface::TITLE => 'Page title', - PageInterface::CUSTOM_THEME => '1' + PageInterface::CUSTOM_THEME => '1', + PageInterface::PAGE_LAYOUT => 'empty' ]; //Creating a new page with design properties without the required permissions. @@ -119,6 +127,37 @@ public function testSaveDesign(): void ); } + /** + * Check that default design values are accepted without the permissions. + * + * @magentoDbIsolation enabled + * @return void + */ + public function testSaveDesignWithDefaults(): void + { + //Test page data. + $id = 'test-page'; + $defaultLayout = $this->scopeConfig->getValue('web/default_layouts/default_cms_layout'); + $requestData = [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::PAGE_LAYOUT => $defaultLayout + ]; + //Creating a new page with design properties without the required permissions but with default values. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + + //Validating saved page + /** @var Page $page */ + $page = Bootstrap::getObjectManager()->create(PageInterface::class); + $page->load($id, PageInterface::IDENTIFIER); + $this->assertNotEmpty($page->getId()); + $this->assertNotNull($page->getPageLayout()); + $this->assertEquals($defaultLayout, $page->getPageLayout()); + } + /** * Test that custom layout update fields are dealt with properly. * From e3328a5ece7679ffea92b4fb4a6b7d2fe16edd5c Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 20 Sep 2019 17:00:45 -0500 Subject: [PATCH 0592/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Catalog/Model/Category/Authorization.php | 3 +++ app/code/Magento/Catalog/Model/Product/Authorization.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php index d2d37226e7006..5529b067df3a9 100644 --- a/app/code/Magento/Catalog/Model/Category/Authorization.php +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -55,6 +55,9 @@ private function hasChanges(CategoryModel $category, ?CategoryModel $oldCategory if ($oldCategory) { //New value must match saved value exactly $oldValues = [$oldCategory->getData($designAttribute->getAttributeCode())]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } } else { //New value can be either empty or default value. $oldValues[] = $designAttribute->getDefaultValue(); diff --git a/app/code/Magento/Catalog/Model/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php index 8147ecf38c84a..2500330e14968 100644 --- a/app/code/Magento/Catalog/Model/Product/Authorization.php +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -68,6 +68,9 @@ private function hasProductChanged(ProductModel $product, ?ProductModel $oldProd if ($oldProduct) { //New value may only be the saved value $oldValues = [$oldProduct->getData($designAttribute)]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } } elseif (array_key_exists($designAttribute, $attributes)) { //New value can be empty or default $oldValues[] = $attributes[$designAttribute]->getDefaultValue(); From 2fe3c1dd15b905c4b0c594ec47753277a3ba5133 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Wed, 18 Sep 2019 10:58:11 -0400 Subject: [PATCH 0593/2437] Add Merge Cart Mutation * Merge two guest carts * Merge customer cart with guest cart * Merge customer cart with inactive customer cart Fixes magento/graphql-ce#905 --- .../QuoteGraphQl/Model/Cart/GetCart.php | 95 ++++++ .../Model/Cart/GetCartForUser.php | 46 +-- .../QuoteGraphQl/Model/Cart/MergeCarts.php | 91 ++++++ .../Model/Resolver/MergeCarts.php | 65 +++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 6 + .../GraphQl/Quote/Customer/MergeCartsTest.php | 271 ++++++++++++++++++ .../GraphQl/Quote/Guest/MergeCartsTest.php | 138 +++++++++ 7 files changed, 672 insertions(+), 40 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/GetCart.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/MergeCarts.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCart.php new file mode 100644 index 0000000000000..4f414ac1de3d9 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCart.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\Quote; + +/** + * Get cart merge + */ +class GetCart +{ + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param CartRepositoryInterface $cartRepository + */ + public function __construct( + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + CartRepositoryInterface $cartRepository + ) { + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->cartRepository = $cartRepository; + } + + /** + * Get cart for merge + * + * @param string $cartHash + * @param int|null $customerId + * @param int $storeId + * @return Quote + * @throws GraphQlNoSuchEntityException + * @throws NoSuchEntityException + */ + public function execute(string $cartHash, ?int $customerId, int $storeId): Quote + { + try { + $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) + ); + } + + try { + /** @var Quote $cart */ + $cart = $this->cartRepository->get($cartId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException( + __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) + ); + } + + if ((int)$cart->getStoreId() !== $storeId) { + throw new GraphQlNoSuchEntityException( + __( + 'Wrong store code specified for cart "%masked_cart_id"', + ['masked_cart_id' => $cartHash] + ) + ); + } + + $cartCustomerId = (int)$cart->getCustomerId(); + + /* Guest cart, allow operations */ + if (0 === $cartCustomerId) { + return $cart; + } + + if ($cartCustomerId !== $customerId) { + throw new GraphQlNoSuchEntityException( + __('The current user cannot perform operations on cart "%masked_cart_id"', ['masked_cart_id' => $cartHash]) + ); + } + return $cart; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index af70809a1053d..67e1a0df608b1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -10,8 +10,6 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; /** @@ -20,25 +18,17 @@ class GetCartForUser { /** - * @var MaskedQuoteIdToQuoteIdInterface + * @var GetCart */ - private $maskedQuoteIdToQuoteId; + private $getCart; /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId - * @param CartRepositoryInterface $cartRepository + * @param GetCart $getCart */ public function __construct( - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository + GetCart $getCart ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; - $this->cartRepository = $cartRepository; + $this->getCart = $getCart; } /** @@ -54,22 +44,7 @@ public function __construct( */ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote { - try { - $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) - ); - } - - try { - /** @var Quote $cart */ - $cart = $this->cartRepository->get($cartId); - } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) - ); - } + $cart = $this->getCart->execute($cartHash, $customerId, $storeId); if (false === (bool)$cart->getIsActive()) { throw new GraphQlNoSuchEntityException( @@ -77,15 +52,6 @@ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote ); } - if ((int)$cart->getStoreId() !== $storeId) { - throw new GraphQlNoSuchEntityException( - __( - 'Wrong store code specified for cart "%masked_cart_id"', - ['masked_cart_id' => $cartHash] - ) - ); - } - $cartCustomerId = (int)$cart->getCustomerId(); /* Guest cart, allow operations */ diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/MergeCarts.php b/app/code/Magento/QuoteGraphQl/Model/Cart/MergeCarts.php new file mode 100644 index 0000000000000..f844a23613984 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/MergeCarts.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; +use Magento\Quote\Api\CartRepositoryInterface; + +/** + * Merge two carts + */ +class MergeCarts +{ + /** + * @var QuoteIdMaskFactory + */ + private $quoteMaskFactory; + + /** + * @var QuoteIdMaskResourceModel + */ + private $quoteMaskResource; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @param QuoteIdMaskFactory $quoteMaskFactory + * @param QuoteIdMaskResourceModel $quoteMaskResource + * @param CartRepositoryInterface $cartRepository + */ + public function __construct( + QuoteIdMaskFactory $quoteMaskFactory, + QuoteIdMaskResourceModel $quoteMaskResource, + CartRepositoryInterface $cartRepository + ) { + $this->quoteMaskFactory = $quoteMaskFactory; + $this->quoteMaskResource = $quoteMaskResource; + $this->cartRepository = $cartRepository; + } + + /** + * Merge two quotes + * + * @param Quote $firstCart + * @param Quote $secondQuote + * @return string + */ + public function execute(Quote $firstCart, Quote $secondQuote): string + { + $firstCart->merge($secondQuote); + $firstCart->setIsActive(true); + + $this->updateMaskedId($secondQuote); + $maskedQuoteId = $this->updateMaskedId($firstCart); + + $this->cartRepository->save($firstCart); + + $secondQuote->setIsActive(false); + $this->cartRepository->save($secondQuote); + + return $maskedQuoteId; + } + + /** + * Update quote masked id + * + * @param Quote $quote + * @return string + */ + private function updateMaskedId(Quote $quote): string + { + /** @var QuoteIdMask $quoteIdMask */ + $quoteIdMask = $this->quoteMaskFactory->create(); + $this->quoteMaskResource->load($quoteIdMask, $quote->getId(), 'quote_id'); + $quoteIdMask->unsetData('masked_id'); + $this->quoteMaskResource->save($quoteIdMask); + $maskedId = $quoteIdMask->getMaskedId(); + + return $maskedId; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php new file mode 100644 index 0000000000000..6e440d8c2be97 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\QuoteGraphQl\Model\Cart\GetCart; +use Magento\QuoteGraphQl\Model\Cart\MergeCarts as MergeCartsModel; + +/** + * @inheritdoc + */ +class MergeCarts implements ResolverInterface +{ + /** + * @var GetCart + */ + private $getCart; + + /** + * @var MergeCartsModel + */ + private $mergeCarts; + + /** + * @param GetCart $getCart + * @param MergeCartsModel $mergeCarts + */ + public function __construct( + GetCart $getCart, + MergeCartsModel $mergeCarts + ) { + $this->getCart = $getCart; + $this->mergeCarts = $mergeCarts; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (empty($args['input']['first_cart_id'])) { + throw new GraphQlInputException(__('Required parameter "first_cart_id" is missing')); + } + if (empty($args['input']['second_cart_id'])) { + throw new GraphQlInputException(__('Required parameter "second_cart_id" is missing')); + } + + $currentUserId = $context->getUserId(); + $storeId = $storeId = (int) $context->getExtensionAttributes()->getStore()->getId(); + $firstCart = $this->getCart->execute($args['input']['first_cart_id'], $currentUserId, $storeId); + $secondCart = $this->getCart->execute($args['input']['second_cart_id'], $currentUserId, $storeId); + + $maskedQuoteId = $this->mergeCarts->execute($firstCart, $secondCart); + + return $maskedQuoteId; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index a86eea46aa864..753cabc587a11 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -20,6 +20,7 @@ type Mutation { setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart") setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder") placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder") + mergeCarts(input: MergeCartsInput): String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts") } input createEmptyCartInput { @@ -146,6 +147,11 @@ input SetGuestEmailOnCartInput { email: String! } +input MergeCartsInput { + first_cart_id: String! + second_cart_id: String! +} + type CartPrices { grand_total: Money subtotal_including_tax: Money diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php new file mode 100644 index 0000000000000..f6428243905b6 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php @@ -0,0 +1,271 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Integration\Api\CustomerTokenServiceInterface; + +/** + * Test for merging customer carts + */ +class MergeCartsTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + protected function tearDown() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, '1', 'customer_id'); + $this->quoteResource->delete($quote); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testMergeGuestWithCustomerCart() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load($secondQuote, 'test_order_with_virtual_product_without_address', 'reserved_order_id'); + + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $mergeResponse = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('mergeCarts', $mergeResponse); + $maskedQuoteId = $mergeResponse['mergeCarts']; + self::assertNotEquals($firstMaskedId, $maskedQuoteId); + self::assertNotEquals($secondMaskedId, $maskedQuoteId); + + $cartResponse = $this->graphQlMutation($this->getCartQuery($maskedQuoteId), [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $cartResponse); + self::assertArrayHasKey('items', $cartResponse['cart']); + self::assertCount(2, $cartResponse['cart']['items']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php + */ + public function testMergeTwoCustomerCarts() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + + $createCartResponse = $this->graphQlMutation($this->getCreateEmptyCartMutation(), [], '', $this->getHeaderMap()); + self::assertArrayHasKey('createEmptyCart', $createCartResponse); + $secondMaskedId = $createCartResponse['createEmptyCart']; + $this->addSimpleProductToCart($secondMaskedId, $this->getHeaderMap()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $mergeResponse = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('mergeCarts', $mergeResponse); + $maskedQuoteId = $mergeResponse['mergeCarts']; + self::assertNotEquals($firstMaskedId, $maskedQuoteId); + self::assertNotEquals($secondMaskedId, $maskedQuoteId); + + $cartResponse = $this->graphQlMutation($this->getCartQuery($maskedQuoteId), [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $cartResponse); + self::assertArrayHasKey('items', $cartResponse['cart']); + self::assertCount(1, $cartResponse['cart']['items']); + + $item = $cartResponse['cart']['items'][0]; + self::assertArrayHasKey('quantity', $item); + self::assertArrayHasKey('product', $item); + self::assertArrayHasKey('sku', $item['product']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/two_customers.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot perform operations on cart + */ + public function testMergeOtherCustomerCart() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); + + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + $createCartResponse = $this->graphQlMutation($this->getCreateEmptyCartMutation(), [], '', $this->getHeaderMap('customer_two@example.com')); + self::assertArrayHasKey('createEmptyCart', $createCartResponse); + $secondMaskedId = $createCartResponse['createEmptyCart']; + $this->addSimpleProductToCart($secondMaskedId, $this->getHeaderMap('customer_two@example.com')); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * Add simple product to cart + * + * @param string $maskedId + * @param array $headerMap + */ + private function addSimpleProductToCart(string $maskedId, array $headerMap): void + { + $result = $this->graphQlMutation($this->getAddProductToCartMutation($maskedId), [], '', $headerMap); + self::assertArrayHasKey('addSimpleProductsToCart', $result); + self::assertArrayHasKey('cart', $result['addSimpleProductsToCart']); + self::assertArrayHasKey('items', $result['addSimpleProductsToCart']['cart']); + self::assertArrayHasKey(0, $result['addSimpleProductsToCart']['cart']['items']); + self::assertArrayHasKey('quantity', $result['addSimpleProductsToCart']['cart']['items'][0]); + self::assertEquals(1, $result['addSimpleProductsToCart']['cart']['items'][0]['quantity']); + self::assertArrayHasKey('product', $result['addSimpleProductsToCart']['cart']['items'][0]); + self::assertArrayHasKey('sku', $result['addSimpleProductsToCart']['cart']['items'][0]['product']); + self::assertEquals('simple_product', $result['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); + } + + /** + * Create the mergeCart mutation + * + * @param string $firstMaskedId + * @param string $secondMaskedId + * @return string + */ + private function getCartMergeMutation(string $firstMaskedId, string $secondMaskedId): string + { + return <<<QUERY +mutation { + mergeCarts(input: { + first_cart_id: "{$firstMaskedId}" + second_cart_id: "{$secondMaskedId}" + }) +} +QUERY; + } + + /** + * Get cart query + * + * @param string $maskedId + * @return string + */ + private function getCartQuery(string $maskedId): string + { + return <<<QUERY +{ + cart(cart_id: "{$maskedId}") { + items { + quantity + product { + sku + } + } + } +} +QUERY; + } + + /** + * Get create empty cart mutation + * + * @return string + */ + private function getCreateEmptyCartMutation(): string + { + return <<<QUERY +mutation { + createEmptyCart +} +QUERY; + } + + /** + * Get add product to cart mutation + * + * @param string $maskedId + * @return string + */ + private function getAddProductToCartMutation(string $maskedId): string + { + return <<<QUERY +mutation { + addSimpleProductsToCart(input: { + cart_id: "{$maskedId}" + cart_items: { + data: { + quantity: 1 + sku: "simple_product" + } + } + }) { + cart { + items { + quantity + product { + sku + } + } + } + } +} +QUERY; + } + + /** + * @param string $username + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php new file mode 100644 index 0000000000000..349e8058ee2c7 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for merging guest carts + */ +class MergeCartsTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testMergeGuestCarts() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load($secondQuote, 'test_order_with_virtual_product_without_address', 'reserved_order_id'); + + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $mergeResponse = $this->graphQlMutation($query); + + self::assertArrayHasKey('mergeCarts', $mergeResponse); + $maskedQuoteId = $mergeResponse['mergeCarts']; + self::assertNotEquals($firstMaskedId, $maskedQuoteId); + self::assertNotEquals($secondMaskedId, $maskedQuoteId); + + $cartResponse = $this->graphQlMutation($this->getCartQuery($maskedQuoteId)); + + self::assertArrayHasKey('cart', $cartResponse); + self::assertArrayHasKey('items', $cartResponse['cart']); + self::assertCount(2, $cartResponse['cart']['items']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot perform operations on cart + */ + public function testMergeGuestWithCustomerCart() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_order_with_virtual_product_without_address', 'reserved_order_id'); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load($secondQuote, 'test_quote', 'reserved_order_id'); + + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $this->graphQlMutation($query); + } + + /** + * Create the mergeCart mutation + * + * @param string $firstMaskedId + * @param string $secondMaskedId + * @return string + */ + private function getCartMergeMutation(string $firstMaskedId, string $secondMaskedId): string + { + return <<<QUERY +mutation { + mergeCarts(input: { + first_cart_id: "{$firstMaskedId}" + second_cart_id: "{$secondMaskedId}" + }) +} +QUERY; + } + + /** + * Get cart query + * + * @param string $maskedId + * @return string + */ + private function getCartQuery(string $maskedId): string + { + return <<<QUERY +{ + cart(cart_id: "{$maskedId}") { + items { + quantity + product { + sku + } + } + } +} +QUERY; + } +} From 6a61d0475c285a60f81a2a0415793c5074c7f9cc Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Sat, 21 Sep 2019 08:03:02 -0400 Subject: [PATCH 0594/2437] fix static tests --- .../GraphQl/Quote/Customer/MergeCartsTest.php | 20 ++++++++++++++++--- .../GraphQl/Quote/Guest/MergeCartsTest.php | 6 +++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php index f6428243905b6..525c3731aa7e2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php @@ -68,7 +68,11 @@ public function testMergeGuestWithCustomerCart() $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); $secondQuote = $this->quoteFactory->create(); - $this->quoteResource->load($secondQuote, 'test_order_with_virtual_product_without_address', 'reserved_order_id'); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); @@ -101,7 +105,12 @@ public function testMergeTwoCustomerCarts() $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); - $createCartResponse = $this->graphQlMutation($this->getCreateEmptyCartMutation(), [], '', $this->getHeaderMap()); + $createCartResponse = $this->graphQlMutation( + $this->getCreateEmptyCartMutation(), + [], + '', + $this->getHeaderMap() + ); self::assertArrayHasKey('createEmptyCart', $createCartResponse); $secondMaskedId = $createCartResponse['createEmptyCart']; $this->addSimpleProductToCart($secondMaskedId, $this->getHeaderMap()); @@ -140,7 +149,12 @@ public function testMergeOtherCustomerCart() $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); - $createCartResponse = $this->graphQlMutation($this->getCreateEmptyCartMutation(), [], '', $this->getHeaderMap('customer_two@example.com')); + $createCartResponse = $this->graphQlMutation( + $this->getCreateEmptyCartMutation(), + [], + '', + $this->getHeaderMap('customer_two@example.com') + ); self::assertArrayHasKey('createEmptyCart', $createCartResponse); $secondMaskedId = $createCartResponse['createEmptyCart']; $this->addSimpleProductToCart($secondMaskedId, $this->getHeaderMap('customer_two@example.com')); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php index 349e8058ee2c7..3be368c06a917 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php @@ -51,7 +51,11 @@ public function testMergeGuestCarts() $this->quoteResource->load($firstQuote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); $secondQuote = $this->quoteFactory->create(); - $this->quoteResource->load($secondQuote, 'test_order_with_virtual_product_without_address', 'reserved_order_id'); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); From 9d54005fc99c1a0aeddb228563ee913ccb7a9e6f Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Sat, 21 Sep 2019 13:49:53 -0400 Subject: [PATCH 0595/2437] Fix test `base_amount` is always returned as null https://github.com/magento/graphql-ce/blob/4013a390348bf15ae48ddcaeecadae402351cf33/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/AvailableShippingMethods.php#L100 --- .../GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php index 13c63929adca8..0d64d73965d2b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php @@ -67,6 +67,7 @@ public function testGetAvailableShippingMethods() 'value' => 10, 'currency' => 'USD', ], + 'base_amount' => null, ]; self::assertEquals( $expectedAddressData, From d562df9da428dd249d80bfc38e86c5e9c3decfbc Mon Sep 17 00:00:00 2001 From: Andrey Legayev <andrey@ven.com> Date: Sat, 21 Sep 2019 21:48:05 +0300 Subject: [PATCH 0596/2437] Static Content Deploy - Optimize files scanning performance According to XDebug profiling Magento2 calls preg_match() and preg_quote() unreasonable amount of times during static content deploy. I've debugged it and found root cause. This is current algorithm: 1. Get list of all modules 2. Get array of all files in all modules 3. Now try to guess which file belongs to which module by using dynamic regex: preg_quote(), preg_match() I've refactored it to avoid mixing all files in one array and then splitting again. Quite simple fix. --- .../Magento/Framework/App/Utility/Files.php | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index 3460faf854bac..8c95ede7f2e50 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -925,14 +925,12 @@ public function getStaticPreProcessingFiles($filePattern = '*') $area = '*'; $locale = '*'; $result = []; - $moduleWebPath = []; $moduleLocalePath = []; foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleDir) { - $moduleWebPath[] = $moduleDir . "/view/{$area}/web"; $moduleLocalePath[] = $moduleDir . "/view/{$area}/web/i18n/{$locale}"; } - $this->_accumulateFilesByPatterns($moduleWebPath, $filePattern, $result, '_parseModuleStatic'); + $this->_accumulateStaticFiles($area, $filePattern, $result); $this->_accumulateFilesByPatterns($moduleLocalePath, $filePattern, $result, '_parseModuleLocaleStatic'); $this->accumulateThemeStaticFiles($area, $locale, $filePattern, $result); self::$_cache[$key] = $result; @@ -1041,25 +1039,26 @@ protected function _accumulateFilesByPatterns(array $patterns, $filePattern, arr } /** - * Parse meta-info of a static file in module + * Search static files from all modules by the specified pattern and accumulate meta-info * - * @param string $file - * @return array + * @param string $area + * @param string $filePattern + * @param array $result + * @return void */ - protected function _parseModuleStatic($file) + protected function _accumulateStaticFiles($area, $filePattern, array &$result) { - foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) { - if (preg_match( - '/^' . preg_quote("{$modulePath}/", '/') . 'view\/([a-z]+)\/web\/(.+)$/i', - $file, - $matches - ) === 1 - ) { - list(, $area, $filePath) = $matches; - return [$area, '', '', $moduleName, $filePath, $file]; + foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) { + $moduleWebPath = $moduleDir . "/view/{$area}/web"; + + foreach (self::getFiles([$moduleWebPath], $filePattern) as $absolutePath) { + $localPath = substr($absolutePath, strlen($moduleDir) + 1); + if (preg_match('/^view\/([a-z]+)\/web\/(.+)$/i', $localPath, $matches) === 1) { + list(, $parsedArea, $parsedPath) = $matches; + $result[] = [$parsedArea, '', '', $moduleName, $parsedPath, $absolutePath]; + } } } - return []; } /** From ab4e73654bb2b9eceb010e6adc3368435e696326 Mon Sep 17 00:00:00 2001 From: Sergey Dovbenko <sdovbenko@magecom.us> Date: Sun, 22 Sep 2019 16:09:20 +0000 Subject: [PATCH 0597/2437] Covered with api-functional Tests --- ...mpleProductWithCustomOptionsToCartTest.php | 58 +++++++++++++++++++ .../GetCustomOptionsValuesForQueryBySku.php | 7 ++- .../GetEmptyOptionsValuesForQueryBySku.php | 53 +++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetEmptyOptionsValuesForQueryBySku.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php index b0b116b0cddad..272c0df29e04b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php @@ -31,6 +31,11 @@ class AddSimpleProductWithCustomOptionsToCartTest extends GraphQlAbstract */ private $getCustomOptionsValuesForQueryBySku; + /** + * @var GetEmptyOptionsValuesForQueryBySku + */ + private $getEmptyOptionsValuesForQueryBySku; + /** * @inheritdoc */ @@ -40,6 +45,7 @@ protected function setUp() $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); $this->productCustomOptionsRepository = $objectManager->get(ProductCustomOptionRepositoryInterface::class); $this->getCustomOptionsValuesForQueryBySku = $objectManager->get(GetCustomOptionsValuesForQueryBySku::class); + $this->getEmptyOptionsValuesForQueryBySku = $objectManager->get(GetEmptyOptionsValuesForQueryBySku::class); } /** @@ -99,6 +105,58 @@ public function testAddSimpleProductWithMissedRequiredOptionsSet() $this->graphQlMutation($query); } + /** + * Test adding a simple product to the shopping cart with Date customizable option assigned + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_option_date.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddSimpleProductWithDateOption() + { + $sku = 'simple-product-1'; + $quantity = 1; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + + $customOptionsValues = $this->getCustomOptionsValuesForQueryBySku->execute($sku); + $queryCustomizableOptionValues = preg_replace('/"([^"]+)"\s*:\s*/', '$1:', json_encode($customOptionsValues)); + $customizableOptions = "customizable_options: {$queryCustomizableOptionValues}"; + $query = $this->getQuery($maskedQuoteId, $sku, $quantity, $customizableOptions); + + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('items', $response['addSimpleProductsToCart']['cart']); + self::assertCount(1, $response['addSimpleProductsToCart']['cart']); + + $customizableOptionOutput = $response['addSimpleProductsToCart']['cart']['items'][0]['customizable_options'][0]['values'][0]['value']; + $expectedValue = date("M d, Y", strtotime($customOptionsValues[0]['value_string'])); + + self::assertEquals($expectedValue, $customizableOptionOutput); + } + + /** + * Test adding a simple product with empty values for date option + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_option_date.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddSimpleProductWithMissedDateOptionsSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $sku = 'simple-product-1'; + $quantity = 1; + + $customOptionsValues = $this->getEmptyOptionsValuesForQueryBySku->execute($sku); + $queryCustomizableOptionValues = preg_replace('/"([^"]+)"\s*:\s*/', '$1:', json_encode($customOptionsValues)); + $customizableOptions = "customizable_options: {$queryCustomizableOptionValues}"; + $query = $this->getQuery($maskedQuoteId, $sku, $quantity, $customizableOptions); + + self::expectExceptionMessage( + 'Invalid format provided. Please use \'Y-m-d H:i:s\' format.' + ); + + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param string $sku diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsValuesForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsValuesForQueryBySku.php index 7514eb1c4e1d0..8bc17cba0bf72 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsValuesForQueryBySku.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCustomOptionsValuesForQueryBySku.php @@ -40,7 +40,12 @@ public function execute(string $sku): array foreach ($customOptions as $customOption) { $optionType = $customOption->getType(); - if ($optionType == 'field' || $optionType == 'area') { + if ($optionType == 'date') { + $customOptionsValues[] = [ + 'id' => (int)$customOption->getOptionId(), + 'value_string' => '2012-12-12 00:00:00' + ]; + } elseif ($optionType == 'field' || $optionType == 'area') { $customOptionsValues[] = [ 'id' => (int)$customOption->getOptionId(), 'value_string' => 'test' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetEmptyOptionsValuesForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetEmptyOptionsValuesForQueryBySku.php new file mode 100644 index 0000000000000..b6c0fecf0f1ce --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetEmptyOptionsValuesForQueryBySku.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; + +/** + * Generate an array with test values for customizable options based on the option type + */ +class GetEmptyOptionsValuesForQueryBySku +{ + /** + * @var ProductCustomOptionRepositoryInterface + */ + private $productCustomOptionRepository; + + /** + * @param ProductCustomOptionRepositoryInterface $productCustomOptionRepository + */ + public function __construct(ProductCustomOptionRepositoryInterface $productCustomOptionRepository) + { + $this->productCustomOptionRepository = $productCustomOptionRepository; + } + + /** + * Returns array of empty options for the product + * + * @param string $sku + * @return array + */ + public function execute(string $sku): array + { + $customOptions = $this->productCustomOptionRepository->getList($sku); + $customOptionsValues = []; + + foreach ($customOptions as $customOption) { + $optionType = $customOption->getType(); + if ($optionType == 'date') { + $customOptionsValues[] = [ + 'id' => (int)$customOption->getOptionId(), + 'value_string' => '' + ]; + } + } + + return $customOptionsValues; + } +} From 6a648202b3bf6d93f357901f31f51292982fe1d7 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 20 Sep 2019 10:40:57 -0500 Subject: [PATCH 0598/2437] MC-17518: Cart Rest API not showing all shipping assignments for multi-shipping --- .../Controller/Checkout/Plugin.php | 22 ++- .../MultishippingQuoteRepositoryPlugin.php | 159 ++++++++++++++++++ .../Plugin/ResetShippingAssigmentPlugin.php | 52 ++++++ .../Unit/Controller/Checkout/PluginTest.php | 63 +++++-- app/code/Magento/Multishipping/etc/di.xml | 3 + .../Magento/Multishipping/etc/frontend/di.xml | 3 + .../Magento/Quote/Api/CartRepositoryTest.php | 79 ++++++++- 7 files changed, 354 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php create mode 100644 app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php diff --git a/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php b/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php index f88cdfc26fa9f..f60feb2b834b9 100644 --- a/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php +++ b/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php @@ -7,36 +7,44 @@ namespace Magento\Multishipping\Controller\Checkout; +use Magento\Checkout\Model\Cart; +use Magento\Framework\App\Action\Action; + /** * Turns Off Multishipping mode for Quote. */ class Plugin { /** - * @var \Magento\Checkout\Model\Cart + * @var Cart */ - protected $cart; + private $cart; /** - * @param \Magento\Checkout\Model\Cart $cart + * @param Cart $cart */ - public function __construct(\Magento\Checkout\Model\Cart $cart) - { + public function __construct( + Cart $cart + ) { $this->cart = $cart; } /** * Disable multishipping * - * @param \Magento\Framework\App\Action\Action $subject + * @param Action $subject * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function beforeExecute(\Magento\Framework\App\Action\Action $subject) + public function beforeExecute(Action $subject) { $quote = $this->cart->getQuote(); if ($quote->getIsMultiShipping()) { $quote->setIsMultiShipping(0); + $extensionAttributes = $quote->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getShippingAssignments()) { + $extensionAttributes->setShippingAssignments([]); + } $this->cart->saveQuote(); } } diff --git a/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php b/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php new file mode 100644 index 0000000000000..db972cf9bd7c7 --- /dev/null +++ b/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php @@ -0,0 +1,159 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Multishipping\Plugin; + +use Magento\Framework\Api\SearchResultsInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\ShippingAssignment\ShippingProcessor; +use Magento\Quote\Model\ShippingAssignmentFactory; + +/** + * Plugin for multishipping quote processing in WebAPI. + */ +class MultishippingQuoteRepositoryPlugin +{ + /** + * @var ShippingAssignmentFactory + */ + private $shippingAssignmentFactory; + + /** + * @var ShippingProcessor + */ + private $shippingProcessor; + + /** + * @param ShippingAssignmentFactory $shippingAssignmentFactory + * @param ShippingProcessor $shippingProcessor + */ + public function __construct( + ShippingAssignmentFactory $shippingAssignmentFactory, + ShippingProcessor $shippingProcessor + ) { + $this->shippingAssignmentFactory = $shippingAssignmentFactory; + $this->shippingProcessor = $shippingProcessor; + } + + /** + * Process multishipping quote for get. + * + * @param CartRepositoryInterface $subject + * @param CartInterface $result + * @return CartInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGet( + CartRepositoryInterface $subject, + CartInterface $result + ) { + return $this->processQuote($result); + } + + /** + * Process multishipping quote for get list. + * + * @param CartRepositoryInterface $subject + * @param SearchResultsInterface $result + * + * @return SearchResultsInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetList( + CartRepositoryInterface $subject, + SearchResultsInterface $result + ) { + $items = []; + foreach ($result->getItems() as $item) { + $items[] = $this->processQuote($item); + } + $result->setItems($items); + + return $result; + } + + /** + * Remove shipping assignments for multishipping quote. + * + * @param CartRepositoryInterface $subject + * @param CartInterface $quote + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave(CartRepositoryInterface $subject, CartInterface $quote) + { + $extensionAttributes = $quote->getExtensionAttributes(); + if ($quote->getIsMultiShipping() && $extensionAttributes && $extensionAttributes->getShippingAssignments()) { + $quote->getExtensionAttributes()->setShippingAssignments([]); + } + + return [$quote]; + } + + /** + * Set shipping assignments for multishipping quote according to customer selection. + * + * @param CartInterface $quote + * @return CartInterface + */ + private function processQuote(CartInterface $quote): CartInterface + { + if (!$quote->getIsMultiShipping() || !$quote instanceof Quote) { + return $quote; + } + + if ($quote->getExtensionAttributes() && $quote->getExtensionAttributes()->getShippingAssignments()) { + $shippingAssignments = []; + $addresses = $quote->getAllAddresses(); + + foreach ($addresses as $address) { + $quoteItems = $this->getQuoteItems($quote, $address); + if (!empty($quoteItems)) { + $shippingAssignment = $this->shippingAssignmentFactory->create(); + $shippingAssignment->setItems($quoteItems); + $shippingAssignment->setShipping($this->shippingProcessor->create($address)); + $shippingAssignments[] = $shippingAssignment; + } + } + + if (!empty($shippingAssignments)) { + $quote->getExtensionAttributes()->setShippingAssignments($shippingAssignments); + } + } + + return $quote; + } + + /** + * Returns quote items assigned to address. + * + * @param Quote $quote + * @param Quote\Address $address + * @return Quote\Item[] + */ + private function getQuoteItems(Quote $quote, Quote\Address $address): array + { + $quoteItems = []; + foreach ($address->getItemsCollection() as $addressItem) { + $quoteItem = $quote->getItemById($addressItem->getQuoteItemId()); + if ($quoteItem) { + $multishippingQuoteItem = clone $quoteItem; + $qty = $addressItem->getQty(); + $sku = $multishippingQuoteItem->getSku(); + if (isset($quoteItems[$sku])) { + $qty += $quoteItems[$sku]->getQty(); + } + $multishippingQuoteItem->setQty($qty); + $quoteItems[$sku] = $multishippingQuoteItem; + } + } + + return array_values($quoteItems); + } +} diff --git a/app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php b/app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php new file mode 100644 index 0000000000000..376dbf723b88f --- /dev/null +++ b/app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Multishipping\Plugin; + +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor; + +/** + * Resets quote shipping assignments when item is removed from multishipping quote. + */ +class ResetShippingAssigmentPlugin +{ + /** + * @var ShippingAssignmentProcessor + */ + private $shippingAssignmentProcessor; + + /** + * @param ShippingAssignmentProcessor $shippingAssignmentProcessor + */ + public function __construct( + ShippingAssignmentProcessor $shippingAssignmentProcessor + ) { + $this->shippingAssignmentProcessor = $shippingAssignmentProcessor; + } + + /** + * Resets quote shipping assignments when item is removed from multishipping quote. + * + * @param Quote $subject + * @param mixed $itemId + * + * @return array + */ + public function beforeRemoveItem(Quote $subject, $itemId): array + { + if ($subject->getIsMultiShipping()) { + $extensionAttributes = $subject->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getShippingAssignments()) { + $shippingAssignment = $this->shippingAssignmentProcessor->create($subject); + $extensionAttributes->setShippingAssignments([$shippingAssignment]); + } + } + + return [$itemId]; + } +} diff --git a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php b/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php index a26f2661ebab1..bbcd6d85c501c 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php @@ -4,53 +4,90 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Multishipping\Test\Unit\Controller\Checkout; +use Magento\Checkout\Controller\Index\Index; +use Magento\Checkout\Model\Cart; use Magento\Multishipping\Controller\Checkout\Plugin; +use Magento\Quote\Api\Data\CartExtensionInterface; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Model\Quote; +/** + * Class PluginTest + */ class PluginTest extends \PHPUnit\Framework\TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $cartMock; + private $cartMock; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $quoteMock; + private $quoteMock; /** * @var Plugin */ - protected $object; + private $object; + /** + * @inheritdoc + */ protected function setUp() { - $this->cartMock = $this->createMock(\Magento\Checkout\Model\Cart::class); + $this->cartMock = $this->createMock(Cart::class); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, - ['__wakeUp', 'setIsMultiShipping', 'getIsMultiShipping'] + Quote::class, + ['__wakeUp', 'setIsMultiShipping', 'getIsMultiShipping', 'getExtensionAttributes'] ); - $this->cartMock->expects($this->once())->method('getQuote')->will($this->returnValue($this->quoteMock)); + $this->cartMock->expects($this->once()) + ->method('getQuote') + ->will($this->returnValue($this->quoteMock)); $this->object = new \Magento\Multishipping\Controller\Checkout\Plugin($this->cartMock); } + /** + * Tests turn off multishipping on multishipping quote. + * + * @return void + */ public function testExecuteTurnsOffMultishippingModeOnMultishippingQuote(): void { - $subject = $this->createMock(\Magento\Checkout\Controller\Index\Index::class); - $this->quoteMock->expects($this->once())->method('getIsMultiShipping')->willReturn(1); - $this->quoteMock->expects($this->once())->method('setIsMultiShipping')->with(0); - $this->cartMock->expects($this->once())->method('saveQuote'); + $subject = $this->createMock(Index::class); + $extensionAttributes = $this->createMock(CartExtensionInterface::class); + $extensionAttributes->method('getShippingAssignments') + ->willReturn( + $this->createMock(ShippingAssignmentInterface::class) + ); + $extensionAttributes->expects($this->once()) + ->method('setShippingAssignments') + ->with([]); + $this->quoteMock->method('getExtensionAttributes') + ->willReturn($extensionAttributes); + $this->quoteMock->expects($this->once()) + ->method('getIsMultiShipping')->willReturn(1); + $this->quoteMock->expects($this->once()) + ->method('setIsMultiShipping') + ->with(0); + $this->cartMock->expects($this->once()) + ->method('saveQuote'); + $this->object->beforeExecute($subject); } + /** + * Tests turn off multishipping on non-multishipping quote. + * + * @return void + */ public function testExecuteTurnsOffMultishippingModeOnNotMultishippingQuote(): void { - $subject = $this->createMock(\Magento\Checkout\Controller\Index\Index::class); + $subject = $this->createMock(Index::class); $this->quoteMock->expects($this->once())->method('getIsMultiShipping')->willReturn(0); $this->quoteMock->expects($this->never())->method('setIsMultiShipping'); $this->cartMock->expects($this->never())->method('saveQuote'); diff --git a/app/code/Magento/Multishipping/etc/di.xml b/app/code/Magento/Multishipping/etc/di.xml index 3bccf0b74bcd8..993376da83783 100644 --- a/app/code/Magento/Multishipping/etc/di.xml +++ b/app/code/Magento/Multishipping/etc/di.xml @@ -9,4 +9,7 @@ <type name="Magento\Quote\Model\Cart\CartTotalRepository"> <plugin name="multishipping_shipping_addresses" type="Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin" /> </type> + <type name="Magento\Quote\Model\QuoteRepository"> + <plugin name="multishipping_quote_repository" type="Magento\Multishipping\Plugin\MultishippingQuoteRepositoryPlugin" /> + </type> </config> diff --git a/app/code/Magento/Multishipping/etc/frontend/di.xml b/app/code/Magento/Multishipping/etc/frontend/di.xml index 0c2daaf45043e..3d85f4c8a447e 100644 --- a/app/code/Magento/Multishipping/etc/frontend/di.xml +++ b/app/code/Magento/Multishipping/etc/frontend/di.xml @@ -45,4 +45,7 @@ <type name="Magento\Checkout\Controller\Cart"> <plugin name="multishipping_clear_addresses" type="Magento\Multishipping\Model\Cart\Controller\CartPlugin" sortOrder="50" /> </type> + <type name="Magento\Quote\Model\Quote"> + <plugin name="multishipping_reset_shipping_assigment" type="Magento\Multishipping\Plugin\ResetShippingAssigmentPlugin"/> + </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php index 8cb82f5c8f206..7ffc4311e40ed 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php @@ -42,6 +42,9 @@ class CartRepositoryTest extends WebapiAbstract */ private $filterBuilder; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -59,8 +62,12 @@ protected function setUp() protected function tearDown() { try { + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); $cart = $this->getCart('test01'); - $cart->delete(); + $quoteRepository->delete($cart); + $cart = $this->getCart('multishipping_quote_id'); + $quoteRepository->delete($cart); } catch (\InvalidArgumentException $e) { // Do nothing if cart fixture was not used } @@ -74,18 +81,27 @@ protected function tearDown() * @return \Magento\Quote\Model\Quote * @throws \InvalidArgumentException */ - protected function getCart($reservedOrderId) + private function getCart($reservedOrderId) { - /** @var $cart \Magento\Quote\Model\Quote */ - $cart = $this->objectManager->get(\Magento\Quote\Model\Quote::class); - $cart->load($reservedOrderId, 'reserved_order_id'); - if (!$cart->getId()) { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + if (empty($items)) { throw new \InvalidArgumentException('There is no quote with provided reserved order ID.'); } - return $cart; + + return array_pop($items); } /** + * Tests successfull get cart web-api call. + * * @magentoApiDataFixture Magento/Sales/_files/quote.php */ public function testGetCart() @@ -130,6 +146,52 @@ public function testGetCart() } /** + * Tests that multishipping quote contains all addresses in shipping assignments. + * + * @magentoApiDataFixture Magento/Multishipping/Fixtures/quote_with_split_items.php + */ + public function testGetMultishippingCart() + { + $cart = $this->getCart('multishipping_quote_id'); + $cartId = $cart->getId(); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/carts/' . $cartId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => 'quoteCartRepositoryV1', + 'serviceVersion' => 'V1', + 'operation' => 'quoteCartRepositoryV1Get', + ], + ]; + + $requestData = ['cartId' => $cartId]; + $cartData = $this->_webApiCall($serviceInfo, $requestData); + + $shippingAssignments = $cart->getExtensionAttributes()->getShippingAssignments(); + foreach ($shippingAssignments as $key => $shippingAssignment) { + $address = $shippingAssignment->getShipping()->getAddress(); + $cartItem = $shippingAssignment->getItems()[0]; + $this->assertEquals( + $address->getId(), + $cartData['extension_attributes']['shipping_assignments'][$key]['shipping']['address']['id'] + ); + $this->assertEquals( + $cartItem->getSku(), + $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['sku'] + ); + $this->assertEquals( + $cartItem->getQty(), + $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['qty'] + ); + } + } + + /** + * Tests exception when cartId is not provided. + * * @expectedException \Exception * @expectedExceptionMessage No such entity with */ @@ -154,6 +216,8 @@ public function testGetCartThrowsExceptionIfThereIsNoCartWithProvidedId() } /** + * Tests carts search. + * * @magentoApiDataFixture Magento/Sales/_files/quote.php */ public function testGetList() @@ -184,6 +248,7 @@ public function testGetList() $this->searchCriteriaBuilder->addFilters([$grandTotalFilter, $subtotalFilter]); $this->searchCriteriaBuilder->addFilters([$minCreatedAtFilter]); $this->searchCriteriaBuilder->addFilters([$maxCreatedAtFilter]); + $this->searchCriteriaBuilder->addFilter('reserved_order_id', 'test01'); /** @var SortOrder $sortOrder */ $sortOrder = $this->sortOrderBuilder->setField('subtotal')->setDirection(SortOrder::SORT_ASC)->create(); $this->searchCriteriaBuilder->setSortOrders([$sortOrder]); From 1d841c378b6ae623b3ff5fd97ddf2f1f206636ca Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Mon, 23 Sep 2019 08:12:39 +0300 Subject: [PATCH 0599/2437] [Wishlist] Remove name from WishlistOutput #920 --- app/code/Magento/WishlistGraphQl/etc/module.xml | 7 ++++++- .../Magento/WishlistGraphQl/etc/schema.graphqls | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/WishlistGraphQl/etc/module.xml b/app/code/Magento/WishlistGraphQl/etc/module.xml index 337623cc85a92..c2f5b3165b2ab 100644 --- a/app/code/Magento/WishlistGraphQl/etc/module.xml +++ b/app/code/Magento/WishlistGraphQl/etc/module.xml @@ -6,5 +6,10 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_WishlistGraphQl" /> + <module name="Magento_WishlistGraphQl"> + <sequence> + <module name="Magento_Customer"/> + <module name="Magento_CustomerGraphQl"/> + </sequence> + </module> </config> diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 2aa5f03a787d0..7daf15596f19d 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -5,10 +5,21 @@ type Query { wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @doc(description: "The wishlist query returns the contents of a customer's wish list") @cache(cacheable: false) } -type WishlistOutput { +type Customer { + wishlists: Wishlist! @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @cache(cacheable: false) +} + +type WishlistOutput @doc(description: "Deprecated: 'Wishlist' type should be used instead") { + items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "Deprecated: use field `items` from type `Wishlist`"), + items_count: Int @doc(description: "Deprecated: use field `items_count` from type `Wishlist`"), + name: String @doc(description: "Deprecated."), + sharing_code: String @doc(description: "Deprecated: use field `sharing_code` from type `Wishlist`"), + updated_at: String @doc(description: "Deprecated: use field `updated_at` from type `Wishlist`") +} + +type Wishlist { items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"), items_count: Int @doc(description: "The number of items in the wish list"), - name: String @doc(description: "When multiple wish lists are enabled, the name the customer assigns to the wishlist"), sharing_code: String @doc(description: "An encrypted code that Magento uses to link to the wish list"), updated_at: String @doc(description: "The time of the last modification to the wish list") } From ffe156b4c79e2b91a61fcf622a2f106428098462 Mon Sep 17 00:00:00 2001 From: George Babarus <george.babarus@emag.ro> Date: Sun, 22 Sep 2019 17:50:34 +0300 Subject: [PATCH 0600/2437] refactor deployment config reader, change inconsistent load method to constructor structure --- .../Framework/App/DeploymentConfig/Reader.php | 12 +++---- .../Test/Unit/DeploymentConfig/ReaderTest.php | 35 ++++++++----------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php index ff7077213c5c3..9c5cd2fe12324 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php @@ -16,7 +16,6 @@ /** * Deployment configuration reader. * Loads the merged configuration from config files. - * * @see FileReader The reader for specific configuration file */ class Reader @@ -107,11 +106,9 @@ public function load($fileKey = null) } } } else { - $configFiles = $this->configFilePool->getPaths(); - $allFilesData = []; - $result = []; - foreach (array_keys($configFiles) as $fileKey) { - $configFile = $path . '/' . $this->configFilePool->getPath($fileKey); + $configFiles = $this->getFiles(); + foreach ($configFiles as $file) { + $configFile = $path . DIRECTORY_SEPARATOR . $file; if ($fileDriver->isExists($configFile)) { $fileData = include $configFile; if (!is_array($fileData)) { @@ -120,7 +117,6 @@ public function load($fileKey = null) } else { continue; } - $allFilesData[$configFile] = $fileData; if ($fileData) { $result = array_replace_recursive($result, $fileData); } @@ -136,6 +132,8 @@ public function load($fileKey = null) * @param string $pathConfig The path config * @param bool $ignoreInitialConfigFiles Whether ignore custom pools * @return array + * @throws FileSystemException + * @throws RuntimeException * @deprecated 100.2.0 Magento does not support custom config file pools since 2.2.0 version * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/ReaderTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/ReaderTest.php index 8f8399263384c..8a8bebb4d2f81 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/ReaderTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfig/ReaderTest.php @@ -43,17 +43,18 @@ protected function setUp() ->willReturn(__DIR__ . '/_files'); $this->fileDriver = $this->createMock(File::class); $this->fileDriver - ->expects($this->any()) ->method('isExists') - ->will($this->returnValueMap([ - [__DIR__ . '/_files/config.php', true], - [__DIR__ . '/_files/custom.php', true], - [__DIR__ . '/_files/duplicateConfig.php', true], - [__DIR__ . '/_files/env.php', true], - [__DIR__ . '/_files/mergeOne.php', true], - [__DIR__ . '/_files/mergeTwo.php', true], - [__DIR__ . '/_files/nonexistent.php', false] - ])); + ->willReturnMap( + [ + [__DIR__.'/_files/config.php', true], + [__DIR__.'/_files/custom.php', true], + [__DIR__.'/_files/duplicateConfig.php', true], + [__DIR__.'/_files/env.php', true], + [__DIR__.'/_files/mergeOne.php', true], + [__DIR__.'/_files/mergeTwo.php', true], + [__DIR__.'/_files/nonexistent.php', false] + ] + ); $this->driverPool = $this->createMock(DriverPool::class); $this->driverPool ->expects($this->any()) @@ -152,8 +153,9 @@ public function testLoadInvalidConfigurationFileWithFileKey() * @expectedException \Magento\Framework\Exception\RuntimeException * @expectedExceptionMessageRegExp /Invalid configuration file: \'.*\/\_files\/emptyConfig\.php\'/ * @return void + * @throws \Magento\Framework\Exception\FileSystemException */ - public function testLoadInvalidConfigurationFile() + public function testLoadInvalidConfigurationFile(): void { $fileDriver = $this->getMockBuilder(File::class) ->disableOriginalConstructor() @@ -173,7 +175,7 @@ public function testLoadInvalidConfigurationFile() $configFilePool = $this->getMockBuilder(ConfigFilePool::class) ->disableOriginalConstructor() ->getMock(); - $configFilePool->expects($this->exactly(2)) + $configFilePool->expects($this->once()) ->method('getPaths') ->willReturn( [ @@ -181,15 +183,6 @@ public function testLoadInvalidConfigurationFile() 'testConfig' => 'emptyConfig.php' ] ); - $configFilePool->expects($this->exactly(2)) - ->method('getPath') - ->withConsecutive( - [$this->identicalTo('configKeyOne')], - [$this->identicalTo('testConfig')] - )->willReturnOnConsecutiveCalls( - 'config.php', - 'emptyConfig.php' - ); $object = new Reader($this->dirList, $driverPool, $configFilePool); $object->load(); } From 83456fcfef437b489b2bba3be0b81c2a5f2c56d6 Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Mon, 23 Sep 2019 10:24:38 +0300 Subject: [PATCH 0601/2437] refactoring --- .../Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml | 2 +- .../Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml | 2 +- .../Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml index 98fd20b3368c2..0d2e26b3cf7c3 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml @@ -26,7 +26,7 @@ <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCLI command="cache:clean config" stepKey="flushCache"/> </before> <after> <!-- Delete data --> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml index 6dfccc3171758..8a816c2334da5 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml @@ -26,7 +26,7 @@ <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <magentoCLI command="cache:clean config" stepKey="flushCache"/> </before> <after> <!-- Delete data --> diff --git a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml index 032651c818b91..3fa93602e5256 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Shipping\Test\TestCase\CreateShipmentEntityTest" summary="Create Shipment for Offline Payment Methods" ticketId="MAGETWO-28708"> <variation name="CreateShipmentEntityTestVariation1" summary="Shipment with tracking number"> - <data name="tag" xsi:type="string">test_type:extended_acceptance_test, mftf_migrated:yes</data> + <data name="tag" xsi:type="string">test_type:extended_acceptance_test,mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> <data name="order/data/total_qty_ordered/0" xsi:type="string">1</data> From 6ac41538732dd5c7f748963361624555eeb76081 Mon Sep 17 00:00:00 2001 From: Andrey Legayev <andrey@ven.com> Date: Mon, 23 Sep 2019 12:02:35 +0300 Subject: [PATCH 0602/2437] Rollback removal of _parseModuleStatic() --- .../Magento/Framework/App/Utility/Files.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index 8c95ede7f2e50..a55010769ee37 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -1038,6 +1038,30 @@ protected function _accumulateFilesByPatterns(array $patterns, $filePattern, arr } } + /** + * Parse meta-info of a static file in module + * + * @deprecated Replaced with method _accumulateStaticFiles() + * + * @param string $file + * @return array + */ + protected function _parseModuleStatic($file) + { + foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) { + if (preg_match( + '/^' . preg_quote("{$modulePath}/", '/') . 'view\/([a-z]+)\/web\/(.+)$/i', + $file, + $matches + ) === 1 + ) { + list(, $area, $filePath) = $matches; + return [$area, '', '', $moduleName, $filePath, $file]; + } + } + return []; + } + /** * Search static files from all modules by the specified pattern and accumulate meta-info * @@ -1046,7 +1070,7 @@ protected function _accumulateFilesByPatterns(array $patterns, $filePattern, arr * @param array $result * @return void */ - protected function _accumulateStaticFiles($area, $filePattern, array &$result) + private function _accumulateStaticFiles($area, $filePattern, array &$result) { foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) { $moduleWebPath = $moduleDir . "/view/{$area}/web"; From 2b3319675ea917db6bae4e889bbb23d860baf964 Mon Sep 17 00:00:00 2001 From: Andrey Legayev <andrey@ven.com> Date: Mon, 23 Sep 2019 12:06:08 +0300 Subject: [PATCH 0603/2437] Keep original formatting in _parseModuleStatic() --- lib/internal/Magento/Framework/App/Utility/Files.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index a55010769ee37..c5f2053ec4076 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -1050,10 +1050,10 @@ protected function _parseModuleStatic($file) { foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) { if (preg_match( - '/^' . preg_quote("{$modulePath}/", '/') . 'view\/([a-z]+)\/web\/(.+)$/i', - $file, - $matches - ) === 1 + '/^' . preg_quote("{$modulePath}/", '/') . 'view\/([a-z]+)\/web\/(.+)$/i', + $file, + $matches + ) === 1 ) { list(, $area, $filePath) = $matches; return [$area, '', '', $moduleName, $filePath, $file]; From ada23e3d7896ef030b71f850389a994e9b695a4f Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Fri, 20 Sep 2019 16:03:30 +0300 Subject: [PATCH 0604/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MAGETWO-99376 --- .../Sales/Model/ResourceModel/OrderTest.php | 27 ++++++++++--------- .../Store/_files/store_with_long_name.php | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php index 9e102fab1d963..25f759e7b1b97 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/ResourceModel/OrderTest.php @@ -10,7 +10,11 @@ use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Event\ManagerInterface; +use Magento\Sales\Model\ResourceModel\Order\CollectionFactory as OrderCollectionFactory; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class OrderTest extends \PHPUnit\Framework\TestCase { /** @@ -59,10 +63,10 @@ protected function tearDown() $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - /** @var \Magento\Sales\Model\Order $order */ - $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); - $order->loadByIncrementId($this->orderIncrementId); - $order->delete(); + $orderCollection = $this->objectManager->create(OrderCollectionFactory::class)->create(); + foreach ($orderCollection as $order) { + $order->delete(); + } $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); @@ -140,11 +144,11 @@ public function testSaveOrder() * Check that store name with length within 255 chars can be saved in table sales_order * * @magentoDataFixture Magento/Store/_files/store_with_long_name.php + * @magentoDbIsolation disabled * @return void */ public function testSaveStoreName() { - $storeName = str_repeat('a', 220); $store = $this->storeRepository->get('test_2'); $this->storeManager->setCurrentStore($store->getId()); $eventManager = $this->objectManager->get(ManagerInterface::class); @@ -154,13 +158,10 @@ public function testSaveStoreName() $payment->setMethod('checkmo'); $order->setStoreId($store->getId())->setPayment($payment); $this->resourceModel->save($order); - $this->resourceModel->load($order, $storeName, 'store_name'); - $name = [ - 'Main Website', - 'Main Website Store', - $storeName, - ]; - $expectedStoreName = implode(PHP_EOL, $name); - $this->assertEquals($expectedStoreName, $order->getStoreName()); + $orderRepository = $this->objectManager->create(\Magento\Sales\Api\OrderRepositoryInterface::class); + $order = $orderRepository->get($order->getId()); + $this->assertEquals(255, strlen($order->getStoreName())); + $this->assertContains($store->getWebsite()->getName(), $order->getStoreName()); + $this->assertContains($store->getGroup()->getName(), $order->getStoreName()); } } diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php index c225519e2b2b8..f1beaee683b82 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/store_with_long_name.php @@ -7,7 +7,7 @@ /** @var $store \Magento\Store\Model\Store */ $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); -$storeName = str_repeat('a', 220); +$storeName = str_repeat('a', 255); if (!$store->load('test', 'code')->getId()) { $store->setData( [ From ed9fa6a3f14f72b114dc1ba0aa18f966eab9a451 Mon Sep 17 00:00:00 2001 From: Aliaksei Yakimovich2 <aliaksei_yakimovich2@epam.com> Date: Tue, 30 Jul 2019 14:43:28 +0300 Subject: [PATCH 0605/2437] MC-18824: Increase test coverage for Import / export functional area - Added integration test for MC-11760; --- .../Model/Export/ProductTest.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 183ba86ca7572..8db0f32941bd9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -404,6 +404,33 @@ public function testExportWithCustomOptions(): void self::assertSame($expectedData, $customOptionData); } + /** + * Check that no duplicate entities when multiple custom options used + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_options.php + */ + public function testExportWithMultipleOptions() + { + $expectedCount = 1; + $resultsFilename = 'export_results.csv'; + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $exportData = $this->model->export(); + + $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); + $varDirectory->writeFile($resultsFilename, $exportData); + /** @var \Magento\Framework\File\Csv $csv */ + $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class); + $data = $csv->getData($varDirectory->getAbsolutePath($resultsFilename)); + $actualCount = count($data) - 1; + + $this->assertSame($expectedCount, $actualCount); + } + /** * @param string $exportedCustomOption * @return array From 409927b7c5597f44baf84c784d8a35c01a086f6a Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 14 Aug 2019 12:47:16 +0400 Subject: [PATCH 0606/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6406 --- ...lityDifferentStoreViewsAfterImportTest.xml | 79 +++++++++++++++++++ .../Store/Test/Mftf/Data/StoreData.xml | 20 ++++- .../_data/import_productsoftwostoresdata.csv | 7 ++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml create mode 100644 dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml new file mode 100644 index 0000000000000..400ad76caf83d --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductVisibilityDifferentStoreViewsAfterImportTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import doesn't allow to set default value per store view"/> + <title value="Checking product visibility in different store views after product importing"/> + <description value="Checking product visibility in different store views after product importing"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6406"/> + <useCaseId value="MAGETWO-59265"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create English and Chinese store views--> + <comment userInput="Create English and Chinese store views" stepKey="commentCreateTwoStoreViews"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnglishStoreView"> + <argument name="StoreGroup" value="_defaultStoreGroup"/> + <argument name="customStore" value="storeViewEnglish"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createChineseStoreView"> + <argument name="StoreGroup" value="_defaultStoreGroup"/> + <argument name="customStore" value="storeViewChinese"/> + </actionGroup> + <!--Import products from file--> + <comment userInput="Import products from file" stepKey="commentImportProducts"/> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_productsoftwostoresdata.csv"/> + <argument name="importMessage" value="Created: 2, Updated: 0, Deleted: 0"/> + </actionGroup> + </before> + <after> + <!--Delete all imported products--> + <comment userInput="Delete all imported products" stepKey="commentDeleteProducts"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="adminDataGridSelectPerPage" stepKey="selectNumberOfProductsPerPage"> + <argument name="perPage" value="100"/> + </actionGroup> + <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <!--Delete store views--> + <comment userInput="Delete store views" stepKey="commentDeleteStoreViews"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteEnglishStoreView"> + <argument name="customStore" value="storeViewEnglish"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteChineseStoreView"> + <argument name="customStore" value="storeViewChinese"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Open imported name4 product--> + <comment userInput="Open imported name4 product" stepKey="commentOpenName4Product"/> + <actionGroup ref="filterAndSelectProduct" stepKey="openName4Product"> + <argument name="productSku" value="name4"/> + </actionGroup> + <!--Switch Chinese store view and assert visibility field--> + <comment userInput="Switch Chinese store view and assert visibility field" stepKey="commentAssertVisibilityChineseView"/> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomStoreView"> + <argument name="storeViewName" value="{{storeViewChinese.name}}"/> + </actionGroup> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForChineseStore"/> + <!--Switch English store view and assert visibility field--> + <comment userInput="Switch English store view and assert visibility field" stepKey="commentAssertVisibilityEnglishView"/> + <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomEnglishView"> + <argument name="storeViewName" value="{{storeViewEnglish.name}}"/> + </actionGroup> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForEnglishView"/> + </test> +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 1a1847bf38308..b0c3905c66dde 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -194,4 +194,22 @@ <data key="name">third_store_view</data> <data key="code">third_store_view</data> </entity> -</entities> \ No newline at end of file + <entity name="storeViewEnglish" type="store"> + <data key="group_id">1</data> + <data key="name">English</data> + <data key="code">english</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_type">store</data> + <data key="store_action">add</data> + </entity> + <entity name="storeViewChinese" type="store"> + <data key="group_id">1</data> + <data key="name">Chinese</data> + <data key="code">chinese</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_type">store</data> + <data key="store_action">add</data> + </entity> +</entities> diff --git a/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv b/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv new file mode 100644 index 0000000000000..5cb120e7e2b2b --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv @@ -0,0 +1,7 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,msrp_price,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id +name4,,Default,simple,,base,name4,name4,name4,0,1,None,Catalog,39,1,7/8/2015 8:00,,name4,,,,12/16/2015 6:33,7/7/2016 13:01,,,Product Info Column,,,,,,,,Use config,,,1,0,1,0,0,0,1,1,10000,1,1,1,1,1,1,1,0,1,0,0,1 +name4,english,Default,simple,,base,, ,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name4,chinese,Default,simple,,base,白瓷奶勺110厘米, ,白瓷奶勺110厘米,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name5,,Default,simple,,base,name5,name5,name5,0,1,,Catalog,229,111.75,7/15/2015 0:00,,name5,,,,12/16/2015 6:33,7/7/2016 13:01,,,Product Info Column,,,,,,,,Use config,,,0,0,1,0,2,2,1,1,10000,1,1,1,1,1,1,1,0,1,0,0,1 +name5,chinese,Default,simple,,base,盐磨瓶18厘米,,盐磨瓶18厘米,,2,None,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name5,english,Default,simple,,base,,,,,2,None,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, From 17fa6d9dad005ad38d69b1a1107f9bc220a77835 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 23 Sep 2019 13:02:50 +0300 Subject: [PATCH 0607/2437] graphQl-914: [Customer] Improve consistency of country field in customer address --- .../Address/CreateCustomerAddress.php | 3 + .../Address/UpdateCustomerAddress.php | 3 + .../CustomerGraphQl/etc/schema.graphqls | 6 +- .../Customer/CreateCustomerAddressTest.php | 99 +++++++++++++++ .../Customer/UpdateCustomerAddressTest.php | 120 ++++++++++++++++++ 5 files changed, 229 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php index 388b6dc2ea943..474bd99a8f136 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php @@ -67,6 +67,9 @@ public function __construct( */ public function execute(int $customerId, array $data): AddressInterface { + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); /** @var AddressInterface $address */ diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php index 65745a20bc8eb..26e53c7c3a0a8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php @@ -66,6 +66,9 @@ public function __construct( */ public function execute(AddressInterface $address, array $data): void { + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index d27debdc39c64..fa50ebeed09c4 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -28,7 +28,8 @@ input CustomerAddressInput { city: String @doc(description: "The city or town") region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID") postcode: String @doc(description: "The customer's ZIP or postal code") - country_id: CountryCodeEnum @doc(description: "The customer's country") + country_id: CountryCodeEnum @doc(description: "The customer's country") @deprecated(reason: "Use country_code instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") fax: String @doc(description: "The fax number") @@ -100,7 +101,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform customer_id: Int @doc(description: "The customer ID") region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID") region_id: Int @doc(description: "A number that uniquely identifies the state, province, or other area") - country_id: String @doc(description: "The customer's country") + country_id: String @doc(description: "The customer's country") @deprecated(reason: "Use country_code instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") street: [String] @doc(description: "An array of strings that define the street number and name") company: String @doc(description: "The customer's company") telephone: String @doc(description: "The telephone number") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 203e9b5cb42e5..a065ab3f26e7e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -133,6 +133,105 @@ public function testCreateCustomerAddress() $this->assertCustomerAddressesFields($address, $newAddress); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithCountryCode() + { + $customerId = 1; + $newAddress = [ + 'region' => [ + 'region' => 'Arizona', + 'region_id' => 4, + 'region_code' => 'AZ' + ], + 'country_code' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<<MUTATION +mutation { + createCustomerAddress(input: { + region: { + region: "{$newAddress['region']['region']}" + region_id: {$newAddress['region']['region_id']} + region_code: "{$newAddress['region']['region_code']}" + } + country_code: {$newAddress['country_code']} + street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}"] + company: "{$newAddress['company']}" + telephone: "{$newAddress['telephone']}" + fax: "{$newAddress['fax']}" + postcode: "{$newAddress['postcode']}" + city: "{$newAddress['city']}" + firstname: "{$newAddress['firstname']}" + lastname: "{$newAddress['lastname']}" + middlename: "{$newAddress['middlename']}" + prefix: "{$newAddress['prefix']}" + suffix: "{$newAddress['suffix']}" + vat_id: "{$newAddress['vat_id']}" + default_shipping: true + default_billing: false + }) { + id + customer_id + region { + region + region_id + region_code + } + country_id + street + company + telephone + fax + postcode + city + firstname + lastname + middlename + prefix + suffix + vat_id + default_shipping + default_billing + } +} +MUTATION; + + $userName = 'customer@example.com'; + $password = 'password'; + + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('createCustomerAddress', $response); + $this->assertArrayHasKey('customer_id', $response['createCustomerAddress']); + $this->assertEquals($customerId, $response['createCustomerAddress']['customer_id']); + $this->assertArrayHasKey('id', $response['createCustomerAddress']); + + $address = $this->addressRepository->getById($response['createCustomerAddress']['id']); + $this->assertEquals($address->getId(), $response['createCustomerAddress']['id']); + + $newAddress['country_id'] = $newAddress['country_code']; + unset($newAddress['country_code']); + $this->assertCustomerAddressesFields($address, $response['createCustomerAddress']); + $this->assertCustomerAddressesFields($address, $newAddress); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php index 9840236dc9896..b83649061c333 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php @@ -77,6 +77,34 @@ public function testUpdateCustomerAddress() $this->assertCustomerAddressesFields($address, $updateAddress); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateCustomerAddressWithCountryCode() + { + $userName = 'customer@example.com'; + $password = 'password'; + $customerId = 1; + $addressId = 1; + + $mutation = $this->getMutationWithCountryCode($addressId); + + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('updateCustomerAddress', $response); + $this->assertArrayHasKey('customer_id', $response['updateCustomerAddress']); + $this->assertEquals($customerId, $response['updateCustomerAddress']['customer_id']); + $this->assertArrayHasKey('id', $response['updateCustomerAddress']); + + $address = $this->addressRepository->getById($addressId); + $this->assertEquals($address->getId(), $response['updateCustomerAddress']['id']); + $this->assertCustomerAddressesFields($address, $response['updateCustomerAddress']); + + $updateAddress = $this->getAddressDataCanadaCountry(); + $this->assertCustomerAddressesFields($address, $updateAddress); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -405,6 +433,35 @@ private function getAddressData(): array ]; } + /** + * @return array + */ + private function getAddressDataCanadaCountry(): array + { + return [ + 'region' => [ + 'region' => 'Alberta', + 'region_id' => 66, + 'region_code' => 'AB' + ], + 'country_id' => 'CA', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company Name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => true + ]; + } + /** * @param int $addressId * @return string @@ -464,6 +521,69 @@ private function getMutation(int $addressId): string default_billing } } +MUTATION; + return $mutation; + } + + /** + * @param int $addressId + * @return string + */ + private function getMutationWithCountryCode(int $addressId): string + { + $updateAddress = $this->getAddressDataCanadaCountry(); + $defaultShippingText = $updateAddress['default_shipping'] ? "true" : "false"; + $defaultBillingText = $updateAddress['default_billing'] ? "true" : "false"; + + $mutation + = <<<MUTATION +mutation { + updateCustomerAddress(id: {$addressId}, input: { + region: { + region: "{$updateAddress['region']['region']}" + region_id: {$updateAddress['region']['region_id']} + region_code: "{$updateAddress['region']['region_code']}" + } + country_code: {$updateAddress['country_id']} + street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] + company: "{$updateAddress['company']}" + telephone: "{$updateAddress['telephone']}" + fax: "{$updateAddress['fax']}" + postcode: "{$updateAddress['postcode']}" + city: "{$updateAddress['city']}" + firstname: "{$updateAddress['firstname']}" + lastname: "{$updateAddress['lastname']}" + middlename: "{$updateAddress['middlename']}" + prefix: "{$updateAddress['prefix']}" + suffix: "{$updateAddress['suffix']}" + vat_id: "{$updateAddress['vat_id']}" + default_shipping: {$defaultShippingText} + default_billing: {$defaultBillingText} + }) { + id + customer_id + region { + region + region_id + region_code + } + country_id + street + company + telephone + fax + postcode + city + firstname + lastname + middlename + prefix + suffix + vat_id + default_shipping + default_billing + } +} MUTATION; return $mutation; } From c1e47ab07ac9b708ba9b22bca1ba6dfcf30d42e9 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 14 Aug 2019 18:38:30 +0400 Subject: [PATCH 0608/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6416 --- .../AdminImportProductsActionGroup.xml | 12 +++++++ .../Mftf/Section/AdminImportHeaderSection.xml | 1 + ...dminImportCSVWithSpecialCharactersTest.xml | 35 +++++++++++++++++++ .../acceptance/tests/_data/import91569.csv | 2 ++ 4 files changed, 50 insertions(+) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml create mode 100644 dev/tests/acceptance/tests/_data/import91569.csv diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 9063916e9f502..faa66886178dd 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -60,4 +60,16 @@ <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> </actionGroup> + <actionGroup name="AdminCheckDataForImportProductsActionGroup" extends="AdminImportProductsActionGroup"> + <arguments> + <argument name="noteMessage" type="string" defaultValue="File must be saved in UTF-8 encoding for proper import"/> + </arguments> + <remove keyForRemoval="clickImportButton"/> + <remove keyForRemoval="AdminImportMainSectionLoad2"/> + <remove keyForRemoval="assertSuccessMessage"/> + <remove keyForRemoval="AdminMessagesSection"/> + <remove keyForRemoval="seeImportMessage"/> + <see selector="{{AdminImportHeaderSection.messageNote}}" userInput="{{noteMessage}}" after="attachFileForImport" stepKey="seeNoteMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="{{importMessage}}" stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml index 748580be09406..c39ebbe04f2e1 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminImportHeaderSection"> <element name="checkDataButton" type="button" selector="#upload_button" timeout="30"/> + <element name="messageNote" type="text" selector="#import_file-note" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml new file mode 100644 index 0000000000000..ec0937e8426f1 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImportCSVWithSpecialCharactersTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Special characters in CSV return error: General system exception happened"/> + <title value="Import CSV with special characters"/> + <description value="Import CSV with special characters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6416"/> + <useCaseId value="MAGETWO-91569"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="AdminCheckDataForImportProductsActionGroup" stepKey="adminImportProducts"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import91569.csv"/> + <argument name="importMessage" value='File is valid! To start import process press "Import" button'/> + <argument name="noteMessage" value="File must be saved in UTF-8 encoding for proper import"/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/import91569.csv b/dev/tests/acceptance/tests/_data/import91569.csv new file mode 100644 index 0000000000000..5d62286ccf2ee --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import91569.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus +Mug,,Default,simple,Default Category/C1,base,Mug,<p>this is a � � � mug</p>,,,1,Taxable Goods,"Catalog, Search",30,,,,mug,Mug,Mug,Mug ,,,,,,,,,"10/1/18, 9:21 PM","10/1/18, 11:30 PM",,,Block after Info Column,,,,Use config,,,,,,,,,gift_wrapping_available=Use config,99,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,, From a6b3f9144e0d5aca1372646dc524ef3b0e3880d7 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 15 Aug 2019 16:44:07 +0400 Subject: [PATCH 0609/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6317 --- .../Catalog/Test/Mftf/Data/ProductData.xml | 14 +++++ ...UpdatingProductThroughImportingCSVTest.xml | 53 +++++++++++++++++++ .../acceptance/tests/_data/export-91544.csv | 2 + 3 files changed, 69 insertions(+) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml create mode 100644 dev/tests/acceptance/tests/_data/export-91544.csv diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index e122615eb8aa4..985057e8c58bc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -64,6 +64,20 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="SimpleProductBeforeUpdate" type="product"> + <data key="sku">simpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name">simpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey">simpleProduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="SimpleProductAfterImport1" type="product"> <data key="sku">SimpleProductForTest1</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml new file mode 100644 index 0000000000000..603bce882fbba --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import Products"/> + <title value="Check that new URL Key works after updating a product through importing CSV file"/> + <description value="Check that new URL Key works after updating a product through importing CSV file"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6317"/> + <useCaseId value="MAGETWO-91544"/> + <group value="importExport"/> + </annotations> + <before> + <!--Create Product--> + <comment userInput="Create Product" stepKey="commentCreateProduct"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProductBeforeUpdate" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete created data--> + <comment userInput="Delete created data" stepKey="commentDeleteData"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Import product form CSV file--> + <comment userInput="Import product from CSV file" stepKey="commentImportProduct"/> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProduct"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="export-91544.csv"/> + <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> + </actionGroup> + <!--Assert product's updated url--> + <comment userInput="Assert product's updated url" stepKey="commentAssertUrl"/> + <amOnPage url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="seeUpdatedUrl"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct.name$$" stepKey="assertProductName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createProduct.sku$$" stepKey="assertProductSku"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/export-91544.csv b/dev/tests/acceptance/tests/_data/export-91544.csv new file mode 100644 index 0000000000000..147d6f8ade275 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/export-91544.csv @@ -0,0 +1,2 @@ +sku,url_key +simpleProduct,simpleProd From f54fe7aa3b354b58bd6de35ba328c125135d9903 Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Fri, 16 Aug 2019 18:41:00 +0400 Subject: [PATCH 0610/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-11332 --- .../ActionGroup/AdminProductActionGroup.xml | 13 +++ .../Catalog/Test/Mftf/Data/ProductData.xml | 4 + ...utesChangedValueToEmptyAfterImportTest.xml | 80 +++++++++++++++++++ .../tests/_data/import_simple_product.csv | 2 + 4 files changed, 99 insertions(+) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml create mode 100644 dev/tests/acceptance/tests/_data/import_simple_product.csv diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 5c5ee0f9cb321..1d3b08b831ce6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -241,6 +241,19 @@ <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> </actionGroup> + <actionGroup name="FillAdminSimpleProductFormWithoutSave" extends="FillAdminSimpleProductForm"> + <remove keyForRemoval="openSeoSection"/> + <remove keyForRemoval="fillUrlKey"/> + <remove keyForRemoval="assertFieldSku"/> + <remove keyForRemoval="assertFieldPrice"/> + <remove keyForRemoval="saveProduct"/> + <remove keyForRemoval="assertSaveMessageSuccess"/> + <remove keyForRemoval="assertFieldName"/> + <remove keyForRemoval="assertFieldSku"/> + <remove keyForRemoval="assertFieldPrice"/> + <remove keyForRemoval="openSeoSectionAssert"/> + <remove keyForRemoval="assertFieldUrlKey"/> + </actionGroup> <!--Fill fields for simple product in a category in Admin, including text option with char limit--> <actionGroup name="AdminCreateSimpleProductWithTextOptionCharLimit"> <annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 985057e8c58bc..fb14e0bad26f6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -1040,6 +1040,10 @@ <entity name="productAlphabeticalB" type="product" extends="_defaultProduct"> <data key="name" unique="suffix">BBB Product</data> </entity> + <entity name="simpleProductWithShortNameAndSku" type="product" extends="defaultSimpleProduct"> + <data key="name">Simple_Product</data> + <data key="sku">testsku</data> + </entity> <entity name="productWithSpecialCharacters" type="product" extends="_defaultProduct"> <data key="name" unique="suffix">Product "!@#$%^&*()+:;\|}{][?=~` </data> <data key="nameWithSafeChars" unique="suffix">|}{][?=~` </data> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml new file mode 100644 index 0000000000000..1d3ec59788b84 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest"> + <annotations> + <stories value="Attribute importing"/> + <title value="Check that some attributes changed the value to an empty after import CSV"/> + <description value="Check that some attributes changed the value to an empty after import CSV"/> + <features value="Import/Export"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-11332"/> + <useCaseId value="MAGETWO-61593"/> + <group value="importExport"/> + <skip> + <issueId value="MC-17175" /> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="productDropDownAttribute" stepKey="productAttribute"/> + <createData entity="productAttributeOption2" stepKey="attributeOptionWithDefaultValue"> + <requiredEntity createDataKey="productAttribute"/> + </createData> + <!--Create product--> + <comment userInput="Create product" stepKey="createProduct"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <actionGroup ref="FillAdminSimpleProductFormWithoutSave" stepKey="fillProductFieldsInAdmin"> + <argument name="category" value="$$createCategory$$"/> + <argument name="simpleProduct" value="simpleProductWithShortNameAndSku"/> + </actionGroup> + <!--Select created attribute--> + <comment userInput="Select created attribute" stepKey="selectedCreatedAttribute"/> + <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="$$productAttribute.attribute_code$$"/> + </actionGroup> + <!--Check that attribute value is selected--> + <comment userInput="Check that attribute value is selected" stepKey="checkSelectedValue"/> + <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle1"/> + <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab1"/> + <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="option2" stepKey="seeAttributeValueIsSelected1"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!--Import product with add/update behavior--> + <comment userInput="Import products with add/update behavior" stepKey="importProduct"/> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_simple_product.csv"/> + <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> + </actionGroup> + </before> + <after> + <!--Delete Product and Category--> + <comment userInput="Delete Product and Category" stepKey="deleteProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductGridPageLoad"/> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductWithShortNameAndSku.name"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!--Delete attribute--> + <comment userInput="Delete attribute" stepKey="deleteAttribute"/> + <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Check that attribute value is empty after import--> + <comment userInput="Check that attribute value is empty after import" stepKey="checkAttrValueAfterImport"/> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> + <argument name="productSku" value="{{simpleProductWithShortNameAndSku.sku}}"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle2"/> + <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab2"/> + <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="" stepKey="seeAttributeValueIsSelected2"/> + </test> +</tests> diff --git a/dev/tests/acceptance/tests/_data/import_simple_product.csv b/dev/tests/acceptance/tests/_data/import_simple_product.csv new file mode 100644 index 0000000000000..1e7756008996b --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import_simple_product.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,giftcard_type,giftcard_allow_open_amount,giftcard_open_amount_min,giftcard_open_amount_max,giftcard_amount,use_config_is_redeemable,giftcard_is_redeemable,use_config_lifetime,giftcard_lifetime,use_config_allow_message,giftcard_allow_message,use_config_email_template,giftcard_email_template,associated_skus,configurable_variations,configurable_variation_labels +testsku,,Default,simple,Default Category/simpleCategory5d53a993b7ccb2,base,Simple_Product,,,,1,Taxable Goods,"Catalog, Search",560,,,,simple-product,Simple_Product,Simple_Product,Simple_Product ,,,,,,,,,"8/14/19, 6:27 AM","8/14/19, 6:27 AM",,,Block after Info Column,,,,Use config,,,,,,,Use config,,,25,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, From bce2d9d39162f3a788c3f4db4d6537baf950f800 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 4 Sep 2019 17:42:47 +0400 Subject: [PATCH 0611/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6416 --- .../Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml | 4 ++-- .../tests/_data/{import91569.csv => importSpecChars.csv} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename dev/tests/acceptance/tests/_data/{import91569.csv => importSpecChars.csv} (100%) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index ec0937e8426f1..f752cb3e7c908 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -11,7 +11,7 @@ <test name="AdminImportCSVWithSpecialCharactersTest"> <annotations> <features value="Import/Export"/> - <stories value="Special characters in CSV return error: General system exception happened"/> + <stories value="Import CSV file"/> <title value="Import CSV with special characters"/> <description value="Import CSV with special characters"/> <severity value="MAJOR"/> @@ -27,7 +27,7 @@ </after> <actionGroup ref="AdminCheckDataForImportProductsActionGroup" stepKey="adminImportProducts"> <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="import91569.csv"/> + <argument name="importFile" value="importSpecChars.csv"/> <argument name="importMessage" value='File is valid! To start import process press "Import" button'/> <argument name="noteMessage" value="File must be saved in UTF-8 encoding for proper import"/> </actionGroup> diff --git a/dev/tests/acceptance/tests/_data/import91569.csv b/dev/tests/acceptance/tests/_data/importSpecChars.csv similarity index 100% rename from dev/tests/acceptance/tests/_data/import91569.csv rename to dev/tests/acceptance/tests/_data/importSpecChars.csv From 6500f2a0f1bf0c8673a6993eab5ce57dc88816f2 Mon Sep 17 00:00:00 2001 From: Aliaksei Yakimovich2 <aliaksei_yakimovich2@epam.com> Date: Wed, 4 Sep 2019 16:07:35 +0300 Subject: [PATCH 0612/2437] MC-18824: Increase test coverage for Import / export functional area - Added integration test for MC-13653; --- .../Model/Export/ProductTest.php | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 8db0f32941bd9..4753d947e9d3c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -8,6 +8,10 @@ namespace Magento\CatalogImportExport\Model\Export; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Observer\SwitchPriceAttributeScopeOnConfigChange; +use Magento\Framework\App\Config\ReinitableConfigInterface; + /** * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @magentoAppIsolation enabled @@ -32,6 +36,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected $fileSystem; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * Stock item attributes which must be exported * @@ -69,6 +78,7 @@ protected function setUp() $this->model = $this->objectManager->create( \Magento\CatalogImportExport\Model\Export\Product::class ); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); } /** @@ -459,4 +469,70 @@ function ($input) { return $optionItems; } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoConfigFixture current_store catalog/price/scope 1 + * @magentoDbIsolation disabled + * @magentoAppArea adminhtml + */ + public function testExportProductWithTwoWebsites() + { + $globalStoreCode = 'admin'; + $secondStoreCode = 'fixture_second_store'; + + $expectedData = [ + $globalStoreCode => 10.0, + $secondStoreCode => 9.99 + ]; + + /** @var \Magento\Store\Model\Store $store */ + $store = $this->objectManager->create(\Magento\Store\Model\Store::class); + $reinitiableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + $observer = $this->objectManager->get(\Magento\Framework\Event\Observer::class); + $switchPriceScope = $this->objectManager->get(SwitchPriceAttributeScopeOnConfigChange::class); + /** @var \Magento\Catalog\Model\Product\Action $productAction */ + $productAction = $this->objectManager->create(\Magento\Catalog\Model\Product\Action::class); + /** @var \Magento\Framework\File\Csv $csv */ + $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class); + /** @var $varDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */ + $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); + $secondStore = $store->load($secondStoreCode); + + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + + $reinitiableConfig->setValue('catalog/price/scope', \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE); + $switchPriceScope->execute($observer); + + $product = $this->productRepository->get('simple'); + $productId = $product->getId(); + $productAction->updateWebsites([$productId], [$secondStore->getWebsiteId()], 'add'); + $product->setStoreId($secondStore->getId()); + $product->setPrice('9.99'); + $product->getResource()->save($product); + + $exportData = $this->model->export(); + + $varDirectory->writeFile('test_product_with_two_websites.csv', $exportData); + $data = $csv->getData($varDirectory->getAbsolutePath('test_product_with_two_websites.csv')); + + $columnNumber = array_search('price', $data[0]); + $this->assertNotFalse($columnNumber); + + $pricesData = [ + $globalStoreCode => (float)$data[1][$columnNumber], + $secondStoreCode => (float)$data[2][$columnNumber], + ]; + + self::assertSame($expectedData, $pricesData); + + $reinitiableConfig->setValue('catalog/price/scope', \Magento\Store\Model\Store::PRICE_SCOPE_GLOBAL); + $switchPriceScope->execute($observer); + } } From 983ce3e249d3f248ddd9c94f5621a156dd8ff006 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 9 Sep 2019 16:26:27 +0400 Subject: [PATCH 0613/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6317 --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 2 +- ...nURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml | 4 ++-- .../tests/_data/{export-91544.csv => simpleProductUpdate.csv} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename dev/tests/acceptance/tests/_data/{export-91544.csv => simpleProductUpdate.csv} (100%) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index fb14e0bad26f6..909a1223a048b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -68,7 +68,7 @@ <data key="sku">simpleProduct</data> <data key="type_id">simple</data> <data key="attribute_set_id">4</data> - <data key="name">simpleProduct</data> + <data key="name" unique="suffix">SimpleProduct</data> <data key="price">123.00</data> <data key="visibility">4</data> <data key="status">1</data> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml index 603bce882fbba..42af7f67ca4ee 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -35,11 +35,11 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> - <!--Import product form CSV file--> + <!--Import product from CSV file--> <comment userInput="Import product from CSV file" stepKey="commentImportProduct"/> <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProduct"> <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="export-91544.csv"/> + <argument name="importFile" value="simpleProductUpdate.csv"/> <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> </actionGroup> <!--Assert product's updated url--> diff --git a/dev/tests/acceptance/tests/_data/export-91544.csv b/dev/tests/acceptance/tests/_data/simpleProductUpdate.csv similarity index 100% rename from dev/tests/acceptance/tests/_data/export-91544.csv rename to dev/tests/acceptance/tests/_data/simpleProductUpdate.csv From 03d7471d70ad3bd2fbf481192cb2be661d76e5a1 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 9 Sep 2019 17:00:24 +0400 Subject: [PATCH 0614/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6406 --- ...ibilityDifferentStoreViewsAfterImportTest.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml index 400ad76caf83d..175a575acb188 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -11,7 +11,7 @@ <test name="AdminProductVisibilityDifferentStoreViewsAfterImportTest"> <annotations> <features value="Import/Export"/> - <stories value="Import doesn't allow to set default value per store view"/> + <stories value="Import Products"/> <title value="Checking product visibility in different store views after product importing"/> <description value="Checking product visibility in different store views after product importing"/> <severity value="CRITICAL"/> @@ -31,13 +31,6 @@ <argument name="StoreGroup" value="_defaultStoreGroup"/> <argument name="customStore" value="storeViewChinese"/> </actionGroup> - <!--Import products from file--> - <comment userInput="Import products from file" stepKey="commentImportProducts"/> - <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> - <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="import_productsoftwostoresdata.csv"/> - <argument name="importMessage" value="Created: 2, Updated: 0, Deleted: 0"/> - </actionGroup> </before> <after> <!--Delete all imported products--> @@ -58,6 +51,13 @@ </actionGroup> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> + <!--Import products from file--> + <comment userInput="Import products from file" stepKey="commentImportProducts"/> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_productsoftwostoresdata.csv"/> + <argument name="importMessage" value="Created: 2, Updated: 0, Deleted: 0"/> + </actionGroup> <!--Open imported name4 product--> <comment userInput="Open imported name4 product" stepKey="commentOpenName4Product"/> <actionGroup ref="filterAndSelectProduct" stepKey="openName4Product"> From e7f342a66e321224f360abf89b835c11718712dc Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 9 Sep 2019 17:44:18 +0400 Subject: [PATCH 0615/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-11332 --- .../ActionGroup/AdminProductActionGroup.xml | 13 ---- ...utesChangedValueToEmptyAfterImportTest.xml | 59 ++++++++++--------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 1d3b08b831ce6..5c5ee0f9cb321 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -241,19 +241,6 @@ <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> </actionGroup> - <actionGroup name="FillAdminSimpleProductFormWithoutSave" extends="FillAdminSimpleProductForm"> - <remove keyForRemoval="openSeoSection"/> - <remove keyForRemoval="fillUrlKey"/> - <remove keyForRemoval="assertFieldSku"/> - <remove keyForRemoval="assertFieldPrice"/> - <remove keyForRemoval="saveProduct"/> - <remove keyForRemoval="assertSaveMessageSuccess"/> - <remove keyForRemoval="assertFieldName"/> - <remove keyForRemoval="assertFieldSku"/> - <remove keyForRemoval="assertFieldPrice"/> - <remove keyForRemoval="openSeoSectionAssert"/> - <remove keyForRemoval="assertFieldUrlKey"/> - </actionGroup> <!--Fill fields for simple product in a category in Admin, including text option with char limit--> <actionGroup name="AdminCreateSimpleProductWithTextOptionCharLimit"> <annotations> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 1d3ec59788b84..20e4ed2257cd4 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -18,9 +18,9 @@ <testCaseId value="MC-11332"/> <useCaseId value="MAGETWO-61593"/> <group value="importExport"/> - <skip> - <issueId value="MC-17175" /> - </skip> + <!--<skip>--> + <!--<issueId value="MC-17175" />--> + <!--</skip>--> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -28,31 +28,9 @@ <createData entity="productAttributeOption2" stepKey="attributeOptionWithDefaultValue"> <requiredEntity createDataKey="productAttribute"/> </createData> - <!--Create product--> - <comment userInput="Create product" stepKey="createProduct"/> + <!--Create category--> + <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="FillAdminSimpleProductFormWithoutSave" stepKey="fillProductFieldsInAdmin"> - <argument name="category" value="$$createCategory$$"/> - <argument name="simpleProduct" value="simpleProductWithShortNameAndSku"/> - </actionGroup> - <!--Select created attribute--> - <comment userInput="Select created attribute" stepKey="selectedCreatedAttribute"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> - <argument name="attributeCode" value="$$productAttribute.attribute_code$$"/> - </actionGroup> - <!--Check that attribute value is selected--> - <comment userInput="Check that attribute value is selected" stepKey="checkSelectedValue"/> - <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle1"/> - <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab1"/> - <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="option2" stepKey="seeAttributeValueIsSelected1"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> - <!--Import product with add/update behavior--> - <comment userInput="Import products with add/update behavior" stepKey="importProduct"/> - <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> - <argument name="behavior" value="Add/Update"/> - <argument name="importFile" value="import_simple_product.csv"/> - <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> - </actionGroup> </before> <after> <!--Delete Product and Category--> @@ -68,6 +46,33 @@ <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> + <!--Create product--> + <comment userInput="Create product" stepKey="commentCreateProduct"/> + <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"/> + <actionGroup ref="fillMainProductForm" stepKey="fillProductFieldsInAdmin"> + <argument name="product" value="simpleProductWithShortNameAndSku"/> + </actionGroup> + <actionGroup ref="SetCategoryByName" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!--Select created attribute--> + <comment userInput="Select created attribute" stepKey="selectedCreatedAttribute"/> + <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="$$productAttribute.attribute_code$$"/> + </actionGroup> + <!--Check that attribute value is selected--> + <comment userInput="Check that attribute value is selected" stepKey="checkSelectedValue"/> + <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle1"/> + <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab1"/> + <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="option2" stepKey="seeAttributeValueIsSelected1"/> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!--Import product with add/update behavior--> + <comment userInput="Import products with add/update behavior" stepKey="importProduct"/> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_simple_product.csv"/> + <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> + </actionGroup> <!--Check that attribute value is empty after import--> <comment userInput="Check that attribute value is empty after import" stepKey="checkAttrValueAfterImport"/> <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> From ff811e6acfa1e65da0137ec63d42bf4dabac6b45 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Thu, 29 Aug 2019 16:02:16 +0300 Subject: [PATCH 0616/2437] MC-18824: Increase test coverage for Import / export functional area - Integration test for MC-6348 --- .../Model/Import/ProductTest.php | 34 ++++++++++++++++++- .../_files/import_media_hidden_images.csv | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 1b33cd695d06e..6587f005aed4f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /** * Test class for \Magento\CatalogImportExport\Model\Import\Product * * The "CouplingBetweenObjects" warning is caused by tremendous complexity of the original class - * */ namespace Magento\CatalogImportExport\Model\Import; @@ -20,6 +20,7 @@ use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Filesystem; use Magento\Framework\Registry; @@ -846,6 +847,37 @@ public function testSaveMediaImage() $this->assertEquals('Additional Image Label Two', $additionalImageTwoItem->getLabel()); } + /** + * Test that after product import images from "hide_from_product_page" attribute hidden properly. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoAppIsolation enabled + */ + public function testSaveHiddenImages() + { + $this->importDataForMediaTest('import_media_hidden_images.csv'); + $product = $this->getProductBySku('simple_new'); + $images = $product->getMediaGalleryEntries(); + + $hiddenImages = array_filter( + $images, + static function (DataObject $image) { + return $image->getDisabled() == 1; + } + ); + + $this->assertCount(3, $hiddenImages); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_image.jpg', $imageItem->getFile()); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_thumbnail.jpg', $imageItem->getFile()); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_additional_image_two.jpg', $imageItem->getFile()); + } + /** * Test that new images should be added after the existing ones. * diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv new file mode 100644 index 0000000000000..1c1bebee57578 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two","magento_image.jpg,magento_thumbnail.jpg,magento_additional_image_two.jpg",,,,,,, From 230e8ae27057cbe8e4ca1f833218be45dff7a072 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Fri, 2 Aug 2019 08:39:35 +0300 Subject: [PATCH 0617/2437] MC-18824: Increase test coverage for Import / export functional area - Integration test for MC-11391 --- .../Model/Import/ProductTest.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 6587f005aed4f..8a40fc9a6c2c6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -2612,4 +2612,25 @@ private function importFile(string $fileName): void $this->_model->importData(); } + + /** + * Checking product images after Add/Update import failure + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * + * @return void + */ + public function testProductBaseImageAfterImport() + { + $this->importDataForMediaTest('import_media.csv'); + + $this->testImportWithNonExistingImage(); + + /** @var $productAfterImport \Magento\Catalog\Model\Product */ + $productAfterImport = $this->getProductBySku('simple_new'); + $this->assertNotEquals('/no/exists/image/magento_image.jpg', $productAfterImport->getData('image')); + } } From 91021d5fd3ea76769c52e8fd39bdfcabc1d5f18b Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 11 Sep 2019 17:58:40 +0400 Subject: [PATCH 0618/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-11332 --- ...atSomeAttributesChangedValueToEmptyAfterImportTest.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 20e4ed2257cd4..50573faf9860a 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -10,17 +10,17 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest"> <annotations> + <features value="Import/Export"/> <stories value="Attribute importing"/> <title value="Check that some attributes changed the value to an empty after import CSV"/> <description value="Check that some attributes changed the value to an empty after import CSV"/> - <features value="Import/Export"/> <severity value="CRITICAL"/> <testCaseId value="MC-11332"/> <useCaseId value="MAGETWO-61593"/> <group value="importExport"/> - <!--<skip>--> - <!--<issueId value="MC-17175" />--> - <!--</skip>--> + <skip> + <issueId value="MC-17175" /> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From f1ba0ce3a73f6de9c0c646c35679aae46574ecad Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Thu, 19 Sep 2019 18:18:02 +0300 Subject: [PATCH 0619/2437] MC-18824: Increase test coverage for Import / export functional area - Integration test for MC-6348 : CR comments fix. --- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 8a40fc9a6c2c6..f62c84eea4057 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -848,7 +848,7 @@ public function testSaveMediaImage() } /** - * Test that after product import images from "hide_from_product_page" attribute hidden properly. + * Tests that "hide_from_product_page" attribute is hidden after importing product images. * * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled From 78a7e6076ca03d532eed0ee8513bae834a7d7d8d Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 23 Sep 2019 14:29:23 +0400 Subject: [PATCH 0620/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- .../Catalog/Test/Mftf/Data/ProductData.xml | 3 ++ ...ontElasticsearchSearchInvalidValueTest.xml | 46 ++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 517ab253b8238..d8af28a23671f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -64,6 +64,9 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="ProductWithSpecialSymbols" extends="SimpleProduct" type="product"> + <data key="name">/s\i’m“p:l\$e#@!,.`=%&^</data> + </entity> <entity name="SimpleProductAfterImport1" type="product"> <data key="sku">SimpleProductForTest1</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml index 1e3badb5f1ce6..84bbe00d2b971 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml @@ -22,26 +22,20 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create category--> - <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Enable Elasticsearch--> - <comment userInput="Enable Elasticsearch" stepKey="commentEnableElasticsearch"/> <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="enableElasticsearch"/> <!--Set Minimal Query Length--> - <comment userInput="Set Minimal Query Length" stepKey="commentSetMinQueryLength"/> <magentoCLI command="config:set catalog/search/min_query_length 2" stepKey="setMinQueryLength"/> <!--Reindex indexes and clear cache--> - <comment userInput="Reindex indexes and clear cache" stepKey="commentReindexClearCache"/> <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> <magentoCLI command="cache:flush config" stepKey="flushCache"/> </before> <after> <!--Set configs to default--> - <comment userInput="Set configs to default" stepKey="commentSetDefault"/> <magentoCLI command="config:set catalog/search/min_query_length 3" stepKey="setMinQueryLengthPreviousState"/> <magentoCLI command="config:set catalog/search/engine mysql" stepKey="resetSearchEnginePreviousState"/> <!--Delete created data--> - <comment userInput="Delete created data" stepKey="commentDeleteData"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> <argument name="ProductAttributeCode" value="{{textProductAttribute.attribute_code}}"/> @@ -49,20 +43,18 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> <waitForPageLoad stepKey="waitForAttributePageLoad"/> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> - <argument name="sku" value="{{SimpleProduct.sku}}"/> - </actionGroup> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteProduct"/> <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Create new searchable product attribute--> - <comment userInput="Create new searchable product attribute" stepKey="commentCreateAttribute"/> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <actionGroup ref="AdminCreateSearchableProductAttribute" stepKey="createAttribute"> <argument name="attribute" value="textProductAttribute"/> </actionGroup> <!--Assign attribute to the Default set--> - <comment userInput="Assign attribute to the Default set" stepKey="commentAssignToDefaultSet"/> <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> @@ -71,14 +63,13 @@ </actionGroup> <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> <!--Create product and fill new attribute field--> - <comment userInput="Create product and fill new attribute field" stepKey="commentCreateProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> - <argument name="product" value="SimpleProduct"/> + <argument name="product" value="ProductWithSpecialSymbols"/> </actionGroup> <actionGroup ref="SetCategoryByName" stepKey="addCategoryToProduct"> <argument name="categoryName" value="$$createCategory.name$$"/> @@ -88,17 +79,28 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush catalog_product_attribute " stepKey="flushCache"/> <!--Assert search results on storefront--> - <comment userInput="Assert search results on storefront" stepKey="commentAssertSearchResult"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFirstSearchTerm"> - <argument name="phrase" value="searchable"/> + <argument name="phrase" value="?searchable;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductName"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForSecondSearchTerm"> + <argument name="phrase" value="? searchable ;"/> </actionGroup> - <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="seeProductName"/> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductNameSecondTime"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForSecondSearchTerm"> <argument name="phrase" value="?;"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbols"> + <argument name="phrase" value="?/s\i’m“p:l\$e#@!,.`=%&^;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbols"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbolsSecondTime"> + <argument name="phrase" value="? /s\i’m“p:l\$e#@!,.`=%&^ ;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbolsSecondTime"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForThirdSearchTerm"> <argument name="phrase" value="?anythingcangobetween;"/> </actionGroup> @@ -107,11 +109,13 @@ <argument name="phrase" value="? anything at all ;"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForForthSearchTerm"/> - <!--Search for the product wit special symbols--> - <comment userInput="Search for the product wit special symbols" stepKey="commentSearchForProduct"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFifthSearchTerm"> - <argument name="phrase" value="-+~/\\<>\’“:*$#@()!,.?`=%&^"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSearchableString"> + <argument name="phrase" value="?searchable string;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSearchableString"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSearchableStringSecondTime"> + <argument name="phrase" value="? searchable string ;"/> </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForFifthSearchTerm"/> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSearchableStringSecondTime"/> </test> </tests> From 85650c61257b52bd85e6a66aff3bc3c09294ca6a Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Mon, 23 Sep 2019 14:06:25 +0300 Subject: [PATCH 0621/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Model/Cart/SetBillingAddressOnCart.php | 4 ++ .../Model/Cart/SetShippingAddressesOnCart.php | 4 ++ .../Customer/SetBillingAddressOnCartTest.php | 49 ++++++++++++++++++ .../Customer/SetShippingAddressOnCartTest.php | 50 +++++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 22dff2d5550ba..e4457f2741101 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -116,6 +116,10 @@ private function createBillingAddress( if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); } + + if (0 === $customerId) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index ccb35e884061c..fbb6d0e5a6521 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -107,6 +107,10 @@ private function createShippingAddress( if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); } + + if (0 === $customerId) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 40ec237c8b233..00188014f26ea 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -651,6 +651,55 @@ public function testSetNewBillingAddressWithRedundantStreetLine() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetBillingAddressToGuestCartWithoutAddressId() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "{$maskedQuoteId}" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + } + } + ) { + cart { + shipping_addresses { + region { + code + } + country { + code + } + } + } + } +} +QUERY; + $this->expectExceptionMessage( + 'The current customer isn\'t authorized.' + ); + + $this->graphQlMutation($query, [], ''); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 95c8f4118adb9..57d428822799a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -619,6 +619,56 @@ public function testSetShippingAddressToGuestCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetShippingAddressToGuestCartWithoutAddressId() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "{$maskedQuoteId}" + shipping_addresses: [ + { + address: { + firstname: "John" + lastname: "Doe" + street: ["6161 West Centinella Avenue"] + city: "Culver City" + region: "CA" + postcode: "90230" + country_code: "us" + telephone: "555-555-55-55" + } + } + ] + } + ) { + cart { + shipping_addresses { + region { + code + } + country { + code + } + } + } + } +} +QUERY; + $this->expectExceptionMessage( + 'The current customer isn\'t authorized.' + ); + + $this->graphQlMutation($query, [], ''); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php From e720b6864fb8b91cf99403c9b6bffe13d0d627bc Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Mon, 23 Sep 2019 16:31:24 +0300 Subject: [PATCH 0622/2437] magento/magento2#24686: Static Content Deploy - Optimize files scanning performance - Fix static test failures - Ignore using array_merge in loop for now --- .../Magento/Framework/App/Utility/Files.php | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index c5f2053ec4076..10b14f7490050 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -24,44 +24,20 @@ */ class Files { - /** - * Include app code - */ const INCLUDE_APP_CODE = 1; - /** - * Include tests - */ const INCLUDE_TESTS = 2; - /** - * Include dev tools - */ const INCLUDE_DEV_TOOLS = 4; - /** - * Include templates - */ const INCLUDE_TEMPLATES = 8; - /** - * Include lib files - */ const INCLUDE_LIBS = 16; - /** - * Include pub code - */ const INCLUDE_PUB_CODE = 32; - /** - * Include non classes - */ const INCLUDE_NON_CLASSES = 64; - /** - * Include setup - */ const INCLUDE_SETUP = 128; /** @@ -397,6 +373,7 @@ public function getMainConfigFiles($asDataSet = true) $configXmlPaths = array_merge($globPaths, $configXmlPaths); $files = []; foreach ($configXmlPaths as $xmlPath) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($files, glob($xmlPath, GLOB_NOSORT)); } self::$_cache[$cacheKey] = $files; @@ -679,6 +656,7 @@ private function collectModuleLayoutFiles(array $params, $location) } } } else { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($files, $moduleFiles); } } @@ -713,8 +691,10 @@ private function collectThemeLayoutFiles(array $params, $location) ); if ($params['with_metainfo']) { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($this->parseThemeFiles($themeFiles, $currentThemePath, $theme)); } else { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $files = array_merge($files, $themeFiles); } } From 1cc75d452dd58f4d7a551058bb4bff2d01f18d66 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Mon, 23 Sep 2019 16:37:11 +0300 Subject: [PATCH 0623/2437] magento/magento2#24686: Static Content Deploy - Optimize files scanning performance Fix one using array_merge in loop issue --- lib/internal/Magento/Framework/App/Utility/Files.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index 10b14f7490050..65794e4f79da6 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -371,12 +371,11 @@ public function getMainConfigFiles($asDataSet = true) } $globPaths = [BP . '/app/etc/config.xml', BP . '/app/etc/*/config.xml']; $configXmlPaths = array_merge($globPaths, $configXmlPaths); - $files = []; + $files = [[]]; foreach ($configXmlPaths as $xmlPath) { - // phpcs:ignore Magento2.Performance.ForeachArrayMerge - $files = array_merge($files, glob($xmlPath, GLOB_NOSORT)); + $files[] = glob($xmlPath, GLOB_NOSORT); } - self::$_cache[$cacheKey] = $files; + self::$_cache[$cacheKey] = array_merge(...$files); } if ($asDataSet) { return self::composeDataSets(self::$_cache[$cacheKey]); From 3e8b90c054e98671f48b29960b28a48eceb355d7 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Mon, 23 Sep 2019 16:42:26 +0300 Subject: [PATCH 0624/2437] magento/magento2#24686: Static Content Deploy - Optimize files scanning performance Remove underscore from new method name --- lib/internal/Magento/Framework/App/Utility/Files.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index 65794e4f79da6..d329a7db029e4 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -909,7 +909,7 @@ public function getStaticPreProcessingFiles($filePattern = '*') $moduleLocalePath[] = $moduleDir . "/view/{$area}/web/i18n/{$locale}"; } - $this->_accumulateStaticFiles($area, $filePattern, $result); + $this->accumulateStaticFiles($area, $filePattern, $result); $this->_accumulateFilesByPatterns($moduleLocalePath, $filePattern, $result, '_parseModuleLocaleStatic'); $this->accumulateThemeStaticFiles($area, $locale, $filePattern, $result); self::$_cache[$key] = $result; @@ -1020,7 +1020,7 @@ protected function _accumulateFilesByPatterns(array $patterns, $filePattern, arr /** * Parse meta-info of a static file in module * - * @deprecated Replaced with method _accumulateStaticFiles() + * @deprecated Replaced with method accumulateStaticFiles() * * @param string $file * @return array @@ -1049,7 +1049,7 @@ protected function _parseModuleStatic($file) * @param array $result * @return void */ - private function _accumulateStaticFiles($area, $filePattern, array &$result) + private function accumulateStaticFiles($area, $filePattern, array &$result) { foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) { $moduleWebPath = $moduleDir . "/view/{$area}/web"; From 2d86f3971dab3ddce51a65060417a378f4904ef6 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 23 Sep 2019 11:45:19 -0500 Subject: [PATCH 0625/2437] MC-17518: Cart Rest API not showing all shipping assignments for multi-shipping --- .../Test/Unit/Controller/Checkout/PluginTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php b/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php index bbcd6d85c501c..5397317ca2997 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php @@ -59,7 +59,10 @@ protected function setUp() public function testExecuteTurnsOffMultishippingModeOnMultishippingQuote(): void { $subject = $this->createMock(Index::class); - $extensionAttributes = $this->createMock(CartExtensionInterface::class); + $extensionAttributes = $this->createPartialMock( + CartExtensionInterface::class, + ['setShippingAssignments', 'getShippingAssignments'] + ); $extensionAttributes->method('getShippingAssignments') ->willReturn( $this->createMock(ShippingAssignmentInterface::class) From 3150b0ef3326a3a4f8fe156ca998f698966787c3 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Mon, 23 Sep 2019 14:48:35 -0500 Subject: [PATCH 0626/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality --- .../Catalog/Test/Mftf/Data/ProductData.xml | 14 +++++++++++ ...ateProductAttributeFromProductPageTest.xml | 3 +++ ...ateProductAttributesStoreViewScopeTest.xml | 24 ++++++++++++------- ...sUpdateProductStatusStoreViewScopeTest.xml | 19 +++++++++++---- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 5 ++++ .../StorefrontProductNameWithDoubleQuote.xml | 4 ++-- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 4 ++-- 7 files changed, 55 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index e122615eb8aa4..8d7b9713f9b68 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -438,6 +438,20 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiProductNameWithNoSpaces" type="product"> + <data key="sku" unique="suffix">api-simple-product</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">ApiSimpleProduct</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-simple-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="_newDefaultProduct" type="product"> <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index 5c798db29b976..63a964f4b5e91 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -90,6 +90,9 @@ <waitForPageLoad stepKey="waitForProductToSave"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <!--Run Re-Index task --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Verify product attribute added in product form --> <scrollTo selector="{{AdminProductFormSection.contentTab}}" stepKey="scrollToContentTab"/> <waitForElementVisible selector="{{AdminProductFormSection.attributeTab}}" stepKey="waitForAttributeToVisible"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index bee13bec370da..f16296f99cb25 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -24,10 +24,12 @@ <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> + <createData entity="ApiProductNameWithNoSpaces" stepKey="createProductThree"/> </before> <after> <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <deleteData createDataKey="createProductThree" stepKey="deleteProductThree"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </after> @@ -54,22 +56,17 @@ <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> - <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> - <!-- Assert on storefront default view --> + <!-- Assert on storefront default view with partial word of product name --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> <argument name="name" value="$$createProductOne.name$$"/> <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> - <!-- Assert on storefront custom view --> + <!-- Assert on storefront custom view with partial word of product name --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> @@ -77,6 +74,15 @@ <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> - <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + <see userInput="2 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + + <!-- Assert Storefront default view with exact product name --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault1"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault1"> + <argument name="name" value="$$createProductThree.name$$"/> + <argument name="description" value="$$createProductThree.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault1"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault1"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml index e9b54e3f1a3dc..9eec6d0009488 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -117,16 +117,25 @@ <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> - <!-- Assert on storefront default view --> + <!-- Assert on storefront default view with first product --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> <argument name="description" value=""/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> - <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront default view with second product --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefaultToSearchSecondProduct"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefaultWithSecondProduct"> + <argument name="name" value="{{simpleProductForMassUpdate2.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefaultForSecondProduct"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefaultSecondProductResults"/> - <!-- Enable the product in Default store view --> + <!--Enable the product in Default store view--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> @@ -148,4 +157,4 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> </test> -</tests> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 7c0de6da18caf..495b8f2d40360 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -56,6 +56,9 @@ <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> </after> + <!--Re-index--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!-- Step 1: User browses catalog --> <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> @@ -191,6 +194,8 @@ <!-- Clear comparison sidebar --> <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 386633f0e9476..f2c3f15ab4343 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -111,11 +111,11 @@ <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="goToProduct2DisplayPage"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('2')}}" stepKey="goToProduct2DisplayPage"/> <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/>--> <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> - <!--Veriy the breadcrumbs on Product Display page--> + <!--Verify the breadcrumbs on Product Display page--> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$createCategoryOne.name$$" stepKey="seeCorrectBreadCrumbCategory2"/> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productTwo.name$$" stepKey="seeCorrectBreadCrumbProduct2"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 99f3fc00a7401..84c621d161f09 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -11,7 +11,7 @@ <test name="EndToEndB2CGuestUserTest"> <!-- Step 2: User searches for product --> <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> - <!-- Advanced Search with Product 1 Data --> + <!-- Advanced Search with Product Data --> <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> <!-- @TODO: Change to scalar value after MQE-498 is implemented --> @@ -22,7 +22,7 @@ <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> - <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> + <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> From b39441a14e171e28d01b4a584cd8a963c2c33ae9 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Tue, 24 Sep 2019 10:28:06 +0300 Subject: [PATCH 0627/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Model/Cart/SetBillingAddressOnCart.php | 4 -- .../Model/Cart/SetShippingAddressesOnCart.php | 4 -- .../Customer/SetBillingAddressOnCartTest.php | 49 ------------------ .../Customer/SetShippingAddressOnCartTest.php | 50 ------------------- 4 files changed, 107 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index e4457f2741101..22dff2d5550ba 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -116,10 +116,6 @@ private function createBillingAddress( if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { $this->saveQuoteAddressToCustomerAddressBook->execute($billingAddress, $customerId); } - - if (0 === $customerId) { - throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index fbb6d0e5a6521..ccb35e884061c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -107,10 +107,6 @@ private function createShippingAddress( if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); } - - if (0 === $customerId) { - throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); - } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 00188014f26ea..40ec237c8b233 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -651,55 +651,6 @@ public function testSetNewBillingAddressWithRedundantStreetLine() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - */ - public function testSetBillingAddressToGuestCartWithoutAddressId() - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - - $query = <<<QUERY -mutation { - setBillingAddressOnCart( - input: { - cart_id: "{$maskedQuoteId}" - billing_address: { - address: { - firstname: "test firstname" - lastname: "test lastname" - company: "test company" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - } - } - } - ) { - cart { - shipping_addresses { - region { - code - } - country { - code - } - } - } - } -} -QUERY; - $this->expectExceptionMessage( - 'The current customer isn\'t authorized.' - ); - - $this->graphQlMutation($query, [], ''); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 57d428822799a..95c8f4118adb9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -619,56 +619,6 @@ public function testSetShippingAddressToGuestCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - */ - public function testSetShippingAddressToGuestCartWithoutAddressId() - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - cart_id: "{$maskedQuoteId}" - shipping_addresses: [ - { - address: { - firstname: "John" - lastname: "Doe" - street: ["6161 West Centinella Avenue"] - city: "Culver City" - region: "CA" - postcode: "90230" - country_code: "us" - telephone: "555-555-55-55" - } - } - ] - } - ) { - cart { - shipping_addresses { - region { - code - } - country { - code - } - } - } - } -} -QUERY; - $this->expectExceptionMessage( - 'The current customer isn\'t authorized.' - ); - - $this->graphQlMutation($query, [], ''); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php From 5c51622c3aaced8373c8ef712d3f5e20c3e48686 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Tue, 24 Sep 2019 10:38:37 +0300 Subject: [PATCH 0628/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../_files/two_orders_with_order_items.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php index 8d2132dadd69b..ade37aed49d59 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/two_orders_with_order_items.php @@ -70,8 +70,8 @@ $orderRepository->save($order); /** @var Payment $payment */ -$payment2 = $objectManager->create(Payment::class); -$payment2->setMethod('checkmo') +$secondPayment = $objectManager->create(Payment::class); +$secondPayment->setMethod('checkmo') ->setAdditionalInformation('last_trans_id', '11122') ->setAdditionalInformation( 'metadata', @@ -82,8 +82,8 @@ ); /** @var OrderItem $orderItem */ -$orderItem2 = $objectManager->create(OrderItem::class); -$orderItem2->setProductId($product->getId()) +$secondOrderItem = $objectManager->create(OrderItem::class); +$secondOrderItem->setProductId($product->getId()) ->setQtyOrdered(2) ->setBasePrice($product->getPrice()) ->setPrice($product->getPrice()) @@ -93,10 +93,10 @@ ->setSku($product->getSku()); /** @var Order $order */ -$order2 = $objectManager->create(Order::class); -$order2->setIncrementId('100000002') +$secondOrder = $objectManager->create(Order::class); +$secondOrder->setIncrementId('100000002') ->setState(Order::STATE_PROCESSING) - ->setStatus($order2->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setStatus($secondOrder->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) ->setSubtotal(100) ->setGrandTotal(100) ->setBaseSubtotal(100) @@ -106,6 +106,6 @@ ->setBillingAddress($billingAddress) ->setShippingAddress($shippingAddress) ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) - ->addItem($orderItem2) - ->setPayment($payment2); -$orderRepository->save($order2); + ->addItem($secondOrderItem) + ->setPayment($secondPayment); +$orderRepository->save($secondOrder); From 029acac9341a3052196464da63740c458fa33cd0 Mon Sep 17 00:00:00 2001 From: Thomas Klein <thomas.klein@mpbio.com> Date: Mon, 2 Sep 2019 10:39:57 +0200 Subject: [PATCH 0629/2437] #12371 allows to add handlers via di --- app/code/Magento/Catalog/Helper/Output.php | 47 +++++++++++++--------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Output.php b/app/code/Magento/Catalog/Helper/Output.php index 33e261dc353b4..5c3907aa7918a 100644 --- a/app/code/Magento/Catalog/Helper/Output.php +++ b/app/code/Magento/Catalog/Helper/Output.php @@ -9,9 +9,18 @@ use Magento\Catalog\Model\Category as ModelCategory; use Magento\Catalog\Model\Product as ModelProduct; +use Magento\Eav\Model\Config; +use Magento\Framework\App\Helper\AbstractHelper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\Escaper; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filter\Template; +use function is_object; +use function method_exists; +use function preg_match; +use function strtolower; -class Output extends \Magento\Framework\App\Helper\AbstractHelper +class Output extends AbstractHelper { /** * Array of existing handlers @@ -37,12 +46,12 @@ class Output extends \Magento\Framework\App\Helper\AbstractHelper /** * Eav config * - * @var \Magento\Eav\Model\Config + * @var Config */ protected $_eavConfig; /** - * @var \Magento\Framework\Escaper + * @var Escaper */ protected $_escaper; @@ -53,23 +62,26 @@ class Output extends \Magento\Framework\App\Helper\AbstractHelper /** * Output constructor. - * @param \Magento\Framework\App\Helper\Context $context - * @param \Magento\Eav\Model\Config $eavConfig + * @param Context $context + * @param Config $eavConfig * @param Data $catalogData - * @param \Magento\Framework\Escaper $escaper + * @param Escaper $escaper * @param array $directivePatterns + * @param array $handlers */ public function __construct( - \Magento\Framework\App\Helper\Context $context, - \Magento\Eav\Model\Config $eavConfig, + Context $context, + Config $eavConfig, Data $catalogData, - \Magento\Framework\Escaper $escaper, - $directivePatterns = [] + Escaper $escaper, + $directivePatterns = [], + array $handlers = [] ) { $this->_eavConfig = $eavConfig; $this->_catalogData = $catalogData; $this->_escaper = $escaper; $this->directivePatterns = $directivePatterns; + $this->_handlers = $handlers; parent::__construct($context); } @@ -115,8 +127,7 @@ public function addHandler($method, $handler) */ public function getHandlers($method) { - $method = strtolower($method); - return $this->_handlers[$method] ?? []; + return $this->_handlers[strtolower($method)] ?? []; } /** @@ -145,21 +156,21 @@ public function process($method, $result, $params) * @param string $attributeName * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function productAttribute($product, $attributeHtml, $attributeName) { $attribute = $this->_eavConfig->getAttribute(ModelProduct::ENTITY, $attributeName); if ($attribute && $attribute->getId() && - $attribute->getFrontendInput() != 'media_image' && + $attribute->getFrontendInput() !== 'media_image' && (!$attribute->getIsHtmlAllowedOnFront() && !$attribute->getIsWysiwygEnabled()) ) { - if ($attribute->getFrontendInput() != 'price') { + if ($attribute->getFrontendInput() !== 'price') { $attributeHtml = $this->_escaper->escapeHtml($attributeHtml); } - if ($attribute->getFrontendInput() == 'textarea') { + if ($attribute->getFrontendInput() === 'textarea') { $attributeHtml = nl2br($attributeHtml); } } @@ -187,14 +198,14 @@ public function productAttribute($product, $attributeHtml, $attributeName) * @param string $attributeHtml * @param string $attributeName * @return string - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function categoryAttribute($category, $attributeHtml, $attributeName) { $attribute = $this->_eavConfig->getAttribute(ModelCategory::ENTITY, $attributeName); if ($attribute && - $attribute->getFrontendInput() != 'image' && + $attribute->getFrontendInput() !== 'image' && (!$attribute->getIsHtmlAllowedOnFront() && !$attribute->getIsWysiwygEnabled()) ) { From 629099d9d37bee4d2bb57ed8ede524f10fb918e1 Mon Sep 17 00:00:00 2001 From: Dzmitry Tabusheu <dzmitry_tabusheu@epam.com> Date: Tue, 24 Sep 2019 15:33:47 +0300 Subject: [PATCH 0630/2437] MAGETWO-72172: [2.3] Disabled variation of configurable product can be added to shopping cart via admin - Fixed functional test --- .../Test/NoOptionAvailableToConfigureDisabledProductTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index ed521cef2a411..fd607d2203c66 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -10,6 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="NoOptionAvailableToConfigureDisabledProductTest"> <annotations> + <features value="ConfigurableProduct"/> + <stories value="Admin order configurable product"/> <title value="Disabled variation of configurable product can't be added to shopping cart via admin"/> <description value="Disabled variation of configurable product can't be added to shopping cart via admin"/> <severity value="AVERAGE"/> From 3cf16dcb884a0051c8031ea872c32e0286f249f6 Mon Sep 17 00:00:00 2001 From: vital_pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 24 Sep 2019 14:20:43 +0300 Subject: [PATCH 0631/2437] MAGETWO-99838: Error message '"customer address attribute" is a required value.' is absent on Storefront - Fixed bug - Added unit test --- .../Eav/Model/Validator/Attribute/Data.php | 10 +-- .../Model/Validator/Attribute/DataTest.php | 81 ++++++++++++++++--- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Eav/Model/Validator/Attribute/Data.php b/app/code/Magento/Eav/Model/Validator/Attribute/Data.php index cd0d5141154c0..15dcea077c887 100644 --- a/app/code/Magento/Eav/Model/Validator/Attribute/Data.php +++ b/app/code/Magento/Eav/Model/Validator/Attribute/Data.php @@ -4,15 +4,15 @@ * See COPYING.txt for license details. */ +namespace Magento\Eav\Model\Validator\Attribute; + +use Magento\Eav\Model\Attribute; + /** * EAV attribute data validator * * @author Magento Core Team <core@magentocommerce.com> */ -namespace Magento\Eav\Model\Validator\Attribute; - -use Magento\Eav\Model\Attribute; - class Data extends \Magento\Framework\Validator\AbstractValidator { /** @@ -126,7 +126,7 @@ public function isValid($entity) $dataModel = $this->_attrDataFactory->create($attribute, $entity); $dataModel->setExtractedData($data); if (!isset($data[$attributeCode])) { - $data[$attributeCode] = null; + $data[$attributeCode] = ''; } $result = $dataModel->validateValue($data[$attributeCode]); if (true !== $result) { diff --git a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php index 07ce6fbfc6a4c..9f1e74fccbd4f 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php @@ -4,13 +4,43 @@ * See COPYING.txt for license details. */ +namespace Magento\Eav\Test\Unit\Model\Validator\Attribute; + /** * Test for \Magento\Eav\Model\Validator\Attribute\Data */ -namespace Magento\Eav\Test\Unit\Model\Validator\Attribute; - class DataTest extends \PHPUnit\Framework\TestCase { + /** + * @var \Magento\Eav\Model\AttributeDataFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $attrDataFactory; + + /** + * @var \Magento\Eav\Model\Validator\Attribute\Data + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->attrDataFactory = $this->getMockBuilder(\Magento\Eav\Model\AttributeDataFactory::class) + ->setMethods(['create']) + ->setConstructorArgs( + [ + 'objectManager' => $this->createMock(\Magento\Framework\ObjectManagerInterface::class), + 'string' => $this->createMock(\Magento\Framework\Stdlib\StringUtils::class) + ] + ) + ->getMock(); + + $this->model = new \Magento\Eav\Model\Validator\Attribute\Data( + $this->attrDataFactory + ); + } + /** * Testing \Magento\Eav\Model\Validator\Attribute\Data::isValid * @@ -381,13 +411,15 @@ public function testAddErrorMessages() protected function _getAttributeMock($attributeData) { $attribute = $this->getMockBuilder(\Magento\Eav\Model\Attribute::class) - ->setMethods([ - 'getAttributeCode', - 'getDataModel', - 'getFrontendInput', - '__wakeup', - 'getIsVisible', - ]) + ->setMethods( + [ + 'getAttributeCode', + 'getDataModel', + 'getFrontendInput', + '__wakeup', + 'getIsVisible', + ] + ) ->disableOriginalConstructor() ->getMock(); @@ -466,4 +498,35 @@ protected function _getEntityMock() )->disableOriginalConstructor()->getMock(); return $entity; } + + /** + * Test for isValid() without data for attribute. + * + * @return void + */ + public function testIsValidWithoutData() : void + { + $attributeData = ['attribute_code' => 'attribute', 'frontend_input' => 'text', 'is_visible' => true]; + $entity = $this->_getEntityMock(); + $attribute = $this->_getAttributeMock($attributeData); + $this->model->setAttributes([$attribute])->setData([]); + $dataModel = $this->getMockBuilder(\Magento\Eav\Model\Attribute\Data\AbstractData::class) + ->disableOriginalConstructor() + ->setMethods(['validateValue']) + ->getMockForAbstractClass(); + $dataModel->expects($this->once()) + ->method('validateValue') + // only empty string + ->with( + $this->logicalAnd( + $this->isEmpty(), + $this->isType('string') + ) + )->willReturn(true); + $this->attrDataFactory->expects($this->once()) + ->method('create') + ->with($attribute, $entity) + ->willReturn($dataModel); + $this->assertEquals(true, $this->model->isValid($entity)); + } } From 491b65a5e84ee3e00be1aeb336dc49773a1de038 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Tue, 24 Sep 2019 09:41:39 -0500 Subject: [PATCH 0632/2437] MC-20358: Create New MFTF Suites for MySQL/Elasticsearch Specific Functionality --- .../Suite/SearchEngineElasticsearchSuite.xml | 17 ++++++++++++++ .../Search/Test/Mftf/Data/ConfigData.xml | 22 ++++++++++++++++++ .../Mftf/Suite/SearchEngineMysqlSuite.xml | 23 +++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml new file mode 100644 index 0000000000000..ff2b86d3abd55 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> + <suite name="SearchEngineElasticsearchSuite"> + <include> + <group name="SearchEngineElasticsearch" /> + </include> + <exclude> + <group name="skip"/> + </exclude> + </suite> +</suites> diff --git a/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..1761e9a735761 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SearchEngineDefaultConfigData"> + <data key="path">catalog/search/engine</data> + <data key="scope_id">1</data> + <data key="label">Elasticsearch 6.0+</data> + <data key="value">elasticsearch6</data> + </entity> + <entity name="SearchEngineMysqlConfigData"> + <data key="path">catalog/search/engine</data> + <data key="scope_id">1</data> + <data key="label">MySQL</data> + <data key="value">mysql</data> + </entity> +</entities> diff --git a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml new file mode 100644 index 0000000000000..a389efbbe443e --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> + <suite name="SearchEngineMysqlSuite"> + <before> + <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> + </before> + <after> + <magentoCLI stepKey="restoreSearchEngineToDefault" command="config:set {{SearchEngineDefaultConfigData.path}} {{SearchEngineDefaultConfigData.value}}"/> + </after> + <include> + <group name="SearchEngineMysql" /> + </include> + <exclude> + <group name="skip"/> + </exclude> + </suite> +</suites> From 5fd3c14b5bac44a5dce5aaef26203a526be6f32f Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Tue, 24 Sep 2019 17:44:11 +0300 Subject: [PATCH 0633/2437] MC-20071: Fix Skipped MFTF Tests From MC-17140: MAGETWO-98211, MC-56, MC-88 --- ...CustomizableOptionToProductWithSKUTest.xml | 21 +++++++------------ ...UpdateProductAttributesGlobalScopeTest.xml | 13 +++++------- .../AdminConfigurableProductUpdateTest.xml | 15 +++++++++---- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index e29a23fe4f18f..c191822b2c7e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -15,36 +15,32 @@ <title value="Import customizable options to a product with existing SKU"/> <description value="Import customizable options to a product with existing SKU"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-98211"/> + <testCaseId value="MC-16471"/> <useCaseId value="MAGETWO-70232"/> <group value="catalog"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create category--> - <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Create two product --> - <comment userInput="Create two product" stepKey="commentCreateTwoProduct"/> + <!-- Create two products --> <createData entity="SimpleProduct2" stepKey="createFirstProduct"/> <createData entity="ApiSimpleProduct" stepKey="createSecondProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <!--Delete second product with changed sku--> - <comment userInput="Delete second product with changed sku" stepKey="commentDeleteProduct"/> <actionGroup ref="deleteProductBySku" stepKey="deleteSecondProduct"> <argument name="sku" value="$$createFirstProduct.sku$$-1"/> </actionGroup> - <!--Delete created data--> - <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductGridFilter"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> <!--Go to product page --> - <comment userInput="Go to product page" stepKey="commentGoToProductPage"/> <amOnPage url="{{AdminProductEditPage.url($$createFirstProduct.id$$)}}" stepKey="goToProductEditPage"/> <waitForPageLoad stepKey="waitForProductEditPageLoad"/> <actionGroup ref="AddProductCustomOptionField" stepKey="addCutomOption1"> @@ -55,12 +51,10 @@ </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!--Change second product sku to first product sku--> - <comment userInput="Change second product sku to first product sku" stepKey="commentChangeSecondProduct"/> <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToProductEditPage1"/> <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku1"/> <!--Import customizable options and check--> - <comment userInput="Import customizable options and check" stepKey="commentImportOptions"/> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> <actionGroup ref="importProductCustomizableOptions" stepKey="importOptions"> <argument name="productName" value="$$createFirstProduct.name$$"/> @@ -74,7 +68,6 @@ <argument name="optionIndex" value="1"/> </actionGroup> <!--Save product and check sku changed message--> - <comment userInput="Save product and check sku changed message" stepKey="commentSAveProductAndCheck"/> <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> <see selector="{{AdminMessagesSection.notice}}" userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 5c1a97721201d..925cc4d1590c5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -29,15 +29,13 @@ <createData entity="ApiSimpleProduct" stepKey="createProductTwo"> <requiredEntity createDataKey="createCategory"/> </createData> - <magentoCLI stepKey="setIndexerMode" command="indexer:set-mode" arguments="schedule" /> </before> <after> - <magentoCLI stepKey="setIndexersMode" command="indexer:set-mode" arguments="realtime" /> - <magentoCLI stepKey="indexerReindex" command="indexer:reindex" /> <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -61,14 +59,13 @@ <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> <fillField selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="$$createProductOne.price$$0" stepKey="fillAttributeNameField"/> <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="cronSchedule"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="cronRun"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 88bd48909e3d1..4cd962e76bc96 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -34,15 +34,19 @@ </before> <after> - <actionGroup ref="logout" stepKey="logout"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Search for prefix of the 3 products we created via api --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> @@ -58,9 +62,9 @@ <!-- Update the description --> <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> - <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCronFirstTime"/> @@ -70,10 +74,13 @@ <!-- Check storefront for description --> <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> <amOnPage url="{{StorefrontProductPage.url($$createSecondProduct.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> <amOnPage url="{{StorefrontProductPage.url($$createThirdProduct.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> </test> From fbf61075f612a5be444acd3f8d06ebba71a9532f Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Tue, 24 Sep 2019 10:26:31 -0500 Subject: [PATCH 0634/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality --- .../AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml | 4 ++++ ...ctWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml | 4 ++++ ...ProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml | 3 +++ 3 files changed, 11 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml index e1cb45be22b4e..a863de2716c97 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml @@ -63,6 +63,10 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + + <!--Run re-index task --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Verify product is visible in category front page --> <amOnPage url="$$createCategory.name$$.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 637ae790c16c8..7974618cde1a1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -66,6 +66,7 @@ <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> @@ -94,6 +95,9 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice245InStock.urlKey}}" stepKey="seeUrlKey"/> + <!--Run re-index task --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Verify customer see updated simple product link on category page --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 045b3f3420ff6..4817b3497c97e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -94,6 +94,9 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32501InStock.urlKey}}" stepKey="seeUrlKey"/> + <!--Run re-index task --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Verify customer see updated simple product link on category page --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> From 4e3202f6aeed48574c070a47695f514450580d0d Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Tue, 24 Sep 2019 13:03:38 -0500 Subject: [PATCH 0635/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality --- .../Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml index d17078d794b42..b613068893b0e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml @@ -59,6 +59,9 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> + <!--Run re-index task --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Verify category displayed in store front page--> <amOnPage url="/$$createDefaultCategory.name$$/{{SimpleSubCategory.name}}.html" stepKey="seeTheCategoryInStoreFrontPage"/> <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> From 58c0999ce5f2bfe30535ce8e546051e7ed9a0821 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Tue, 24 Sep 2019 14:30:27 -0500 Subject: [PATCH 0636/2437] MC-20358: Create New MFTF Suites for MySQL/Elasticsearch Specific Functionality --- .../Elasticsearch6/Test/Mftf/Data/ConfigData.xml | 16 ++++++++++++++++ .../Magento/Search/Test/Mftf/Data/ConfigData.xml | 6 ------ .../Test/Mftf/Suite/SearchEngineMysqlSuite.xml | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..f1f2f39f4457b --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SearchEngineElasticsearchConfigData"> + <data key="path">catalog/search/engine</data> + <data key="scope_id">1</data> + <data key="label">Elasticsearch 6.0+</data> + <data key="value">elasticsearch6</data> + </entity> +</entities> diff --git a/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml index 1761e9a735761..4a742b290c983 100644 --- a/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml +++ b/app/code/Magento/Search/Test/Mftf/Data/ConfigData.xml @@ -7,12 +7,6 @@ --> <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="SearchEngineDefaultConfigData"> - <data key="path">catalog/search/engine</data> - <data key="scope_id">1</data> - <data key="label">Elasticsearch 6.0+</data> - <data key="value">elasticsearch6</data> - </entity> <entity name="SearchEngineMysqlConfigData"> <data key="path">catalog/search/engine</data> <data key="scope_id">1</data> diff --git a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml index a389efbbe443e..7fb3c5474d899 100644 --- a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml +++ b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml @@ -11,7 +11,7 @@ <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> </before> <after> - <magentoCLI stepKey="restoreSearchEngineToDefault" command="config:set {{SearchEngineDefaultConfigData.path}} {{SearchEngineDefaultConfigData.value}}"/> + <magentoCLI stepKey="setSearchEngineToElasticsearch" command="config:set {{SearchEngineElasticsearchConfigData.path}} {{SearchEngineElasticsearchConfigData.value}}"/> </after> <include> <group name="SearchEngineMysql" /> From 1e821dd8aa6f11c9fbef3c2f6b118c03c3b17a92 Mon Sep 17 00:00:00 2001 From: Sergey Dovbenko <sdovbenko@magecom.us> Date: Tue, 24 Sep 2019 19:59:12 +0000 Subject: [PATCH 0637/2437] Corrected Code Styles --- .../Quote/AddSimpleProductWithCustomOptionsToCartTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php index 272c0df29e04b..95f2e05e63958 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductWithCustomOptionsToCartTest.php @@ -127,7 +127,8 @@ public function testAddSimpleProductWithDateOption() self::assertArrayHasKey('items', $response['addSimpleProductsToCart']['cart']); self::assertCount(1, $response['addSimpleProductsToCart']['cart']); - $customizableOptionOutput = $response['addSimpleProductsToCart']['cart']['items'][0]['customizable_options'][0]['values'][0]['value']; + $cartItem = $response['addSimpleProductsToCart']['cart']['items'][0]; + $customizableOptionOutput = $cartItem['customizable_options'][0]['values'][0]['value']; $expectedValue = date("M d, Y", strtotime($customOptionsValues[0]['value_string'])); self::assertEquals($expectedValue, $customizableOptionOutput); From 5a60b4f4494e9d813d73724190164d5b099d3734 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Tue, 24 Sep 2019 16:33:04 -0500 Subject: [PATCH 0638/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Magento/CatalogRule/Model/Indexer/IndexBuilder.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 6391171be0a35..a434d337f00c2 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -279,11 +279,7 @@ protected function doReindexByIds($ids) $activeRules = $this->getActiveRules()->getItems(); foreach ($activeRules as $rule) { $rule->setProductsFilter($ids); - $productIds = $rule->getMatchingProductIds(); - foreach ($productIds as $productId => $result) { - $websiteIds = array_keys(array_filter($result)); - $this->assignProductToRule($rule, $productId, $websiteIds); - } + $this->reindexRuleProduct->execute($rule, $this->batchCount); } $this->cleanProductPriceIndex($ids); @@ -435,6 +431,8 @@ private function assignProductToRule(Rule $rule, int $productId, array $websiteI * @param Product $product * @return $this * @throws \Exception + * @deprecated + * @see ReindexRuleProduct::execute * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function applyRule(Rule $rule, $product) From 7ec3c72ee14b40e1326e44fab82cbe179c0fe84c Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 24 Sep 2019 14:15:16 -0500 Subject: [PATCH 0639/2437] MC-20181: Elasticsearch failing with fielddata error --- .../LayeredNavigation/Test/TestCase/FilterProductListTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml index 9c9c917f8a66d..72b89916a08df 100644 --- a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml +++ b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml @@ -95,6 +95,7 @@ </variation> <variation name="FilterProductListTestVariation4" summary="Use sorting category filter when layered navigation is applied" ticketId="MAGETWO-42701"> <data name="configData" xsi:type="string">layered_navigation_manual_range_10</data> + <data name="runReindex" xsi:type="boolean">true</data> <data name="category/dataset" xsi:type="string">default_anchor_subcategory</data> <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::product_10_dollar, catalogProductSimple::product_20_dollar, configurableProduct::filterable_two_options_with_zero_price</data> <data name="layeredNavigation" xsi:type="array"> From ede0d63dd0237e5c3d7b041c2a85930b9e15cd92 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Tue, 24 Sep 2019 17:00:32 -0500 Subject: [PATCH 0640/2437] MC-20358: Create New MFTF Suites for MySQL/Elasticsearch Specific Functionality --- .../Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml | 10 ++++++++++ .../Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml | 6 ------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml index ff2b86d3abd55..81debdb0d448e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml @@ -7,6 +7,16 @@ --> <suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="SearchEngineElasticsearchSuite"> + <before> + <magentoCLI stepKey="setSearchEngineToElasticsearch" command="config:set {{SearchEngineElasticsearchConfigData.path}} {{SearchEngineElasticsearchConfigData.value}}"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> <include> <group name="SearchEngineElasticsearch" /> </include> diff --git a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml index 7fb3c5474d899..eaa90f7590bfe 100644 --- a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml +++ b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml @@ -7,12 +7,6 @@ --> <suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="SearchEngineMysqlSuite"> - <before> - <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> - </before> - <after> - <magentoCLI stepKey="setSearchEngineToElasticsearch" command="config:set {{SearchEngineElasticsearchConfigData.path}} {{SearchEngineElasticsearchConfigData.value}}"/> - </after> <include> <group name="SearchEngineMysql" /> </include> From 7774abcb8ffb50937ad568893c073d997164b62d Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 24 Sep 2019 14:08:29 -0500 Subject: [PATCH 0641/2437] MC-20346: Update Regions list for Belgium country --- .../Customer/Test/Mftf/Data/AddressData.xml | 15 +++ ...efrontUpdateCustomerAddressBelgiumTest.xml | 51 ++++++++++ .../Setup/Patch/Data/AddDataForBelgium.php | 94 +++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForBelgium.php diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4d7a39b3246e1..f1d1a1d683db2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -312,4 +312,19 @@ <data key="postcode">90230</data> <data key="telephone">555-55-555-55</data> </entity> + <entity name="updateCustomerBelgiumAddress" type="address"> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>Chaussee de Wavre</item> + <item>318</item> + </array> + <data key="city">Bihain</data> + <data key="state">Hainaut</data> + <data key="country_id">BE</data> + <data key="country">Belgium</data> + <data key="postcode">6690</data> + <data key="telephone">0477-58-77867</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml new file mode 100644 index 0000000000000..6c0615f701df6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontUpdateCustomerAddressBelgiumTest"> + <annotations> + <stories value="Update Regions list for Belgium country"/> + <title value="Update customer address on storefront with Belgium address"/> + <description value="Update customer address on storefront with Belgium address and verify you can select a region"/> + <testCaseId value="MC-20234"/> + <severity value="AVERAGE"/> + <group value="customer"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + </before> + <after> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Update customer address Belgium in storefront--> + <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddress"> + <argument name="Address" value="updateCustomerBelgiumAddress"/> + </actionGroup> + <!--Verify customer address save success message--> + <see selector="{{AdminCustomerMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeAssertCustomerAddressSuccessSaveMessage"/> + + <!--Verify customer default billing address--> + <actionGroup ref="VerifyCustomerBillingAddressWithState" stepKey="verifyBillingAddress"> + <argument name="address" value="updateCustomerBelgiumAddress"/> + </actionGroup> + + <!--Verify customer default shipping address--> + <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <argument name="address" value="updateCustomerBelgiumAddress"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForBelgium.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForBelgium.php new file mode 100644 index 0000000000000..7e53198cb9a4e --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForBelgium.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add Regions for Belgium. + */ +class AddDataForBelgium implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var \Magento\Directory\Setup\DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForBelgium() + ); + } + + /** + * Belgium states data. + * + * @return array + */ + private function getDataForBelgium() + { + return [ + ['BE', 'VAN', 'Antwerpen'], + ['BE', 'WBR', 'Brabant wallon'], + ['BE', 'BRU', 'Brussels Hoofdstedelijk Gewest'], + ['BE', 'WHT', 'Hainaut'], + ['BE', 'VLI', 'Limburg'], + ['BE', 'WLG', 'Liege'], + ['BE', 'WLX', 'Luxembourg'], + ['BE', 'WNA', 'Namur'], + ['BE', 'VOV', 'Oost-Vlaanderen'], + ['BE', 'VLG', 'Vlaams Gewest'], + ['BE', 'VBR', 'Vlaams-Brabant'], + ['BE', 'VWV', 'West-Vlaanderen'], + ['BE', 'WAL', 'Wallonne, Region'] + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} From 3cec03358c610f9fc7f94d9b2ccaf43ca47eb879 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Tue, 24 Sep 2019 22:56:26 -0500 Subject: [PATCH 0642/2437] MQE-1714: Community MTF to MFTF test conversion code review - minor fixes and added testCaseId --- .../Test/AdminUserLockWhenEditingUserTest.xml | 9 +-- .../AdminFillUserRoleFormActionGroup.xml | 4 +- .../AssertAdminUserIsInGridActionGroup.xml | 22 ++++++ ...ertUserRoleRestrictedAccessActionGroup.xml | 14 ++++ .../Magento/User/Test/Mftf/Data/UserData.xml | 6 ++ .../User/Test/Mftf/Data/UserRoleData.xml | 22 +++--- .../Test/Mftf/Metadata/user_role-meta.xml | 7 +- .../Mftf/Test/AdminUpdateUserRoleTest.xml | 78 ------------------ .../Test/Mftf/Test/AdminUpdateUserTest.xml | 79 +++++++++++++++++++ 9 files changed, 141 insertions(+), 100 deletions(-) create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml delete mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml create mode 100644 app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml index 2ce54d6c0fda5..9f421668bdc4f 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml @@ -20,14 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <!-- - @TODO: Remove "executeJS" in scope of MQE-1561 - Hack to be able to pass current admin user password without hardcoding it. - --> - <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> - <createData entity="NewAdminUser" stepKey="user"> - <field key="current_password">{$adminPassword}</field> - </createData> + <createData entity="NewAdminUser" stepKey="user" /> <!-- Log in to Admin Panel --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml index 480695aa7a931..7b913382651ae 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml @@ -17,13 +17,13 @@ <argument name="currentAdminPassword" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" /> </arguments> - <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="fillRoleName"/> + <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.rolename}}" stepKey="fillRoleName"/> <fillField selector="{{AdminCreateRoleSection.password}}" userInput="{{currentAdminPassword}}" stepKey="fillCurrentUserPassword"/> <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> <waitForPageLoad stepKey="waitForRoleResourceTab" /> <selectOption userInput="{{role.resourceAccess}}" selector="{{AdminCreateRoleSection.resourceAccess}}" stepKey="selectResourceAccess" /> - <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resources}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resource}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml new file mode 100644 index 0000000000000..3499f4e0d951c --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminUserIsInGridActionGroup"> + <arguments> + <argument name="user" type="entity"/> + </arguments> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset" time="15"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml new file mode 100644 index 0000000000000..0747eab31588e --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertUserRoleRestrictedAccessActionGroup"> + <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index d465851c62373..6366cbe594309 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -33,6 +33,12 @@ <item>1</item> </array> </entity> + <entity name="AdminUserWithUpdatedUserRoleToSales" extends="NewAdminUser"> + <data key="password">123123qA</data> + <data key="password_confirmation">123123qA</data> + <data key="role">{{roleSales.rolename}}</data> + </entity> + <entity name="EditAdminUser" type="user"> <data key="username" unique="suffix">admin</data> <data key="firstname">John</data> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index a0d89bbf3fb9d..ba53b853efe0f 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -14,26 +14,30 @@ <data key="access">1</data> </entity> - <entity name="roleAdministrator" type="role"> - <data key="name" unique="suffix">Administrator </data> + <entity name="roleAdministrator" type="user_role"> + <data key="rolename" unique="suffix">Administrator </data> <data key="resourceAccess">All</data> - <data key="resources">[]</data> + <data key="all">1</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="resource">[]</data> </entity> - <entity name="roleSales" type="role"> - <data key="name" unique="suffix">Role Sales </data> + <entity name="roleSales"> + <data key="rolename" unique="suffix">Role Sales </data> <data key="resourceAccess">Custom</data> - <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> + <data key="all">0</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="resource">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> <entity name="limitedRole" type="role"> - <data key="name" unique="suffix">Limited</data> + <data key="rolename" unique="suffix">Limited</data> <data key="roleScopes">Custom</data> <data key="resourceAccess">All</data> </entity> <entity name="restrictedRole" type="role"> - <data key="name" unique="suffix">Restricted</data> + <data key="rolename" unique="suffix">Restricted</data> <data key="roleScopes">Custom</data> <data key="resourceAccess">All</data> </entity> @@ -41,7 +45,7 @@ <!-- This admin created for checking turn off "Bulk Actions" --> <entity name="adminWithoutBulkActionRole" type="user_role"> <data key="rolename">restrictedWebsiteRole</data> - <data key="current_password">123123q</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> <data key="gws_is_all">0</data> <array key="gws_websites"> <item>1</item> diff --git a/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml b/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml index 9d0132453c798..5384bd520b2c7 100644 --- a/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml +++ b/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml @@ -10,9 +10,10 @@ <operation name="CreateUserRole" dataType="user_role" type="create" auth="adminFormKey" url="/admin/user_role/saverole/" method="POST" successRegex="/messages-message-success/" returnRegex="" > <contentType>application/x-www-form-urlencoded</contentType> - <field key="rolename">string</field> - <field key="current_password">string</field> - <array key="resource"> + <field key="rolename" required="true">string</field> + <field key="current_password" required="true">string</field> + <field key="all" required="true">integer</field> + <array key="resource" required="false"> <value>string</value> </array> </operation> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml deleted file mode 100644 index 570bc572df7de..0000000000000 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserRoleTest.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminUpdateUserRoleTest"> - <annotations> - <features value="User"/> - <title value="Admin user role update"/> - <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> - <group value="user"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logOut"/> - </after> - - <!--Create New User--> - <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> - <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> - - <!--Create New Role--> - <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> - <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> - <argument name="role" value="salesRole"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> - - <!--Assign new role--> - <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - - <actionGroup ref="AdminUpdateUserRoleActionGroup" stepKey="assignNewUserRole"> - <argument name="role" value="salesRole"/> - </actionGroup> - <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> - <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> - <argument name="message" value="You saved the user."/> - </actionGroup> - - <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> - <argument name="adminUser" value="NewAdminUser"/> - </actionGroup> - <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> - <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> - <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> - - <!--Delete new User--> - <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> - <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> - <argument name="user" value="NewAdminUser"/> - </actionGroup> - - <!--Delete new Role--> - <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> - <argument name="roleName" value="{{salesRole.name}}"/> - </actionGroup> - - </test> - </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml new file mode 100644 index 0000000000000..dfadee8ee6807 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateUserTest"> + <annotations> + <features value="User"/> + <title value="Update admin user entity by changing user role"/> + <stories value="Update User" /> + <testCaseId value="MC-14264" /> + <severity value="MAJOR" /> + <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <!--Create New Role--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="roleSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> + </before> + <after> + <!--Delete new User--> + <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + + <!--Delete new Role--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> + <argument name="roleName" value="{{roleSales.rolename}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + + <!--Assign new role--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillUserForm"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You saved the user."/> + </actionGroup> + + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> + <argument name="adminUser" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> + <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> + </test> +</tests> From 7d03bdc3c8735446cb4f74d4219a9a54c11230b4 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Wed, 25 Sep 2019 12:37:07 +0300 Subject: [PATCH 0643/2437] [Wishlist] Remove name from WishlistOutput #920 --- .../Resolver/CustomerWishlistsResolver.php | 67 +++++++++++ .../WishlistGraphQl/etc/schema.graphqls | 2 +- .../Wishlist/CustomerWishlistsTest.php | 110 ++++++++++++++++++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistsResolver.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistsResolver.php new file mode 100644 index 0000000000000..a2b8280fc3d0d --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistsResolver.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Wishlist\Model\ResourceModel\Wishlist\CollectionFactory; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; + +/** + * Fetches the Wishlists data according to the GraphQL schema + */ +class CustomerWishlistsResolver implements ResolverInterface +{ + /** + * @var CollectionFactory + */ + private $_wishlistCollectionFactory; + + /** + * @param CollectionFactory $wishlistCollectionFactory + */ + public function __construct(CollectionFactory $wishlistCollectionFactory) + { + $this->_wishlistCollectionFactory = $wishlistCollectionFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $customerId = $context->getUserId(); + + /* Guest checking */ + if (!$customerId && 0 === $customerId) { + throw new GraphQlAuthorizationException(__('The current user cannot perform operations on wishlist')); + } + $collection = $this->_wishlistCollectionFactory->create()->filterByCustomerId($customerId); + $wishlists = $collection->getItems(); + $wishlistsData = []; + if (0 === count($wishlists)) { + return $wishlistsData; + } + + foreach ($wishlists as $wishlist) { + $wishlistsData [] = [ + 'sharing_code' => $wishlist->getSharingCode(), + 'updated_at' => $wishlist->getUpdatedAt(), + 'items_count' => $wishlist->getItemsCount(), + 'model' => $wishlist, + ]; + } + return $wishlistsData; + } +} diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 7daf15596f19d..b7cc60fe100c6 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -6,7 +6,7 @@ type Query { } type Customer { - wishlists: Wishlist! @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @cache(cacheable: false) + wishlists: [Wishlist]! @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\CustomerWishlistsResolver") @doc(description: "The wishlist query returns the contents of a customer's wish lists") @cache(cacheable: false) } type WishlistOutput @doc(description: "Deprecated: 'Wishlist' type should be used instead") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php new file mode 100644 index 0000000000000..cdc774f08c2cb --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Wishlist; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\ResourceModel\Wishlist\CollectionFactory; + +class CustomerWishlistsTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var CollectionFactory + */ + private $_wishlistCollectionFactory; + + protected function setUp() + { + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->_wishlistCollectionFactory = Bootstrap::getObjectManager()->get(CollectionFactory::class); + } + + /** + * @magentoApiDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testGetCustomerWishlists(): void + { + /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ + $collection = $this->_wishlistCollectionFactory->create()->filterByCustomerId(1); + + /** @var Item $wishlistItem */ + $wishlistItem = $collection->getFirstItem(); + $query = + <<<QUERY +{ + customer + { + wishlists { + items_count + sharing_code + updated_at + items { + product { + sku + } + } + } + } +} +QUERY; + + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', 'password') + ); + + $this->assertEquals($wishlistItem->getItemsCount(), $response['wishlists'][0]['items_count']); + $this->assertEquals($wishlistItem->getSharingCode(), $response['wishlists'][0]['sharing_code']); + $this->assertEquals($wishlistItem->getUpdatedAt(), $response['wishlists'][0]['updated_at']); + $this->assertEquals('simple', $response['wishlists'][0]['items'][0]['product']['sku']); + + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testGetGuestWishlist() + { + $query = + <<<QUERY +{ + customer + { + wishlists { + items_count + sharing_code + updated_at + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} From f38582816355f797da3f870fe8ca796dbd8c6a44 Mon Sep 17 00:00:00 2001 From: Nikita Chubukov <nikita_chubukov@epam.com> Date: Wed, 25 Sep 2019 12:00:28 +0300 Subject: [PATCH 0644/2437] MAGETWO-62508: Shipment Tracking REST API should throw an error if order doesn't exist - Fix CR comments --- .../Model/Order/Shipment/TrackRepository.php | 1 + .../Sales/Service/V1/ShipmentAddTrackTest.php | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index 3cf173117b4b6..24ccf45d60145 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -121,6 +121,7 @@ public function delete(ShipmentTrackInterface $entity) public function save(ShipmentTrackInterface $entity) { $shipments = $this->shipmentCollection->create() + ->addFieldToFilter('order_id', $entity['order_id']) ->addFieldToFilter('entity_id', $entity['parent_id']) ->toArray(); diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php index 5b7b1bf606932..93d835d77a1e5 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php @@ -104,20 +104,22 @@ public function testShipmentTrackWithFailedOrderId() ShipmentTrackInterface::TITLE => 'Shipment title', ShipmentTrackInterface::CARRIER_CODE => Track::CUSTOM_CARRIER_CODE, ]; - $expectedMessage = 'Could not save the shipment tracking.'; + $exceptionMessage = ''; try { $this->_webApiCall($this->getServiceInfo(), ['entity' => $trackData]); } catch (\SoapFault $e) { - $this->assertContains( - $expectedMessage, - $e->getMessage(), - 'SoapFault does not contain expected message.' - ); + $exceptionMessage = $e->getMessage(); } catch (\Exception $e) { $errorObj = $this->processRestExceptionResult($e); - $this->assertEquals($expectedMessage, $errorObj['message']); + $exceptionMessage = $errorObj['message']; } + + $this->assertContains( + $exceptionMessage, + 'Could not save the shipment tracking.', + 'SoapFault or CouldNotSaveException does not contain exception message.' + ); } /** From da05cc8a24613ce8b37166d17c1ce2b719914b44 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Wed, 25 Sep 2019 15:34:34 +0300 Subject: [PATCH 0645/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../Sales/Model/Order/ShipmentTest.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index 0c9728b71c82d..c52450d413ad6 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -155,6 +155,7 @@ private function getOrder(string $incrementId): OrderInterface */ public function testGetTracksCollection() { + $trackCount = 1; $order = $this->getOrder('100000001'); $items = []; foreach ($order->getItems() as $item) { @@ -175,6 +176,7 @@ public function testGetTracksCollection() $shipment->addTrack($track); $this->shipmentRepository->save($shipment); + $shipmentTracksCollection = $shipment->getTracksCollection(); $secondOrder = $this->getOrder('100000002'); $secondOrderItems = []; @@ -193,11 +195,26 @@ public function testGetTracksCollection() $secondOrderShipment->addTrack($secondShipmentTrack); $this->shipmentRepository->save($secondOrderShipment); + $secondShipmentTrackCollection = $secondOrderShipment->getTracksCollection(); + + $shipmentId = $shipment->getId(); + $shipmentTrackIds = $shipmentTracksCollection->getColumnValues('parent_id'); + foreach ($shipmentTrackIds as $trackShipmentId) { + self::assertEquals($shipmentId, $trackShipmentId); + } + self::assertCount($trackCount, $shipmentTrackIds); + + $secondShipmentId = $secondOrderShipment->getId(); + $secondShipmentTrackIds = $secondShipmentTrackCollection->getColumnValues('parent_id'); + foreach ($secondShipmentTrackIds as $trackShipmentId) { + self::assertEquals($secondShipmentId, $trackShipmentId); + } + self::assertCount($trackCount, $secondShipmentTrackIds); self::assertEmpty( array_intersect( - $shipment->getTracksCollection()->getColumnValues('id'), - $secondOrderShipment->getTracksCollection()->getColumnValues('id') + $shipmentTracksCollection->getColumnValues('id'), + $secondShipmentTrackCollection->getColumnValues('id') ) ); } From ff1a40b265c78a0eb8d8d449d22ad3a12628c3a1 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 25 Sep 2019 11:15:57 -0500 Subject: [PATCH 0646/2437] MC-20279: Update AdvancedSearch related MFTF tests to Elasticsearch --- .../AdvanceCatalogSearchBundleProductTest.xml | 173 ++++++++++++++++++ ...CatalogSearchAdvancedResultMainSection.xml | 1 + ...AdvanceCatalogSearchGroupedProductTest.xml | 153 ++++++++++++++++ 3 files changed, 327 insertions(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index 52bce67600888..13ea7c68c6840 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -17,6 +17,47 @@ <severity value="MAJOR"/> <testCaseId value="MC-139"/> <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchBundleByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -87,6 +128,47 @@ <severity value="MAJOR"/> <testCaseId value="MC-242"/> <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchBundleByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -122,6 +204,47 @@ <severity value="MAJOR"/> <testCaseId value="MC-250"/> <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchBundleByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -157,6 +280,56 @@ <severity value="MAJOR"/> <testCaseId value="MC-251"/> <group value="Bundle"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <getData entity="GetProduct" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchBundleByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml index 6b28b4f36c6a7..eb3bc8e79d7b5 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedResultMainSection.xml @@ -17,5 +17,6 @@ <element name="message" type="text" selector="div.message div"/> <element name="itemFound" type="text" selector=".search.found>strong"/> <element name="productName" type="text" selector=".product.name.product-item-name>a"/> + <element name="nthProductName" type="text" selector="li.product-item:nth-of-type({{var1}}) .product-item-name>a" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml index 2a600d38250f8..5b7c0cf4c4a0a 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml @@ -17,6 +17,42 @@ <severity value="MAJOR"/> <testCaseId value="MC-141"/> <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchGroupedProductByNameMysqlTest" extends="AdvanceCatalogSearchSimpleProductByNameTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -77,6 +113,42 @@ <severity value="MAJOR"/> <testCaseId value="MC-282"/> <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchGroupedProductByDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -107,6 +179,42 @@ <severity value="MAJOR"/> <testCaseId value="MC-283"/> <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchGroupedProductByShortDescriptionMysqlTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> @@ -137,6 +245,51 @@ <severity value="MAJOR"/> <testCaseId value="MC-284"/> <group value="GroupedProduct"/> + <group value="SearchEngineElasticsearch"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <getData entity="GetProduct3" stepKey="arg1"> + <requiredEntity createDataKey="product"/> + </getData> + <getData entity="GetProduct" stepKey="arg2"> + <requiredEntity createDataKey="simple1"/> + </getData> + <getData entity="GetProduct" stepKey="arg3"> + <requiredEntity createDataKey="simple2"/> + </getData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + <see userInput="3 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <see userInput="$$product.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('1')}}" stepKey="seeProductName"/> + <see userInput="$$simple1.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('2')}}" stepKey="seeSimple1ProductName"/> + <see userInput="$$simple2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.nthProductName('3')}}" stepKey="seeSimple2ProductName"/> + </test> + <test name="AdvanceCatalogSearchGroupedProductByPriceMysqlTest" extends="AdvanceCatalogSearchSimpleProductByPriceTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> + <severity value="MAJOR"/> + <testCaseId value=""/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> From 113519abdc1f16450d2f6c3b34d52cdb6ad908c7 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Wed, 25 Sep 2019 11:54:46 -0500 Subject: [PATCH 0647/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality "Added review comments to remove extra spaces" --- ...oductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 7974618cde1a1..82395e5d6e0eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -66,7 +66,6 @@ <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> - <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> From 055710132d6b560b7b3a12b69f18d2ef9fd6cd70 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Thu, 22 Aug 2019 10:35:57 -0500 Subject: [PATCH 0648/2437] MC-19303: Shipping price shows 0 on Order view page after multiple address checkout - MFTF tests cases updated --- .../Model/Checkout/Type/Multishipping.php | 13 ++++ .../AdminSalesOrderActionGroup.xml | 29 ++++++++ ...eckingWithMultipleAddressesActionGroup.xml | 3 +- .../ActionGroup/PlaceOrderActionGroup.xml | 3 +- .../ActionGroup/ReviewOrderActionGroup.xml | 3 +- .../ActionGroup/SalesOrderActionGroup.xml | 38 ++++++++++ .../SelectBillingInfoActionGroup.xml | 3 +- .../SelectShippingInfoActionGroup.xml | 3 +- .../Mftf/Page/MultishippingCheckoutPage.xml | 2 +- .../Mftf/Section/PaymentMethodSection.xml | 2 +- .../Test/Mftf/Section/ReviewOrderSection.xml | 3 +- .../Test/Mftf/Section/SalesOrderSection.xml | 15 ++++ .../Mftf/Section/ShippingMethodSection.xml | 3 +- ...oreFrontMyAccountWithMultishipmentTest.xml | 69 +++++++++++++++++++ .../Model/Checkout/Type/MultishippingTest.php | 30 +++++++- 15 files changed, 201 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml create mode 100644 app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml create mode 100644 app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml create mode 100644 app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 7105fd4e9d26d..d1103abfbb94e 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -691,6 +691,19 @@ protected function _prepareOrder(\Magento\Quote\Model\Quote\Address $address) $this->quoteAddressToOrder->convert($address) ); + $shippingMethodCode = $address->getShippingMethod(); + if (isset($shippingMethodCode) && !empty($shippingMethodCode)) { + $rate = $address->getShippingRateByCode($shippingMethodCode); + $shippingPrice = $rate->getPrice(); + } else { + $shippingPrice = $order->getShippingAmount(); + } + $store = $order->getStore(); + $amountPrice = $store->getBaseCurrency() + ->convert($shippingPrice, $store->getCurrentCurrencyCode()); + $order->setBaseShippingAmount($shippingPrice); + $order->setShippingAmount($amountPrice); + $order->setQuote($quote); $order->setBillingAddress($this->quoteAddressToOrderAddress->convert($quote->getBillingAddress())); diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml new file mode 100644 index 0000000000000..67ba256f50ea7 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <actionGroup name="AdminSalesOrderActionGroup"> + <waitForPageLoad stepKey="waitForAdminSalesPageToLoad"/> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowLink"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <waitForPageLoad stepKey="waitForCheckTotalActionGroup"/> + <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> + <grabTextFrom selector="{{AdminOrderTotalSection.subTotal}}" stepKey="grabvalueForSubtotal"/> + <grabTextFrom selector="{{AdminOrderTotalSection.shippingAndHandling}}" stepKey="grabvalueForShippingHandling"/> + <grabTextFrom selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="grabvalueForGrandTotal"/> + <executeJS stepKey="sum_TotalValue" function=" + var subtotal = '{$grabvalueForSubtotal}'.substr(1); + var handling = '{$grabvalueForShippingHandling}'.substr(1); + var subtotal_handling = (parseFloat(subtotal) + parseFloat(handling)).toFixed(2); + return ('$' + subtotal_handling);"/> + <assertEquals stepKey="assertSubTotalPrice"> + <expectedResult type="string">$sum_TotalValue</expectedResult> + <actualResult type="string">$grabvalueForGrandTotal</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml index 333c2aec6c28e..722578a993d3f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml @@ -23,5 +23,4 @@ <click stepKey="clickOnUpdateAddress" selector="{{SingleShippingSection.updateAddress}}" after="selectSecondShippingMethod" /> <waitForPageLoad stepKey="waitForShippingInformation" after="clickOnUpdateAddress" /> </actionGroup> -</actionGroups> - +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml index efb860e314780..349d31ef1da5e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml @@ -16,5 +16,4 @@ <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> </actionGroup> -</actionGroups> - +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml index af7d897910ca3..bbd0e9ebad7aa 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml @@ -35,5 +35,4 @@ </assertEquals> </actionGroup> -</actionGroups> - +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml new file mode 100644 index 0000000000000..47cc3ffa455a0 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <actionGroup name="SalesOrderForMultiShipmentActionGroup"> + <arguments> + <argument name="shippingPrice" defaultValue="$5.00" type="string" /> + <argument name="subtotalPrice" defaultValue="$123.00" type="string" /> + <argument name="totalPrice" defaultValue="$128.00" type="string" /> + </arguments> + <waitForPageLoad stepKey="waitForSalesOrderHistoryPageToLoad" /> + <!--Click on View Order Link--> + <click stepKey="viewOrderAction" selector="{{SalesOrderSection.viewOrderLink}}"/> + <waitForPageLoad stepKey="waitForViewOrderPageToLoad" /> + <!--Check Shipping Method, Subtotal and Total Price--> + <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('subtotal')}}" stepKey="salesOrderSubtotalPrice"/> + <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('shipping')}}" stepKey="salesOrderShippingPrice"/> + <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('grand_total')}}" stepKey="salesOrderGrandTotalPrice"/> + <assertEquals stepKey="assertSubtotalPrice"> + <expectedResult type="string">{{subtotalPrice}}</expectedResult> + <actualResult type="string">$salesOrderSubtotalPrice</actualResult> + </assertEquals> + <assertEquals stepKey="assertShippingMethodPrice"> + <expectedResult type="string">{{shippingPrice}}</expectedResult> + <actualResult type="string">$salesOrderShippingPrice</actualResult> + </assertEquals> + <assertEquals stepKey="assertTotalPrice"> + <expectedResult type="string">{{totalPrice}}</expectedResult> + <actualResult type="string">$salesOrderGrandTotalPrice</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml index 3f7578953df70..c5dd97cadcc2d 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml @@ -12,5 +12,4 @@ <waitForPageLoad stepKey="waitForBillingInfoPageLoad"/> <click stepKey="goToReviewOrder" selector="{{PaymentMethodSection.goToReviewOrder}}"/> </actionGroup> -</actionGroups> - +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml index af0b2467862ba..6d1f83d174ff3 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml @@ -29,5 +29,4 @@ <waitForPageLoad stepKey="waitForRadioOptions"/> <click stepKey="goToBillingInformation" selector="{{ShippingMethodSection.goToBillingInfo}}"/> </actionGroup> -</actionGroups> - +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutPage.xml b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutPage.xml index 001002e98271c..beee76a632b22 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutPage.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Page/MultishippingCheckoutPage.xml @@ -15,4 +15,4 @@ <section name="PaymentMethodSection"/> <section name="ReviewOrderSection"/> </page> -</pages> +</pages> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml index 4e7f4a497ad4d..8113ed3aa0c07 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml @@ -11,4 +11,4 @@ <section name="PaymentMethodSection"> <element name="goToReviewOrder" type="button" selector="//span[text()='Go to Review Your Order']"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml index e13f28929dcc8..7961a0f811f64 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml @@ -23,5 +23,4 @@ <element name="secondOrderTotalPrice" type="text" selector="//div[@class='block-content'][position()=2]//table[position()=1]//tr[@class='grand totals'][position()=1]//td//span[@class='price']"/> <element name="grandTotalPrice" type="text" selector="//div[@class='checkout-review']//div[@class='grand totals']//span[@class='price']"/> </section> -</sections> - +</sections> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml new file mode 100644 index 0000000000000..c788ef5978ad5 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <section name="SalesOrderSection"> + <element name="viewOrderLink" type="text" selector="//span[text()='View Order']"/> + <element name="salesOrderPrice" type="text" selector="//div[@class='order-details-items ordered']//tr[@class='{{price_type}}']//td[@class='amount']//span[@class='price']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml index 6a2290bcf1a43..311b3ae959069 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml @@ -14,5 +14,4 @@ <element name="secondShippingMethodRadioButton" type="select" selector="//div[@class='block block-shipping'][position()=2]//div[@class='block-content']//div[@class='box box-shipping-method']//div[@class='box-content']//dl//dd[position()=2]//fieldset//div//div//input[@class='radio']"/> <element name="goToBillingInfo" type="button" selector="//span[text()='Continue to Billing Information']"/> </section> -</sections> - +</sections> \ No newline at end of file diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml new file mode 100644 index 0000000000000..a81d24e99563a --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontMyAccountWithMultishipmentTest"> + <annotations> + <features value="Multishipping"/> + <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> + <title value="Verify Shipping price for Storefront after multiple address checkout"/> + <description value="Verify that shipping price on My account matches with shipping method prices after multiple addresses checkout (Order view page)"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19303"/> + <group value="multishipping"/> + </annotations> + + <before> + <createData stepKey="category" entity="SimpleSubCategory"/> + <createData stepKey="product1" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData stepKey="product2" entity="SimpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="Simple_US_Customer_Two_Addresses" stepKey="customer"/> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <createData entity="FlatRateShippingMethodDefault" stepKey="enableFlatRateShipping"/> + <magentoCLI command="config:set payment/checkmo/active 1" stepKey="enableCheckMoneyOrderPaymentMethod"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + </before> + + <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> + <argument name="productName" value="$$product1.name$$"/> + </actionGroup> + <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> + <argument name="productName" value="$$product2.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> + <actionGroup ref="CheckingWithMultipleAddressesActionGroup" stepKey="checkoutWithMultipleAddresses"/> + <actionGroup ref="SelectMultiShippingInfoActionGroup" stepKey="checkoutWithMultipleShipping"/> + <actionGroup ref="SelectBillingInfoActionGroup" stepKey="checkoutWithPaymentMethod"/> + <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"/> + <actionGroup ref="PlaceOrderActionGroup" stepKey="placeOrder"/> + <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder"/> + <actionGroup ref="SalesOrderForMultiShipmentActionGroup" stepKey="salesOrderForMultiShipment"/> + <waitForPageLoad stepKey="waitForAdminPageToLoad"/> + <!-- Go to Stores > Configuration > Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage"/> + <actionGroup ref="AdminSalesOrderActionGroup" stepKey="ValidateOrderTotals"/> + <after> + <deleteData stepKey="deleteCategory" createDataKey="category"/> + <deleteData stepKey="deleteProduct1" createDataKey="product1"/> + <deleteData stepKey="deleteProduct2" createDataKey="product2"/> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index 02bc966873774..731365974c235 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -11,6 +11,7 @@ use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\Data\AddressSearchResultsInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Directory\Model\Currency; use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderDefault; use Magento\Multishipping\Model\Checkout\Type\Multishipping\PlaceOrderFactory; use Magento\Quote\Model\Quote\Address; @@ -44,6 +45,7 @@ use Magento\Quote\Model\ShippingAssignment; use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Sales\Model\OrderFactory; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use PHPUnit_Framework_MockObject_MockObject; use \PHPUnit\Framework\TestCase; @@ -453,7 +455,8 @@ public function testCreateOrders(): void ]; $quoteItemId = 1; $paymentProviderCode = 'checkmo'; - + $shippingPrice = '0.00'; + $currencyCode = 'USD'; $simpleProductTypeMock = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\Simple::class) ->disableOriginalConstructor() ->setMethods(['getOrderOptions']) @@ -467,6 +470,17 @@ public function testCreateOrders(): void $this->getQuoteAddressesMock($quoteAddressItemMock, $addressTotal); $this->setQuoteMockData($paymentProviderCode, $shippingAddressMock, $billingAddressMock); + $currencyMock = $this->getMockBuilder(Currency::class) + ->disableOriginalConstructor() + ->setMethods([ 'convert' ]) + ->getMock(); + $currencyMock->method('convert')->willReturn($shippingPrice); + $storeMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->setMethods(['getBaseCurrency','getCurrentCurrencyCode' ]) + ->getMock(); + $storeMock->method('getBaseCurrency')->willReturn($currencyMock); + $storeMock->method('getCurrentCurrencyCode')->willReturn($currencyCode); $orderAddressMock = $this->createSimpleMock(\Magento\Sales\Api\Data\OrderAddressInterface::class); $orderPaymentMock = $this->createSimpleMock(\Magento\Sales\Api\Data\OrderPaymentInterface::class); $orderItemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class) @@ -476,6 +490,9 @@ public function testCreateOrders(): void $orderItemMock->method('getQuoteItemId')->willReturn($quoteItemId); $orderMock = $this->getOrderMock($orderAddressMock, $orderPaymentMock, $orderItemMock); + $orderMock->expects($this->once())->method('getStore')->willReturn($storeMock); + $orderMock->expects($this->once())->method('setBaseShippingAmount')->with($shippingPrice)->willReturnSelf(); + $orderMock->expects($this->once())->method('setShippingAmount')->with($shippingPrice)->willReturnSelf(); $this->orderFactoryMock->expects($this->once())->method('create')->willReturn($orderMock); $this->dataObjectHelperMock->expects($this->once())->method('mergeDataObjects') ->with( @@ -608,12 +625,18 @@ private function getQuoteAddressesMock($quoteAddressItemMock, int $addressTotal) )->getMock(); $shippingAddressMock->method('validate')->willReturn(true); $shippingAddressMock->method('getShippingMethod')->willReturn('carrier'); - $shippingAddressMock->method('getShippingRateByCode')->willReturn('code'); $shippingAddressMock->method('getCountryId')->willReturn('EN'); $shippingAddressMock->method('getAllItems')->willReturn([$quoteAddressItemMock]); $shippingAddressMock->method('getAddressType')->willReturn('shipping'); $shippingAddressMock->method('getGrandTotal')->willReturn($addressTotal); + $shippingRateMock = $this->getMockBuilder(Address\Rate::class) + ->disableOriginalConstructor() + ->setMethods([ 'getPrice' ]) + ->getMock(); + $shippingRateMock->method('getPrice')->willReturn('0.00'); + $shippingAddressMock->method('getShippingRateByCode')->willReturn($shippingRateMock); + $billingAddressMock = $this->getMockBuilder(Address::class) ->disableOriginalConstructor() ->setMethods(['validate']) @@ -673,6 +696,9 @@ private function getOrderMock( 'getCanSendNewEmailFlag', 'getItems', 'setShippingMethod', + 'getStore', + 'setShippingAmount', + 'setBaseShippingAmount' ] )->getMock(); $orderMock->method('setQuote')->with($this->quoteMock); From e41a8166b2b48cdd88444ba4d2a9935d04fde2f8 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Wed, 25 Sep 2019 13:24:06 -0500 Subject: [PATCH 0649/2437] MC-20336: Update tests related to Catalog Search and Swatches --- .../Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml | 2 ++ .../Test/Mftf/Section/CatalogSearchAdminConfigSection.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml index a6e3dfd7eaad4..08cffcccd5203 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml @@ -25,6 +25,8 @@ <see userInput="{{MinMaxQueryLength.Hint}}" selector="{{AdminCatalogSearchConfigurationSection.maxQueryLengthHint}}" stepKey="seeHint2"/> <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.minQueryLengthInherit}}" stepKey="uncheckSystemValue"/> <fillField selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" userInput="{{minLength}}" stepKey="setMinQueryLength"/> + <!--Scrolling till Category Permissions to make sure the collapse tab for Catalog Search is visible--> + <scrollTo selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="scrollToCatalogSearchTab1"/> <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseTab"/> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml index e82ad4670f9b3..ff4c0fead0ee4 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml @@ -7,6 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogSearchConfigurationSection"> + <element name="categoryPermissions" type="button" selector="#catalog_magento_catalogpermissions-head"/> <element name="catalogSearchTab" type="button" selector="#catalog_search-head"/> <element name="checkIfCatalogSearchTabExpand" type="button" selector="#catalog_search-head:not(.open)"/> <element name="searchEngineDefaultSystemValue" type="checkbox" selector="#catalog_search_engine_inherit"/> From 8808f4398fe994ff0950e33acb962c67b0ba8eb5 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Wed, 25 Sep 2019 15:19:12 -0500 Subject: [PATCH 0650/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality "Based on review comments duplicated tests to also support Mysql search engine functionality" --- ...ateProductAttributesStoreViewScopeTest.xml | 73 +++++++ ...sUpdateProductStatusStoreViewScopeTest.xml | 142 +++++++++++++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 192 ++++++++++++++++++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 66 ++++++ 4 files changed, 473 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index f16296f99cb25..18d4b9e341cc6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-128"/> <group value="Catalog"/> <group value="Product Attributes"/> + <group value="SearchEngineElasticsearch"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -85,4 +86,76 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault1"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault1"/> </test> + <test name="AdminMassUpdateProductAttributesStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product attributes"/> + <title value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product attributes in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20467"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> + <createData entity="ApiProductWithDescription" stepKey="createProductOne"/> + <createData entity="ApiProductWithDescription" stepKey="createProductTwo"/> + </before> + <after> + <deleteData createDataKey="createProductOne" stepKey="deleteProductOne"/> + <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <argument name="keyword" value="api-simple-product"/> + </actionGroup> + <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Update attributes')}}" stepKey="clickOption"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + <seeInCurrentUrl stepKey="seeInUrl" url="catalog/product_action_attribute/edit/"/> + <!-- Switch store view --> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> + <!-- Update attribute --> + <click selector="{{AdminEditProductAttributesSection.ChangeAttributeDescriptionToggle}}" stepKey="toggleToChangeDescription"/> + <fillField selector="{{AdminEditProductAttributesSection.AttributeDescription}}" userInput="Updated $$createProductOne.custom_attributes[description]$$" stepKey="fillAttributeDescriptionField"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + + <!-- Run cron twice --> + <magentoCLI command="cron:run" stepKey="runCron1"/> + <magentoCLI command="cron:run" stepKey="runCron2"/> + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormToReload1"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="$$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Assert on storefront custom view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameCustom"> + <argument name="name" value="$$createProductOne.name$$"/> + <argument name="description" value="Updated $$createProductOne.custom_attributes[description]$$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> + </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml index 9eec6d0009488..02e8157282dee 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MAGETWO-59361"/> <group value="Catalog"/> <group value="Product Attributes"/> + <group value="SearchEngineElasticsearch"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> @@ -157,4 +158,145 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> </test> + <test name="AdminMassUpdateProductStatusStoreViewScopeMysqlTest"> + <annotations> + <features value="Catalog"/> + <stories value="Mass update product status"/> + <title value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <description value="Admin should be able to mass update product statuses in store view scope using the Mysql search engine"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20471"/> + <group value="Catalog"/> + <group value="Product Attributes"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + + <!--Create Website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createAdditionalWebsite"> + <argument name="newWebsiteName" value="Second Website"/> + <argument name="websiteCode" value="second_website"/> + </actionGroup> + + <!--Create Store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createNewStore"> + <argument name="website" value="Second Website"/> + <argument name="storeGroupName" value="Second Store"/> + <argument name="storeGroupCode" value="second_store"/> + </actionGroup> + + <!--Create Store view --> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreViewButton}}" stepKey="createStoreViewButton"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="//legend[contains(., 'Store View Information')]" stepKey="waitForNewStorePageToOpen"/> + <selectOption userInput="Second Store" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="Second Store View" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="second_store_view" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption selector="{{AdminNewStoreSection.statusDropdown}}" userInput="1" stepKey="enableStoreViewStatus"/> + <click selector="{{AdminNewStoreViewActionsSection.saveButton}}" stepKey="clickSaveStoreView" /> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal" /> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarning" /> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="dismissModal" /> + <waitForPageLoad stepKey="waitForPageLoad2" time="180" /> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" time="150" stepKey="waitForPageReolad"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage" /> + + <!--Create a Simple Product 1 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> + <argument name="product" value="simpleProductForMassUpdate"/> + <argument name="website" value="Second Website"/> + </actionGroup> + + <!--Create a Simple Product 2 --> + <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> + <argument name="product" value="simpleProductForMassUpdate2"/> + <argument name="website" value="Second Website"/> + </actionGroup> + </before> + <after> + <!--Delete website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + + <!--Delete Products --> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductForMassUpdate.name"/> + </actionGroup> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct2"> + <argument name="productName" value="simpleProductForMassUpdate2.name"/> + </actionGroup> + <actionGroup ref="logout" stepKey="amOnLogoutPage"/> + </after> + + <!-- Search and select products --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> + </actionGroup> + <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + + <!-- Filter to Second Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > + <argument name="customStore" value="'Second Store View'" /> + </actionGroup> + + <!-- Select Product 2 --> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOption"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabled"/> + <waitForPageLoad stepKey="waitForBulkUpdatePage"/> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabled"/> + + <!-- Filter to Default Store View --> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'" /> + </actionGroup> + + <!-- Verify Product Statuses --> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct1IsEnabled"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('2')}}" userInput="Enabled" stepKey="checkIfDefaultViewProduct2IsEnabled"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault"> + <argument name="name" value="{{simpleProductForMassUpdate.keyword}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> + + <!-- Enable the product in Default store view --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex2"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad2"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckboxDefaultStoreView"/> + <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckboxDefaultStoreView2"/> + + <!-- Mass update attributes --> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickDropdownDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickOptionDefaultStoreView"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Disable')}}" stepKey="clickDisabledDefaultStoreView"/> + <waitForPageLoad stepKey="waitForBulkUpdatePageDefaultStoreView"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="Disabled" stepKey="checkIfProduct2IsDisabledDefaultStoreView"/> + + <!-- Assert on storefront default view --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault2"/> + <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup" stepKey="searchByNameDefault2"> + <argument name="name" value="{{simpleProductForMassUpdate.name}}"/> + <argument name="description" value=""/> + </actionGroup> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> + <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> + </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 495b8f2d40360..ad66214e902fe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -17,6 +17,7 @@ <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-87435"/> + <group value="SearchEngineElasticsearch"/> </annotations> <before> <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> @@ -196,6 +197,197 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="compareClearCompare"/> + <comment userInput="End of Comparing Products" stepKey="endOfComparingProducts" /> + </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <annotations> + <features value="End to End scenarios"/> + <stories value="B2C guest user - MAGETWO-75411"/> + <group value="e2e"/> + <title value="You should be able to pass End to End B2C Guest User scenario using the Mysql search engine"/> + <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out using the Mysql search engine."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-20476"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> + + <createData entity="ApiCategory" stepKey="createCategory"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct1Image"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createSimpleProduct1Image1"> + <requiredEntity createDataKey="createSimpleProduct1"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct1" createDataKey="createSimpleProduct1"/> + + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createSimpleProduct2Image"> + <requiredEntity createDataKey="createSimpleProduct2"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateSimpleProduct2" createDataKey="createSimpleProduct2"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image" stepKey="deleteSimpleProduct1Image"/>--> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct1Image1" stepKey="deleteSimpleProduct1Image1"/>--> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createSimpleProduct2Image" stepKey="deleteSimpleProduct2Image"/>--> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + </after> + + <!-- Step 1: User browses catalog --> + <comment userInput="Start of browsing catalog" stepKey="startOfBrowsingCatalog" /> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <waitForPageLoad stepKey="homeWaitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeWaitForWelcomeMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> + + <!-- Open Category --> + <comment userInput="Open category" stepKey="commentOpenCategory" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="browseClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="browseAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <!-- Check simple product 1 in category --> + <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> + <!-- Check simple product 2 in category --> + <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="browseGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct2ImageSrc" stepKey="browseAssertSimpleProduct2ImageNotDefault"/> + + <!-- View Simple Product 1 --> + <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct1PageImageSrc" stepKey="browseAssertSimpleProduct1PageImageNotDefault"/> + + <!-- View Simple Product 2 --> + <comment userInput="View simple product 2" stepKey="commentViewSimpleProduct2" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> + <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct2Page"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabSimpleProduct2PageImageSrc"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabSimpleProduct2PageImageSrc" stepKey="browseAssertSimpleProduct2PageImageNotDefault"/> + <comment userInput="End of browsing catalog" stepKey="endOfBrowsingCatalog" after="browseAssertSimpleProduct2PageImageNotDefault"/> + + <!-- Step 4: User compares products --> + <comment userInput="Start of comparing products" stepKey="startOfComparingProducts" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to comparison --> + <comment userInput="Add simple product 1 to comparison" stepKey="commentAddSimpleProduct1ToComparison" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory" /> + <waitForLoadingMaskToDisappear stepKey="waitForCategoryloaded" /> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrc"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="compareAssertProduct1Page"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="compareGrabSimpleProduct1PageImageSrc"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$compareGrabSimpleProduct1PageImageSrc" stepKey="compareAssertSimpleProduct2PageImageNotDefault"/> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="compareAddSimpleProduct1ToCompare"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Add Simple Product 2 to comparison --> + <comment userInput="Add simple product 2 to comparison" stepKey="commentAddSimpleProduct2ToComparison" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategory1"/> + <waitForLoadingMaskToDisappear stepKey="waitForCompareCategory1loaded" /> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory1"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrc"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrc" stepKey="compareAssertSimpleProduct2ImageNotDefault"/> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddSimpleProduct2ToCompare"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products in comparison sidebar --> + <!-- Check simple product 1 in comparison sidebar --> + <comment userInput="Check simple product 1 in comparison sidebar" stepKey="commentCheckSimpleProduct1InComparisonSidebar" after="compareAddSimpleProduct2ToCompare"/> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct1InSidebar" after="commentCheckSimpleProduct1InComparisonSidebar"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- Check simple product 2 in comparison sidebar --> + <comment userInput="Check simple product 2 in comparison sidebar" stepKey="commentCheckSimpleProduct2InComparisonSidebar" /> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareSimpleProduct2InSidebar"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + + <!-- Check products on comparison page --> + <!-- Check simple product 1 on comparison page --> + <comment userInput="Check simple product 1 on comparison page" stepKey="commentCheckSimpleProduct1OnComparisonPage" after="compareSimpleProduct2InSidebar"/> + <actionGroup ref="StorefrontOpenAndCheckComparisionActionGroup" stepKey="compareOpenComparePage" after="commentCheckSimpleProduct1OnComparisonPage"/> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct1InComparison"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct1ImageSrcInComparison"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrcInComparison" stepKey="compareAssertSimpleProduct1ImageNotDefaultInComparison"/> + <!-- Check simple product2 on comparison page --> + <comment userInput="Check simple product 2 on comparison page" stepKey="commentCheckSimpleProduct2OnComparisonPage" /> + <actionGroup ref="StorefrontCheckCompareSimpleProductActionGroup" stepKey="compareAssertSimpleProduct2InComparison"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="compareGrabSimpleProduct2ImageSrcInComparison"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct2ImageSrcInComparison" stepKey="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + + <!-- Clear comparison sidebar --> + <comment userInput="Clear comparison sidebar" stepKey="commentClearComparisonSidebar" after="compareAssertSimpleProduct2ImageNotDefaultInComparison"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="compareClickCategoryBeforeClear" after="commentClearComparisonSidebar"/> <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="compareAssertCategory2"> <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 84c621d161f09..aa7cf933f6328 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -65,6 +65,72 @@ <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <!-- Quick Search with non-existent product name --> + <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.nonexistentProductName"/> + </actionGroup> + <see userInput="Your search returned no results." selector="{{StorefrontCatalogSearchMainSection.message}}" stepKey="searchAssertQuickSearchMessageNonExistent" after="searchFillQuickSearchNonExistent"/> + <comment userInput="End of searching products" stepKey="endOfSearchingProducts" after="searchAssertQuickSearchMessageNonExistent" /> + </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Step 2: User searches for product --> + <comment userInput="Start of searching products" stepKey="startOfSearchingProducts" after="endOfBrowsingCatalog"/> + <!-- Advanced Search with Product 1 Data --> + <comment userInput="Advanced search" stepKey="commentAdvancedSearch" after="startOfSearchingProducts"/> + <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="searchOpenAdvancedSearchForm" after="commentAdvancedSearch"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <fillField userInput="$$createSimpleProduct1.name$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" stepKey="searchAdvancedFillProductName" after="searchOpenAdvancedSearchForm"/> + <fillField userInput="$$createSimpleProduct1.sku$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" stepKey="searchAdvancedFillSKU" after="searchAdvancedFillProductName"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" stepKey="searchAdvancedFillPriceFrom" after="searchAdvancedFillSKU"/> + <fillField userInput="$$createSimpleProduct1.price$$" selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" stepKey="searchAdvancedFillPriceTo" after="searchAdvancedFillPriceFrom"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="searchClickAdvancedSearchSubmitButton" after="searchAdvancedFillPriceTo"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> + <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> + <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1ImageSrc" after="searchAssertSimpleProduct1"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchAdvancedGrabSimpleProduct1PageImageSrc" after="searchAssertSimpleProduct1Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1PageImageSrc" stepKey="searchAdvancedAssertSimpleProduct1PageImageNotDefault" after="searchAdvancedGrabSimpleProduct1PageImageSrc"/> + + <!-- Quick Search with common part of product names --> + <comment userInput="Quick search" stepKey="commentQuickSearch" after="searchAdvancedAssertSimpleProduct1PageImageNotDefault"/> + <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchQuickSearchCommonPart" after="commentQuickSearch"> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="phrase" value="CONST.apiSimpleProduct"/> + </actionGroup> + <actionGroup ref="StorefrontSelectSearchFilterCategoryActionGroup" stepKey="searchSelectFilterCategoryCommonPart" after="searchQuickSearchCommonPart"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <see userInput="3" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="searchAssertFilterCategoryProductCountCommonPart" after="searchSelectFilterCategoryCommonPart"/> + + <!-- Search simple product 1 --> + <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> + <!-- Search simple product2 --> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct2ImageSrc" after="searchAssertFilterCategorySimpleProduct2"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct2ImageSrc" stepKey="searchAssertSimpleProduct2ImageNotDefault" after="searchGrabSimpleProduct2ImageSrc"/> + <!-- Quick Search with non-existent product name --> <comment userInput="Quick Search with non-existent product name" stepKey="commentQuickSearchWithNonExistentProductName" after="searchAssertSimpleProduct2ImageNotDefault" /> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="searchFillQuickSearchNonExistent" after="commentQuickSearchWithNonExistentProductName"> From f23070c9ab907a1f42e3d47ab60cc16870ee8080 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 25 Sep 2019 15:35:22 -0500 Subject: [PATCH 0651/2437] MC-20279: Update AdvancedSearch related MFTF tests to Elasticsearch * Update test case ids for duplicate MySQL tests --- .../Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml | 8 ++++---- .../Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index 13ea7c68c6840..ddbd0fa9172a6 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -55,7 +55,7 @@ <title value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> <description value="Guest customer should be able to advance search Bundle product with product name using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20472"/> <group value="Bundle"/> <group value="SearchEngineMysql"/> </annotations> @@ -166,7 +166,7 @@ <title value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> <description value="Guest customer should be able to advance search Bundle product with product description using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20473"/> <group value="Bundle"/> <group value="SearchEngineMysql"/> </annotations> @@ -242,7 +242,7 @@ <title value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> <description value="Guest customer should be able to advance search Bundle product with product short description using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20474"/> <group value="Bundle"/> <group value="SearchEngineMysql"/> </annotations> @@ -327,7 +327,7 @@ <title value="Guest customer should be able to advance search Bundle product with product price using the MySQL search engine"/> <description value="Guest customer should be able to advance search Bundle product with product price the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20475"/> <group value="Bundle"/> <group value="SearchEngineMysql"/> </annotations> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml index 5b7c0cf4c4a0a..8c04ae4569e76 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml @@ -50,7 +50,7 @@ <title value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> <description value="Guest customer should be able to advance search Grouped product with product name using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20464"/> <group value="GroupedProduct"/> <group value="SearchEngineMysql"/> </annotations> @@ -146,7 +146,7 @@ <title value="Guest customer should be able to advance search Grouped product with product description using the MySQL search engine"/> <description value="Guest customer should be able to advance search Grouped product with product description using the MYSQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20468"/> <group value="GroupedProduct"/> <group value="SearchEngineMysql"/> </annotations> @@ -212,7 +212,7 @@ <title value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> <description value="Guest customer should be able to advance search Grouped product with product short description using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20469"/> <group value="GroupedProduct"/> <group value="SearchEngineMysql"/> </annotations> @@ -287,7 +287,7 @@ <title value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> <description value="Guest customer should be able to advance search Grouped product with product price using the MySQL search engine"/> <severity value="MAJOR"/> - <testCaseId value=""/> + <testCaseId value="MC-20470"/> <group value="GroupedProduct"/> <group value="SearchEngineMysql"/> </annotations> From 28d119cdb20899c7881ed06b8d3ab99dd8a78b63 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 25 Sep 2019 16:18:57 -0500 Subject: [PATCH 0652/2437] MC-15986: Category Filtering - added Schema changes --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 140aab37c9ceb..24a2123ff60f7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -11,7 +11,8 @@ type Query { ): Products @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") category ( - id: Int @doc(description: "Id of the category.") + id: Int @doc(description: "Id of the category.") + filter: CategoryFilterInput @doc(description: "Identifies which Category inputs to search for and return."), ): CategoryTree @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") } @@ -277,6 +278,12 @@ input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput category_id: FilterEqualTypeInput @doc(description: "Filter product by category id") } +input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { + id: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies and filters the category."), + url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category"), + name: FilterEqualTypeInput @doc(description: "Filter by the display name of the category.") +} + input ProductFilterInput @doc(description: "ProductFilterInput is deprecated, use @ProductAttributeFilterInput instead. ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.") sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.") From 188d766e76c8ce4104bbe4eb60bb611ed8b3a4d5 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 25 Sep 2019 16:03:14 -0500 Subject: [PATCH 0653/2437] MC-20358: Create New MFTF Suites for MySQL/Elasticsearch Specific Functionality * Added before block to suite to ensure config is properly set --- .../Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml index eaa90f7590bfe..9ed6ccda62200 100644 --- a/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml +++ b/app/code/Magento/Search/Test/Mftf/Suite/SearchEngineMysqlSuite.xml @@ -7,6 +7,12 @@ --> <suites xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Suite/etc/suiteSchema.xsd"> <suite name="SearchEngineMysqlSuite"> + <before> + <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after></after> <include> <group name="SearchEngineMysql" /> </include> From 2c88465acc700e9ccd3db4f97806a9fcaf88472e Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Wed, 25 Sep 2019 18:17:45 -0500 Subject: [PATCH 0654/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality "Duplicated above tests as well as they had reference in the main test" --- .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 188 ++++++++++++++++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 209 ++++++++++++++++++ .../Mftf/Test/EndToEndB2CGuestUserTest.xml | 39 ++++ .../Test/EndToEndB2CGuestUserTest.xml | 19 ++ 4 files changed, 455 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 5335ec2ad775d..4281a0eb77da8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -184,6 +184,194 @@ <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> + <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="CustomerAddressSimple" /> + </actionGroup> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceorder" after="guestSeeBillingAddress"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <comment userInput="End of checking out" stepKey="endOfCheckingOut" after="guestPlaceorder" /> + </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Step 3: User adds products to cart --> + <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> + <!-- Add Simple Product 1 to cart --> + <comment userInput="Add Simple Product 1 to cart" stepKey="commentAddSimpleProduct1ToCart" after="startOfAddingProductsToCart" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory" after="commentAddSimpleProduct1ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategoryloaded" after="cartClickCategory"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory" after="waitForCartCategoryloaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct1ImageSrc" after="cartAssertSimpleProduct1"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabSimpleProduct1PageImageSrc" after="cartAssertProduct1Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabSimpleProduct1PageImageSrc" stepKey="cartAssertSimpleProduct1PageImageNotDefault" after="cartGrabSimpleProduct1PageImageSrc"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddProduct1ToCart" after="cartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Add Simple Product 2 to cart --> + <comment userInput="Add Simple Product 2 to cart" stepKey="commentAddSimpleProduct2ToCart" after="cartAddProduct1ToCart" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory1" after="commentAddSimpleProduct2ToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory1loaded" after="cartClickCategory1"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForSimpleProduct2" after="waitForCartCategory1loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartGrabSimpleProduct2ImageSrc" after="cartAssertSimpleProduct2"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct2ImageSrc" stepKey="cartAssertSimpleProduct2ImageNotDefault" after="cartGrabSimpleProduct2ImageSrc"/> + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="cartAddProduct2ToCart" after="cartAssertSimpleProduct2ImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productCount" value="CONST.two"/> + </actionGroup> + + <!-- Check products in minicart --> + <!-- Check simple product 1 in minicart --> + <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="cartAddProduct2ToCart"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct1" after="commentCheckSimpleProduct1InMinicart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1ImageSrc" after="cartOpenMinicartAndCheckSimpleProduct1"/> + <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct1PageImageSrc" after="cartAssertMinicartProduct1Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct1PageImageSrc" stepKey="cartMinicartAssertSimpleProduct1PageImageNotDefault" after="cartMinicartGrabSimpleProduct1PageImageSrc"/> + <actionGroup ref="StorefrontOpenMinicartAndCheckSimpleProductActionGroup" stepKey="cartOpenMinicartAndCheckSimpleProduct2" after="cartMinicartAssertSimpleProduct1PageImageNotDefault"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- Check simple product2 in minicart --> + <comment userInput="Check simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="cartOpenMinicartAndCheckSimpleProduct2"/> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2ImageSrc" after="commentCheckSimpleProduct2InMinicart"/> + <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabSimpleProduct2PageImageSrc" after="cartAssertMinicartProduct2Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabSimpleProduct2PageImageSrc" stepKey="cartMinicartAssertSimpleProduct2PageImageNotDefault" after="cartMinicartGrabSimpleProduct2PageImageSrc"/> + + <!-- Check products in cart --> + <comment userInput="Check cart information" stepKey="commentCheckCartInformation" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart" after="commentCheckCartInformation"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCart" after="cartOpenCart"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check simple product 1 in cart --> + <comment userInput="Check simple product 1 in cart" stepKey="commentCheckSimpleProduct1InCart" after="cartAssertCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct1" after="commentCheckSimpleProduct1InCart"> + <argument name="product" value="$$createSimpleProduct1$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct1ImageSrc" after="cartAssertCartSimpleProduct1"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc1" after="cartAssertCartProduct1Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc1" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault1" after="cartCartGrabSimpleProduct2PageImageSrc1"/> + + <!-- Check simple product 2 in cart --> + <comment userInput="Check simple product 2 in cart" stepKey="commentCheckSimpleProduct2InCart" after="cartCartAssertSimpleProduct2PageImageNotDefault1"/> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart1" after="commentCheckSimpleProduct2InCart"/> + <actionGroup ref="StorefrontCheckCartSimpleProductActionGroup" stepKey="cartAssertCartSimpleProduct2" after="cartOpenCart1"> + <argument name="product" value="$$createSimpleProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createSimpleProduct2.name$$)}}" userInput="src" stepKey="cartCartGrabSimpleProduct2ImageSrc" after="cartAssertCartSimpleProduct2"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> + <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabSimpleProduct2PageImageSrc2" after="cartAssertCartProduct2Page"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabSimpleProduct2PageImageSrc2" stepKey="cartCartAssertSimpleProduct2PageImageNotDefault2" after="cartCartGrabSimpleProduct2PageImageSrc2"/> + <comment userInput="End of adding products to cart" stepKey="endOfAddingProductsToCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> + + <!-- Step 6: Check out --> + <comment userInput="Start of checking out" stepKey="startOfCheckingOut" after="endOfUsingCouponCode" /> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" after="startOfCheckingOut"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection" after="guestGoToCheckoutFromMinicart"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="CustomerAddressSimple" /> + </actionGroup> + + <!-- Check order summary in checkout --> + <comment userInput="Check order summary in checkout" stepKey="commentCheckOrderSummaryInCheckout" after="guestCheckoutFillingShippingSection" /> + <actionGroup ref="CheckOrderSummaryInCheckoutActionGroup" stepKey="guestCheckoutCheckOrderSummary" after="commentCheckOrderSummaryInCheckout"> + <argument name="subtotal" value="480.00"/> + <argument name="shippingTotal" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + + <!-- Check ship to information in checkout --> + <comment userInput="Check ship to information in checkout" stepKey="commentCheckShipToInformationInCheckout" after="guestCheckoutCheckOrderSummary" /> + <actionGroup ref="CheckShipToInformationInCheckoutActionGroup" stepKey="guestCheckoutCheckShipToInformation" after="commentCheckShipToInformationInCheckout"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="CustomerAddressSimple" /> + </actionGroup> + + <!-- Check shipping method in checkout --> + <comment userInput="Check shipping method in checkout" stepKey="commentCheckShippingMethodInCheckout" after="guestCheckoutCheckShipToInformation" /> + <actionGroup ref="CheckShippingMethodInCheckoutActionGroup" stepKey="guestCheckoutCheckShippingMethod" after="commentCheckShippingMethodInCheckout"> + <argument name="shippingMethod" value="E2EB2CQuote.shippingMethod" /> + </actionGroup> + + <!-- Verify Simple Product 1 is in checkout cart items --> + <comment userInput="Verify Simple Product 1 is in checkout cart items" stepKey="commentVerifySimpleProduct1IsInCheckoutCartItems" after="guestCheckoutCheckShippingMethod" /> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct1InCartItems" after="commentVerifySimpleProduct1IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct1$$"/> + </actionGroup> + + <!-- Verify Simple Product 2 is in checkout cart items --> + <comment userInput="Verify Simple Product 2 is in checkout cart items" stepKey="commentVerifySimpleProduct2IsInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct1InCartItems" /> + <actionGroup ref="CheckProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckSimpleProduct2InCartItems" after="commentVerifySimpleProduct2IsInCheckoutCartItems"> + <argument name="productVar" value="$$createSimpleProduct2$$"/> + </actionGroup> + <comment userInput="Place order with check money order payment" stepKey="commentPlaceOrderWithCheckMoneyOrderPayment" after="guestCheckoutCheckSimpleProduct2InCartItems" /> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" after="commentPlaceOrderWithCheckMoneyOrderPayment"/> <actionGroup ref="CheckBillingAddressInCheckoutActionGroup" stepKey="guestSeeBillingAddress" after="guestSelectCheckMoneyOrderPayment"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 47ee09e4b2086..04687a2314dc6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -217,4 +217,213 @@ <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigChildProduct1Image"> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryMagentoLogo" stepKey="createConfigChildProduct2Image"> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ApiProductAttributeMediaGalleryEntryTestImage" stepKey="createConfigProductImage"> + <requiredEntity createDataKey="createConfigProduct"/> + </createData> + <updateData entity="ApiSimpleProductUpdateDescription" stepKey="updateConfigProduct" createDataKey="createConfigProduct"/> + </before> + <after> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct1Image" stepKey="deleteConfigChildProduct1Image"/>--> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigChildProduct2Image" stepKey="deleteConfigChildProduct2Image"/>--> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <!-- @TODO: Uncomment once MQE-679 is fixed --> + <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + </after> + + <!-- Verify Configurable Product in checkout cart items --> + <comment userInput="Verify Configurable Product in checkout cart items" stepKey="commentVerifyConfigurableProductInCheckoutCartItems" after="guestCheckoutCheckSimpleProduct2InCartItems" /> + <actionGroup ref="CheckConfigurableProductInCheckoutCartItemsActionGroup" stepKey="guestCheckoutCheckConfigurableProductInCartItems" after="commentVerifyConfigurableProductInCheckoutCartItems"> + <argument name="productVar" value="$$createConfigProduct$$"/> + <argument name="optionLabel" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$" /> + <argument name="optionValue" value="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" /> + </actionGroup> + + <!-- Check configurable product in category --> + <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> + <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="browseGrabConfigProductImageSrc" after="browseAssertCategoryConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabConfigProductImageSrc" stepKey="browseAssertConfigProductImageNotDefault" after="browseGrabConfigProductImageSrc"/> + + <!-- View Configurable Product --> + <comment userInput="View Configurable Product" stepKey="commentViewConfigurableProduct" after="browseAssertSimpleProduct2PageImageNotDefault" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="browseGrabConfigProductPageImageSrc" after="browseAssertConfigProductPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$browseGrabConfigProductPageImageSrc" stepKey="browseAssertConfigProductPageImageNotDefault" after="browseGrabConfigProductPageImageSrc"/> + + <!-- Add Configurable Product to cart --> + <comment userInput="Add Configurable Product to cart" stepKey="commentAddConfigurableProductToCart" after="cartAddProduct2ToCart" /> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="cartClickCategory2" after="commentAddConfigurableProductToCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartCategory2loaded" after="cartClickCategory2"/> + <actionGroup ref="StorefrontCheckCategoryActionGroup" stepKey="cartAssertCategory1ForConfigurableProduct" after="waitForCartCategory2loaded"> + <argument name="category" value="$$createCategory$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartGrabConfigProductImageSrc" after="cartAssertConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc1" after="cartAssertConfigProductPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> + <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> + <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartGrabConfigProductPageImageSrc2" after="cartAssertConfigProductWithOptionPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc2" stepKey="cartAssertConfigProductPageImageNotDefault2" after="cartGrabConfigProductPageImageSrc2"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigProductToCart" after="cartAssertConfigProductPageImageNotDefault2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + + <!-- Check configurable product in minicart --> + <comment userInput="Check configurable product in minicart" stepKey="commentCheckConfigurableProductInMinicart" after="cartMinicartAssertSimpleProduct2PageImageNotDefault" /> + <actionGroup ref="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup" stepKey="cartOpenMinicartAndCheckConfigProduct" after="commentCheckConfigurableProductInMinicart"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontMinicartSection.productImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartMinicartGrabConfigProductImageSrc" after="cartOpenMinicartAndCheckConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabConfigProductImageSrc" stepKey="cartMinicartAssertConfigProductImageNotDefault" after="cartMinicartGrabConfigProductImageSrc"/> + <click selector="{{StorefrontMinicartSection.productOptionsDetailsByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProductDetails" after="cartMinicartAssertConfigProductImageNotDefault"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> + <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartMinicartGrabConfigProductPageImageSrc" after="cartAssertMinicartConfigProductPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartMinicartGrabConfigProductPageImageSrc" stepKey="cartMinicartAssertConfigProductPageImageNotDefault" after="cartMinicartGrabConfigProductPageImageSrc"/> + + <!-- Check configurable product in cart --> + <comment userInput="Check configurable product in cart" stepKey="commentCheckConfigurableProductInCart" after="cartCartAssertSimpleProduct2PageImageNotDefault2" /> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="cartOpenCart2" after="commentCheckConfigurableProductInCart"/> + <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="cartAssertCartConfigProduct" after="cartOpenCart2"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> + <!-- @TODO: Change to scalar value after MQE-498 is implemented --> + <argument name="productQuantity" value="CONST.one"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{CheckoutCartProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="cartCartGrabConfigProduct2ImageSrc" after="cartAssertCartConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabConfigProduct2ImageSrc" stepKey="cartCartAssertConfigProduct2ImageNotDefault" after="cartCartGrabConfigProduct2ImageSrc"/> + <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> + <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> + <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="cartCartGrabConfigProductPageImageSrc" after="cartAssertCartConfigProductPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartCartGrabConfigProductPageImageSrc" stepKey="cartCartAssertConfigProductPageImageNotDefault" after="cartCartGrabConfigProductPageImageSrc"/> + + <!-- Add Configurable Product to comparison --> + <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> + <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrc" after="compareAssertConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrc" stepKey="compareAssertConfigProductImageNotDefault" after="compareGrabConfigProductImageSrc"/> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="compareAddConfigProductToCompare" after="compareAssertConfigProductImageNotDefault"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product in comparison sidebar --> + <comment userInput="Add Configurable Product in comparison sidebar" stepKey="commentAddConfigurableProductInComparisonSidebar" after="compareSimpleProduct2InSidebar" /> + <actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="compareConfigProductInSidebar" after="commentAddConfigurableProductInComparisonSidebar"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Check configurable product on comparison page --> + <comment userInput="Add Configurable Product on comparison page" stepKey="commentAddConfigurableProductOnComparisonPage" after="compareAssertSimpleProduct2ImageNotDefaultInComparison" /> + <actionGroup ref="StorefrontCheckCompareConfigurableProductActionGroup" stepKey="compareAssertConfigProductInComparison" after="commentAddConfigurableProductOnComparisonPage"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductCompareMainSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="compareGrabConfigProductImageSrcInComparison" after="compareAssertConfigProductInComparison"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabConfigProductImageSrcInComparison" stepKey="compareAssertConfigProductImageNotDefaultInComparison" after="compareGrabConfigProductImageSrcInComparison"/> + </test> </tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 0d365dc089e43..e4e9a62780948 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -38,6 +38,45 @@ <argument name="total" value="447.00"/> </actionGroup> + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="495.00"/> + </actionGroup> + <comment userInput="End of using coupon code" stepKey="endOfUsingCouponCode" after="cartAssertCartAfterCancelCoupon" /> + </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <before> + <createData entity="ApiSalesRule" stepKey="createSalesRule"/> + <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + </after> + + <!-- Step 5: User uses coupon codes --> + <comment userInput="Start of using coupon code" stepKey="startOfUsingCouponCode" after="endOfComparingProducts" /> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="couponOpenCart" after="startOfUsingCouponCode"/> + + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="couponApplyCoupon" after="couponOpenCart"> + <argument name="coupon" value="$$createSalesRuleCoupon$$"/> + </actionGroup> + + <actionGroup ref="StorefrontCheckCouponAppliedActionGroup" stepKey="couponCheckAppliedDiscount" after="couponApplyCoupon"> + <argument name="rule" value="$$createSalesRule$$"/> + <argument name="discount" value="48.00"/> + </actionGroup> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="couponCheckCartWithDiscount" after="couponCheckAppliedDiscount"> + <argument name="subtotal" value="480.00"/> + <argument name="shipping" value="15.00"/> + <argument name="shippingMethod" value="Flat Rate - Fixed"/> + <argument name="total" value="447.00"/> + </actionGroup> + <actionGroup ref="StorefrontCancelCouponActionGroup" stepKey="couponCancelCoupon" after="couponCheckCartWithDiscount"/> <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssertCartAfterCancelCoupon" after="couponCancelCoupon"> <argument name="subtotal" value="480.00"/> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml index f0bfec543f281..cac9d0c3cb55f 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml @@ -27,4 +27,23 @@ <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> </test> + <test name="EndToEndB2CGuestUserMysqlTest"> + <!-- Search configurable product --> + <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> + <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> + <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> + <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> + </actionGroup> + <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.productImage}}" userInput="src" stepKey="searchGrabConfigProductPageImageSrc" after="searchAssertConfigProductPage"/> + <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$searchGrabConfigProductPageImageSrc" stepKey="searchAssertConfigProductPageImageNotDefault" after="searchGrabConfigProductPageImageSrc"/> + </test> </tests> From 3a79507dc12c61480814b83958c989b7106582b4 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 25 Sep 2019 17:46:46 +0400 Subject: [PATCH 0655/2437] MC-18215: Error message while creating shipping label - Added automated test script --- .../AdminGeneralStoreInfomationConfigData.xml | 37 +++++ .../ActionGroup/AdminProductActionGroup.xml | 6 + .../Mftf/ActionGroup/CheckoutActionGroup.xml | 1 + .../Customer/Test/Mftf/Data/AddressData.xml | 1 + .../Fedex/Test/Mftf/Data/FexExConfigData.xml | 49 +++++++ .../Test/AdminCreatingShippingLabelTest.xml | 130 ++++++++++++++++++ .../ActionGroup/AdminShipmentActionGroup.xml | 20 +++ .../Data/AdminShippingSettingsConfigData.xml | 26 ++++ .../AdminShipmentCreatePackageSection.xml | 20 +++ .../Section/AdminShipmentTotalSection.xml | 1 + ...mentTrackingInformationShippingSection.xml | 17 +++ 11 files changed, 308 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml create mode 100644 app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml create mode 100644 app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml create mode 100644 app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTrackingInformationShippingSection.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml new file mode 100644 index 0000000000000..ae0801736d3a9 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminGeneralSetStoreNameConfigData"> + <data key="path">general/store_information/name</data> + <data key="value">New Store Information</data> + </entity> + <entity name="AdminGeneralSetStorePhoneConfigData"> + <data key="path">general/store_information/phone</data> + </entity> + <entity name="AdminGeneralSetCountryConfigData"> + <data key="path">general/store_information/country_id</data> + </entity> + <entity name="AdminGeneralSetCityConfigData"> + <data key="path">general/store_information/city</data> + </entity> + <entity name="AdminGeneralSetPostcodeConfigData"> + <data key="path">general/store_information/postcode</data> + </entity> + <entity name="AdminGeneralSetStreetAddressConfigData"> + <data key="path">general/store_information/street_line1</data> + </entity> + <entity name="AdminGeneralSetStreetAddress2ConfigData"> + <data key="path">general/store_information/street_line2</data> + </entity> + <entity name="AdminGeneralSetVatNumberConfigData"> + <data key="path">general/store_information/merchant_vat_number</data> + <data key="value">111607872</data> + </entity> +</entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 5c5ee0f9cb321..0a8a93fac4145 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -108,6 +108,12 @@ <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> </actionGroup> + <actionGroup name="AdminFillProductCountryOfManufactureActionGroup"> + <arguments> + <argument name="countryId" type="string" defaultValue="US"/> + </arguments> + <selectOption selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="{{countryId}}" stepKey="countryOfManufactureDropDown"/> + </actionGroup> <!--Check that required fields are actually required--> <actionGroup name="checkRequiredFieldsInProductForm"> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 7f6980d0c9744..66f8ed541ffd9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -64,6 +64,7 @@ <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="waitForShippingMethod"/> <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 4d7a39b3246e1..986369f4ebc66 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -291,6 +291,7 @@ <data key="company">Magento</data> <array key="street"> <item>Augsburger Strabe 41</item> + <item>Augsburger Strabe 42</item> </array> <data key="city">Berlin</data> <data key="country_id">DE</data> diff --git a/app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml b/app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml new file mode 100644 index 0000000000000..d03403970ae55 --- /dev/null +++ b/app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminFedexEnableForCheckoutConfigData" type="fedex_config"> + <data key="path">carriers/fedex/active</data> + <data key="value">1</data> + <data key="label">Yes</data> + </entity> + <entity name="AdminFedexEnableSandboxModeConfigData" type="fedex_config"> + <data key="path">carriers/fedex/sandbox_mode</data> + <data key="value">1</data> + <data key="label">Yes</data> + </entity> + <entity name="AdminFedexEnableDebugConfigData" type="fedex_config"> + <data key="path">carriers/fedex/debug</data> + <data key="value">1</data> + <data key="label">Yes</data> + </entity> + <entity name="AdminFedexEnableShowMethodConfigData" type="fedex_config"> + <data key="path">carriers/fedex/showmethod</data> + <data key="value">1</data> + <data key="label">Yes</data> + </entity> + <entity name="AdminFedexDisableShowMethodConfigData" type="fedex_config"> + <data key="path">carriers/fedex/showmethod</data> + <data key="value">0</data> + <data key="label">No</data> + </entity> + <entity name="AdminFedexDisableDebugConfigData" type="fedex_config"> + <data key="path">carriers/fedex/debug</data> + <data key="value">0</data> + <data key="label">No</data> + </entity> + <entity name="AdminFedexDisableSandboxModeConfigData" type="fedex_config"> + <data key="path">carriers/fedex/sandbox_mode</data> + <data key="value">0</data> + <data key="label">No</data> + </entity> + <entity name="AdminFedexDisableForCheckoutConfigData" type="fedex_config"> + <data key="path">carriers/fedex/active</data> + <data key="value">0</data> + <data key="label">No</data> + </entity> +</entities> diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml new file mode 100644 index 0000000000000..4b4654abb2bf0 --- /dev/null +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreatingShippingLabelTest"> + <annotations> + <features value="Fedex"/> + <stories value="Shipping label"/> + <title value="Creating shipping label"/> + <description value="Creating shipping label"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20287"/> + <useCaseId value="MC-18215"/> + <group value="shipping"/> + <skip> + <issueId value="MQE-1578"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create product --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Set Fedex configs data--> + <magentoCLI command="config:set {{AdminFedexEnableForCheckoutConfigData.path}} {{AdminFedexEnableForCheckoutConfigData.value}}" stepKey="enableCheckout"/> + <magentoCLI command="config:set {{AdminFedexEnableSandboxModeConfigData.path}} {{AdminFedexEnableSandboxModeConfigData.value}}" stepKey="enableSandbox"/> + <magentoCLI command="config:set {{AdminFedexEnableDebugConfigData.path}} {{AdminFedexEnableDebugConfigData.value}}" stepKey="enableDebug"/> + <magentoCLI command="config:set {{AdminFedexEnableShowMethodConfigData.path}} {{AdminFedexEnableShowMethodConfigData.value}}" stepKey="enableShowMethod"/> + <!--TODO: add fedex credentials--> + <!--Set StoreInformation configs data--> + <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} '{{AdminGeneralSetStoreNameConfigData.value}}'" stepKey="setStoreInformationName"/> + <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.telephone}}" stepKey="setStoreInformationPhone"/> + <magentoCLI command="config:set {{AdminGeneralSetCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setStoreInformationCountry"/> + <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setStoreInformationCity"/> + <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setStoreInformationPostcode"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setStoreInformationStreetAddress"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[1]}}'" stepKey="setStoreInformationStreetAddress2"/> + <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} {{AdminGeneralSetVatNumberConfigData.value}}" stepKey="setStoreInformationVatNumber"/> + <!--Set Shipping settings origin data--> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setOriginCountry"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setOriginCity"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setOriginZipCode"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setOriginStreetAddress"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[1]}}'" stepKey="setOriginStreetAddress2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!--Reset configs--> + <magentoCLI command="config:set {{AdminFedexDisableForCheckoutConfigData.path}} {{AdminFedexDisableForCheckoutConfigData.value}}" stepKey="disableCheckout"/> + <magentoCLI command="config:set {{AdminFedexDisableSandboxModeConfigData.path}} {{AdminFedexDisableSandboxModeConfigData.value}}" stepKey="disableSandbox"/> + <magentoCLI command="config:set {{AdminFedexDisableDebugConfigData.path}} {{AdminFedexDisableDebugConfigData.value}}" stepKey="disableDebug"/> + <magentoCLI command="config:set {{AdminFedexDisableShowMethodConfigData.path}} {{AdminFedexDisableShowMethodConfigData.value}}" stepKey="disableShowMethod"/> + <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} ''" stepKey="setStoreInformationName"/> + <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} ''" stepKey="setStoreInformationPhone"/> + <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} ''" stepKey="setStoreInformationCity"/> + <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} ''" stepKey="setStoreInformationPostcode"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} ''" stepKey="setStoreInformationStreetAddress"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} ''" stepKey="setStoreInformationStreetAddress2"/> + <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} ''" stepKey="setStoreInformationVatNumber"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} ''" stepKey="setOriginCity"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} ''" stepKey="setOriginZipCode"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} ''" stepKey="setOriginStreetAddress"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} ''" stepKey="setOriginStreetAddress2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Add country of manufacture to product--> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="amOnEditPage"/> + <waitForPageLoad stepKey="waitForEditPage"/> + <actionGroup ref="AdminFillProductCountryOfManufactureActionGroup" stepKey="fillCountryOfManufacture"> + <argument name="countryId" value="DE"/> + </actionGroup> + <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <!--Place for order using FedEx shipping method--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="addAddress"> + <argument name="customerVar" value="Simple_US_Utah_Customer"/> + <argument name="customerAddressVar" value="US_Address_California"/> + <argument name="shippingMethod" value="Federal Express"/> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="customerPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + <!--Open created order in admin--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchOrder"> + <argument name="keyword" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <!--Create Invoice--> + <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> + <!--Create shipping label--> + <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipmentIntoOrder"/> + <checkOption selector="{{AdminShipmentTotalSection.createShippingLabel}}" stepKey="checkCreateShippingLabel"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + <actionGroup ref="AdminShipmentCreatePackageActionGroup" stepKey="createPackage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <actionGroup ref="AdminGoToShipmentTabActionGroup" stepKey="goToShipmentTab"/> + <click selector="{{AdminOrderShipmentsTabSection.viewGridRow('1')}}" stepKey="clickRowToViewShipment"/> + <waitForPageLoad stepKey="waitForShipmentItemsSection"/> + <seeElement selector="{{AdminShipmentTrackingInformationShippingSection.shippingInfoTable}}" stepKey="seeInformationTable"/> + <seeElement selector="{{AdminShipmentTrackingInformationShippingSection.shippingNumber}}" stepKey="seeShippingNumberElement"/> + <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethod}}" stepKey="grabShippingMethod"/> + <grabTextFrom selector="{{AdminShipmentTrackingInformationShippingSection.shippingMethodTitle}}" stepKey="grabShippingMethodTitle"/> + <assertEquals actual="$grabShippingMethod" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodIsFedEx"/> + <assertEquals actual="$grabShippingMethodTitle" expectedType="string" expected="Federal Express" stepKey="assertShippingMethodTitleIsFedEx"/> + </test> +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml index e9809ae0f3e7f..d160f95e2a9cc 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml @@ -62,4 +62,24 @@ <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> </actionGroup> + <actionGroup name="AdminShipmentCreatePackageActionGroup"> + <arguments> + <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> + </arguments> + <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="waitForAddProductElement"/> + <click selector="{{AdminShipmentCreatePackageMainSection.addProductsToPackage}}" stepKey="clickAddProducts"/> + <waitForElementVisible selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="waitForProductBeVisible"/> + <checkOption selector="{{AdminShipmentCreatePackageProductGridSection.concreteProductCheckbox('productName')}}" stepKey="checkProductCheckbox"/> + <waitForElementVisible selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="waitForAddSelectedProductElement"/> + <click selector="{{AdminShipmentCreatePackageMainSection.addSelectedProductToPackage}}" stepKey="clickAddSelectedProduct"/> + <waitForElementNotVisible selector="{{AdminShipmentCreatePackageMainSection.saveButtonDisabled}}" stepKey="waitForBeEnabled"/> + <click selector="{{AdminShipmentCreatePackageMainSection.save}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappear"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created. You created the shipping label." stepKey="seeShipmentCreateSuccess"/> + </actionGroup> + <actionGroup name="AdminGoToShipmentTabActionGroup"> + <click selector="{{AdminOrderDetailsOrderViewSection.shipments}}" stepKey="clickOrderShipmentsTab"/> + <waitForLoadingMaskToDisappear stepKey="waitForShipmentTabLoad" after="clickOrderShipmentsTab"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml new file mode 100644 index 0000000000000..ad366fd7294e5 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminShippingSettingsOriginCountryConfigData"> + <data key="path">shipping/origin/country_id</data> + </entity> + <entity name="AdminShippingSettingsOriginZipCodeConfigData"> + <data key="path">shipping/origin/postcode</data> + </entity> + <entity name="AdminShippingSettingsOriginCityConfigData"> + <data key="path">shipping/origin/city</data> + </entity> + <entity name="AdminShippingSettingsOriginStreetAddressConfigData"> + <data key="path">shipping/origin/street_line1</data> + </entity> + <entity name="AdminShippingSettingsOriginStreetAddress2ConfigData"> + <data key="path">shipping/origin/street_line2</data> + </entity> +</entities> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml new file mode 100644 index 0000000000000..5f33921b5a44f --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentCreatePackageSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminShipmentCreatePackageMainSection"> + <element name="addProductsToPackage" type="button" selector="#package_block_1 button[data-action='package-add-items']"/> + <element name="addSelectedProductToPackage" type="button" selector="#package_block_1 button[data-action='package-save-items']"/> + <element name="save" type="button" selector="button[data-action='save-packages']"/> + <element name="saveButtonDisabled" type="button" selector="button[data-action='save-packages']._disabled"/> + </section> + <section name="AdminShipmentCreatePackageProductGridSection"> + <element name="concreteProductCheckbox" type="checkbox" selector="//td[contains(text(), '{{productName}}')]/parent::tr//input[contains(@class,'checkbox')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml index f2f39d77d8d79..d76ba0493829e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTotalSection.xml @@ -12,5 +12,6 @@ <element name="CommentText" type="textarea" selector="#shipment_comment_text"/> <element name="AppendComments" type="checkbox" selector=".order-totals input#notify_customer"/> <element name="EmailCopy" type="checkbox" selector=".order-totals input#send_email"/> + <element name="createShippingLabel" type="checkbox" selector="input#create_shipping_label"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTrackingInformationShippingSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTrackingInformationShippingSection.xml new file mode 100644 index 0000000000000..bbb61ed013a30 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentTrackingInformationShippingSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminShipmentTrackingInformationShippingSection"> + <element name="shippingInfoTable" type="block" selector="#shipment_tracking_info"/> + <element name="shippingMethod" type="text" selector="#shipment_tracking_info .odd .col-carrier"/> + <element name="shippingMethodTitle" type="text" selector="#shipment_tracking_info .odd .col-title"/> + <element name="shippingNumber" type="text" selector="#shipment_tracking_info .odd .col-number"/> + </section> +</sections> \ No newline at end of file From dfb0c46bb092785659d6dee354f62d5f034f79b5 Mon Sep 17 00:00:00 2001 From: vital_pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Thu, 26 Sep 2019 13:29:48 +0300 Subject: [PATCH 0656/2437] MAGETWO-99838: Error message '"customer address attribute" is a required value.' is absent on Storefront - Updated unit test --- .../Model/Validator/Attribute/DataTest.php | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php index 9f1e74fccbd4f..acba37cc45788 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/DataTest.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Eav\Test\Unit\Model\Validator\Attribute; /** @@ -21,11 +23,17 @@ class DataTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + private $objectManager; + /** * @inheritdoc */ protected function setUp() { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->attrDataFactory = $this->getMockBuilder(\Magento\Eav\Model\AttributeDataFactory::class) ->setMethods(['create']) ->setConstructorArgs( @@ -36,8 +44,11 @@ protected function setUp() ) ->getMock(); - $this->model = new \Magento\Eav\Model\Validator\Attribute\Data( - $this->attrDataFactory + $this->model = $this->objectManager->getObject( + \Magento\Eav\Model\Validator\Attribute\Data::class, + [ + '_attrDataFactory' => $this->attrDataFactory + ] ); } @@ -468,7 +479,7 @@ protected function _getDataModelMock($returnValue, $argument = null) $dataModel = $this->getMockBuilder( \Magento\Eav\Model\Attribute\Data\AbstractData::class )->disableOriginalConstructor()->setMethods( - ['validateValue'] + ['setExtractedData', 'validateValue'] )->getMockForAbstractClass(); if ($argument) { $dataModel->expects( @@ -509,24 +520,13 @@ public function testIsValidWithoutData() : void $attributeData = ['attribute_code' => 'attribute', 'frontend_input' => 'text', 'is_visible' => true]; $entity = $this->_getEntityMock(); $attribute = $this->_getAttributeMock($attributeData); - $this->model->setAttributes([$attribute])->setData([]); - $dataModel = $this->getMockBuilder(\Magento\Eav\Model\Attribute\Data\AbstractData::class) - ->disableOriginalConstructor() - ->setMethods(['validateValue']) - ->getMockForAbstractClass(); - $dataModel->expects($this->once()) - ->method('validateValue') - // only empty string - ->with( - $this->logicalAnd( - $this->isEmpty(), - $this->isType('string') - ) - )->willReturn(true); + $dataModel = $this->_getDataModelMock(true, $this->logicalAnd($this->isEmpty(), $this->isType('string'))); + $dataModel->expects($this->once())->method('setExtractedData')->with([])->willReturnSelf(); $this->attrDataFactory->expects($this->once()) ->method('create') ->with($attribute, $entity) ->willReturn($dataModel); - $this->assertEquals(true, $this->model->isValid($entity)); + $this->model->setAttributes([$attribute])->setData([]); + $this->assertTrue($this->model->isValid($entity)); } } From 8b328e9c68306d5c2d6632e3548d2eaea7c9c0d6 Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Thu, 26 Sep 2019 13:50:22 +0300 Subject: [PATCH 0657/2437] MC-20071: Fix Skipped MFTF Tests From MC-17140: MAGETWO-98211, MC-56, MC-88 --- .../Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 4cd962e76bc96..5f2d889d369fd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -67,10 +67,9 @@ <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCronFirstTime"/> - <magentoCLI command="cron:run" stepKey="runCronSecondTime"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="cronSchedule"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="cronRun"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Check storefront for description --> <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> From 70fa65be9a3ee0bfdf79f871035fa431f4af72ca Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Wed, 25 Sep 2019 23:42:55 +0400 Subject: [PATCH 0658/2437] MC-10974: Return "Is Active" toggle to Catalog Price Rule Page - Updated automated test script. --- .../Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml | 2 +- .../Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml | 2 +- .../Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index 2d4ef3786d1c6..e1c168b4a025f 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -25,7 +25,7 @@ <element name="websites" type="select" selector="[name='website_ids']"/> <element name="active" type="checkbox" selector="//div[contains(@class, 'admin__actions-switch')]/input[@name='is_active']/../label"/> <element name="activeIsEnabled" type="checkbox" selector="(//div[contains(@class, 'admin__actions-switch')])[1]/input[@value='1']"/> - <element name="activePosition" type="checkbox" selector="//fieldset[@class='admin__fieldset']//div[4]"/> + <element name="activePosition" type="checkbox" selector="fieldset[class='admin__fieldset'] div[class*='_required']:nth-of-type(4)"/> <element name="websitesOptions" type="select" selector="[name='website_ids'] option"/> <element name="customerGroups" type="select" selector="[name='customer_group_ids']"/> <element name="customerGroupsOptions" type="select" selector="[name='customer_group_ids'] option"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index be195f564145e..1b85f3b045c5d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -18,7 +18,7 @@ <element name="ProductRegularPriceByName" type="text" selector="//div[descendant::*[contains(text(), '{{var1}}')]]//*[contains(@class, 'subtotal')]" parameterized="true"/> - <element name="productFirstPriceByName" type="text" selector="(//tbody//a[@title='{{prodName}}']/../following-sibling::*//span[@class='price'])[1]" parameterized="true"/> + <element name="productFirstPrice" type="text" selector="td[class~=price] span[class='price']"/> <element name="ProductImageByName" type="text" selector="//main//table[@id='shopping-cart-table']//tbody//tr//img[contains(@class, 'product-image-photo') and @alt='{{var1}}']" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index 443b4bf649ebf..e00906386e46b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -14,8 +14,8 @@ <element name="productLinkByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details']//a[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="productPriceByName" type="text" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//span[@class='price']" parameterized="true"/> <element name="productPriceByItsName" type="text" selector="//a[normalize-space()='{{prodName}}']/../following-sibling::*//*[@class='price']" parameterized="true"/> - <element name="productImageByName" type="text" selector="//header//ol[@id='mini-cart']//span[@class='product-image-container']//img[@alt='{{var1}}']" parameterized="true"/> - <element name="productImageByItsName" type="text" selector="//img[@alt='{{prodName}}']" parameterized="true"/> + <element name="productImageByName" type="text" selector="header ol[id='mini-cart'] span[class='product-image-container'] img[alt='{{prodName}}']" parameterized="true"/> + <element name="productImageByItsName" type="text" selector="img[alt='{{prodName}}']" parameterized="true"/> <element name="productName" type="text" selector=".product-item-name"/> <element name="productOptionsDetailsByName" type="button" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//span[.='See Details']" parameterized="true"/> <element name="productOptionByNameAndAttribute" type="text" selector="//header//ol[@id='mini-cart']//div[@class='product-item-details'][.//a[contains(text(), '{{var1}}')]]//dt[@class='label' and .='{{var2}}']/following-sibling::dd[@class='values']//span" parameterized="true"/> From 6025197fc0b4e5b298982e0cf5f76bdca5fcd04b Mon Sep 17 00:00:00 2001 From: DmitryTsymbal <d.tsymbal@atwix.com> Date: Thu, 26 Sep 2019 15:37:18 +0300 Subject: [PATCH 0659/2437] refactoring --- .../Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml | 3 ++- .../Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml index 899ca8b7d7f4e..533bbb6760573 100644 --- a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml @@ -13,9 +13,10 @@ <actionGroup name="AdminCreatesNewIntegrationActionGroup"> <arguments> <argument name="name" type="string"/> + <argument name="password" type="string"/> </arguments> <fillField stepKey="fillNameField" selector="{{AddNewIntegrationSection.name}}" userInput="{{name}}"/> - <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <fillField stepKey="fillAdminPasswordField" selector="{{AddNewIntegrationSection.password}}" userInput="{{password}}"/> <!--Click the "Save" Button --> <click stepKey="clickSaveButton" selector="{{AddNewIntegrationSection.saveButton}}"/> </actionGroup> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml index 00dc9b320d7ad..bc226a70375f0 100644 --- a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -30,6 +30,7 @@ <!-- Create New Integration --> <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> </actionGroup> </before> <after> @@ -55,5 +56,6 @@ <actionGroup ref="AssertDeletedIntegrationIsNotInGridActionGroup" stepKey="dontSeeIntegration"> <argument name="name" value="Integration1"/> </actionGroup> + <!-- END TEST BODY --> </test> </tests> From 9788ab394334ad9952d341339568222d50fccfad Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Tue, 24 Sep 2019 17:37:36 +0300 Subject: [PATCH 0660/2437] MC-17653: Cannot schedule update for catalog price rule for date attribute - Updated automated test script. --- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 2 +- .../Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml | 1 + .../Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml | 1 - .../SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 06dda37222067..c364dca59223b 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -67,7 +67,7 @@ <scrollTo selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="scrollToConditionsTab" after="expandConditions"/> <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule" after="scrollToConditionsTab"/> <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule" after="waitForNewRule"/> - <selectOption selector="{{PriceRuleConditionsSection.conditionsDropdown}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="selectProductAttribute" after="clickNewRule"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionsDropdown}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="selectProductAttribute" after="clickNewRule"/> <waitForPageLoad stepKey="waitForAttributeLoad" after="selectProductAttribute"/> <!--Assert that attribute contains today date without time--> <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index ba0493d8e995b..02bd539deffe6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -43,6 +43,7 @@ <section name="AdminNewCatalogPriceRuleConditions"> <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child"/> + <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index c37b023317bf7..749d1bee0661a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -11,7 +11,6 @@ <annotations> <stories value="Duplicate Product"/> <features value="ConfigurableProduct"/> - <stories value="Duplicate Product"/> <title value="Visibility of duplicate product on the Storefront"/> <description value="Check visibility of duplicate product on the Storefront"/> <severity value="AVERAGE"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml index 7e0cd323f1140..89398051fcf67 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/PriceRuleConditionsSection.xml @@ -9,7 +9,6 @@ <element name="conditionsTab" type="text" selector="//div[@data-index='conditions']//span[contains(.,'Conditions')][1]"/> <element name="createNewRule" type="text" selector="span.rule-param.rule-param-new-child"/> <element name="rulesDropdown" type="select" selector="select[data-form-part='sales_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> - <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> <element name="addProductAttributesButton" type="text" selector="#conditions__1--1__children>li>span>a"/> <element name="productAttributesDropdown" type="select" selector="#conditions__1--1__new_child"/> <element name="firstProductAttributeSelected" type="select" selector="#conditions__1__children .rule-param:nth-of-type(2) a:nth-child(1)"/> From 7d4a306702274f3d5f41154f0e57903c5136470b Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Thu, 26 Sep 2019 15:53:09 +0300 Subject: [PATCH 0661/2437] [Wishlist] Remove name from WishlistOutput #920 --- .../Magento/GraphQl/Wishlist/CustomerWishlistsTest.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php index cdc774f08c2cb..2a6c70161a623 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php @@ -67,11 +67,10 @@ public function testGetCustomerWishlists(): void $this->getCustomerAuthHeaders('customer@example.com', 'password') ); - $this->assertEquals($wishlistItem->getItemsCount(), $response['wishlists'][0]['items_count']); - $this->assertEquals($wishlistItem->getSharingCode(), $response['wishlists'][0]['sharing_code']); - $this->assertEquals($wishlistItem->getUpdatedAt(), $response['wishlists'][0]['updated_at']); - $this->assertEquals('simple', $response['wishlists'][0]['items'][0]['product']['sku']); - + $this->assertEquals($wishlistItem->getItemsCount(), $response['customer']['wishlists'][0]['items_count']); + $this->assertEquals($wishlistItem->getSharingCode(), $response['customer']['wishlists'][0]['sharing_code']); + $this->assertEquals($wishlistItem->getUpdatedAt(), $response['customer']['wishlists'][0]['updated_at']); + $this->assertEquals('simple', $response['customer']['wishlists'][0]['items'][0]['product']['sku']); } /** From 72183b7b3ca6bdb93f53ed07b717f8e68b230ea4 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Thu, 26 Sep 2019 16:40:34 +0300 Subject: [PATCH 0662/2437] MAGETWO-67450: The rate in order is duplicated - Fix bug - Add mftf test --- .../AdminCurrencyRatesActionGroup.xml | 22 ++++++ .../StorefrontCurrencyRatesActionGroup.xml | 20 +++++ .../Data/AdminCurrencyRatesMessageData.xml | 14 ++++ .../Test/Mftf/Page/AdminCurrencyRatesPage.xml | 14 ++++ .../Section/AdminCurrencyRatesSection.xml | 17 ++++ .../StorefrontSwitchCurrencyRatesSection.xml | 16 ++++ ...ayWhenChooseThreeAllowedCurrenciesTest.xml | 75 ++++++++++++++++++ .../AdminOrderRateDisplayedInOneLineTest.xml | 77 +++++++++++++++++++ .../AdminOrderDetailsInformationSection.xml | 2 + .../adminhtml/templates/order/view/info.phtml | 3 +- 10 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Data/AdminCurrencyRatesMessageData.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml new file mode 100644 index 0000000000000..6b8a93ef3542d --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetCurrencyRatesActionGroup"> + <arguments> + <argument name="firstCurrency" type="string" defaultValue="USD"/> + <argument name="secondCurrency" type="string" defaultValue="EUR"/> + <argument name="rate" type="string" defaultValue="0.5"/> + </arguments> + <fillField selector="{{AdminCurrencyRatesSection.currencyRate(firstCurrency, secondCurrency)}}" userInput="{{rate}}" stepKey="setCurrencyRate"/> + <click selector="{{AdminCurrencyRatesSection.saveCurrencyRates}}" stepKey="clickSaveCurrencyRates"/> + <waitForPageLoad stepKey="waitForSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="{{AdminSaveCurrencyRatesMessageData.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml new file mode 100644 index 0000000000000..61a6123b1a7af --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSwitchCurrency"> + <arguments> + <argument name="currency" type="string" defaultValue="EUR"/> + </arguments> + <click selector="{{StorefrontSwitchCurrencyRatesSection.currencyTrigger}}" stepKey="openTrigger"/> + <waitForElementVisible selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="waitForCurrency"/> + <click selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="chooseCurrency"/> + <see selector="{{StorefrontSwitchCurrencyRatesSection.selectedCurrency}}" userInput="{{currency}}" stepKey="seeSelectedCurrency"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/AdminCurrencyRatesMessageData.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/AdminCurrencyRatesMessageData.xml new file mode 100644 index 0000000000000..90d22b06fcb80 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/AdminCurrencyRatesMessageData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminSaveCurrencyRatesMessageData"> + <data key="success">All valid rates have been saved.</data> + </entity> +</entities> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml new file mode 100644 index 0000000000000..7cfb177ecec31 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCurrencyRatesPage" url="admin/system_currency/" area="admin" module="Currency"> + <section name="AdminCurrencyRatesSection"/> + </page> +</pages> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml new file mode 100644 index 0000000000000..a5799356eb459 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCurrencyRatesSection"> + <element name="import" type="button" selector="//button[@title='Import']"/> + <element name="saveCurrencyRates" type="button" selector="//button[@title='Save Currency Rates']"/> + <element name="oldRate" type="text" selector="//div[contains(@class, 'admin__field-note') and contains(text(), 'Old rate:')]/strong"/> + <element name="currencyRate" type="input" selector="input[name='rate[{{fistCurrency}}][{{secondCurrency}}]']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml new file mode 100644 index 0000000000000..e69823ad68e0e --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontSwitchCurrencyRatesSection"> + <element name="currencyTrigger" type="select" selector="#switcher-currency-trigger" timeout="30"/> + <element name="currency" type="button" selector="//div[@id='switcher-currency-trigger']/following-sibling::ul//a[contains(text(), '{{currency}}')]" parameterized="true" timeout="10"/> + <element name="selectedCurrency" type="text" selector="#switcher-currency-trigger span"/> + </section> +</sections> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml new file mode 100644 index 0000000000000..823e9d1f15578 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest" extends="AdminOrderRateDisplayedInOneLineTest"> + <annotations> + <features value="CurrencySymbol"/> + <stories value="Currency rates order page"/> + <title value="Order rate converting currency for 'Base Currency' and 'Default Display Currency' displayed correct"/> + <description value="Order rate converting currency for 'Base Currency' and 'Default Display Currency' displayed correct"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17255" /> + <useCaseId value="MAGETWO-67450"/> + <group value="currency"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create product--> + <createData entity="SimpleProduct2" stepKey="createNewProduct"/> + <!--Set Currency options for Website--> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow EUR,USD,RUB" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default EUR" stepKey="setCurrencyDefaultUSDWebsites"/> + </before> + <after> + <!--Delete created product--> + <comment userInput="Delete created product" stepKey="commentDeleteCreatedProduct"/> + <deleteData createDataKey="createNewProduct" stepKey="deleteNewProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Set currency rates--> + <amOnPage url="{{AdminCurrencyRatesPage.url}}" stepKey="gotToCurrencyRatesPageSecondTime"/> + <waitForPageLoad stepKey="waitForLoadRatesPageSecondTime"/> + <actionGroup ref="AdminSetCurrencyRatesActionGroup" stepKey="setCurrencyRates"> + <argument name="firstCurrency" value="USD"/> + <argument name="secondCurrency" value="RUB"/> + <argument name="rate" value="0.8"/> + </actionGroup> + <!--Open created product on Storefront and place for order--> + <amOnPage url="{{StorefrontProductPage.url($$createNewProduct.custom_attributes[url_key]$$)}}" stepKey="goToNewProductPage"/> + <waitForPageLoad stepKey="waitForNewProductPagePageLoad"/> + <actionGroup ref="StorefrontSwitchCurrency" stepKey="switchCurrency"> + <argument name="currency" value="RUB"/> + </actionGroup> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontNewProductPage"> + <argument name="productName" value="$$createNewProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutNewProductFromMinicart" /> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutNewFillingShippingSection"> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectNewCheckMoneyOrderPayment" /> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceNewOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabNewOrderNumber"/> + <!--Open order and check rates display in one line--> + <actionGroup ref="OpenOrderById" stepKey="openNewOrderById"> + <argument name="orderId" value="$grabNewOrderNumber"/> + </actionGroup> + <see selector="{{AdminOrderDetailsInformationSection.orderInformationTable}}" userInput="EUR / USD rate" stepKey="seeUSDandEURRate"/> + <see selector="{{AdminOrderDetailsInformationSection.orderInformationTable}}" userInput="RUB / USD rate:" stepKey="seeRUBandEURRate"/> + <grabMultiple selector="{{AdminOrderDetailsInformationSection.rate}}" stepKey="grabRates" /> + <assertEquals stepKey="assertRates"> + <actualResult type="variable">grabRates</actualResult> + <expectedResult type="array">['EUR / USD rate:', 'RUB / USD rate:']</expectedResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml new file mode 100644 index 0000000000000..1d96fa1de6119 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminOrderRateDisplayedInOneLineTest"> + <annotations> + <features value="CurrencySymbol"/> + <stories value="Currency rates order page"/> + <title value="Order rate converting currency for 'Base Currency' and 'Default Display Currency' displayed correct once"/> + <description value="Order rate converting currency for 'Base Currency' and 'Default Display Currency' displayed correct once"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17255" /> + <useCaseId value="MAGETWO-67450"/> + <group value="currency"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create product--> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + <!--Set price scope website--> + <magentoCLI command="config:set catalog/price/scope 1" stepKey="setCatalogPriceScopeWebsite"/> + <!--Set Currency options for Default Config--> + <magentoCLI command="config:set currency/options/base EUR" stepKey="setCurrencyBaseEUR"/> + <magentoCLI command="config:set currency/options/allow EUR,USD" stepKey="setAllowedCurrencyEUR"/> + <magentoCLI command="config:set currency/options/default EUR" stepKey="setCurrencyDefaultEUR"/> + <!--Set Currency options for Website--> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseEURWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow EUR,USD" stepKey="setAllowedCurrencyEURWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default EUR" stepKey="setCurrencyDefaultEURWebsites"/> + </before> + <after> + <!--Delete created product--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!--Reset configurations--> + <magentoCLI command="config:set catalog/price/scope 0" stepKey="setCatalogPriceScopeGlobal"/> + <magentoCLI command="config:set currency/options/base USD" stepKey="setCurrencyBaseUSD"/> + <magentoCLI command="config:set currency/options/default USD" stepKey="setCurrencyDefaultUSD"/> + <magentoCLI command="config:set currency/options/allow USD" stepKey="setAllowedCurrencyUSD"/> + <!--Set Currency options for Website--> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default USD" stepKey="setCurrencyDefaultUSDWebsites"/> + <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow USD" stepKey="setAllowedCurrencyUSDWebsites"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open created product on Storefront and place for order--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPagePageLoad"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + </actionGroup> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="guestSelectCheckMoneyOrderPayment" /> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="guestPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage" /> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + <!--Open order and check rates display in one line--> + <actionGroup ref="OpenOrderById" stepKey="openOrderById"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <see selector="{{AdminOrderDetailsInformationSection.orderInformationTable}}" userInput="EUR / USD rate" stepKey="seeEURandUSDRate"/> + <grabMultiple selector="{{AdminOrderDetailsInformationSection.rate}}" stepKey="grabRate" /> + <assertEquals stepKey="assertSelectedCategories"> + <actualResult type="variable">grabRate</actualResult> + <expectedResult type="array">[EUR / USD rate:]</expectedResult> + </assertEquals> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml index 71a96dc109385..653b1d48686e3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsInformationSection.xml @@ -13,6 +13,8 @@ <element name="orderStatus" type="text" selector=".order-information table.order-information-table #order_status"/> <element name="purchasedFrom" type="text" selector=".order-information table.order-information-table tr:last-of-type > td"/> <element name="accountInformation" type="text" selector=".order-account-information-table"/> + <element name="orderInformationTable" type="block" selector=".order-information-table"/> + <element name="rate" type="text" selector="//table[contains(@class, 'order-information-table')]//th[contains(text(), 'rate:')]"/> <element name="customerName" type="text" selector=".order-account-information table tr:first-of-type > td span"/> <element name="customerEmail" type="text" selector=".order-account-information table tr:nth-of-type(2) > td a"/> <element name="customerGroup" type="text" selector=".order-account-information table tr:nth-of-type(3) > td"/> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml index ab5cd49449ece..8ada10450c967 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml @@ -23,6 +23,7 @@ $orderStoreDate = $block->formatDate( ); $customerUrl = $block->getCustomerViewUrl(); + $allowedAddressHtmlTags = ['b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub', 'sup', 'ul']; ?> @@ -99,7 +100,7 @@ $allowedAddressHtmlTags = ['b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub <td><?= $block->escapeHtml($order->getBaseToGlobalRate()) ?></td> </tr> <?php endif; ?> - <?php if ($order->getBaseCurrencyCode() != $order->getOrderCurrencyCode()) : ?> + <?php if ($order->getBaseCurrencyCode() != $order->getOrderCurrencyCode() && $order->getGlobalCurrencyCode() != $order->getOrderCurrencyCode()) : ?> <tr> <th><?= $block->escapeHtml(__('%1 / %2 rate:', $order->getOrderCurrencyCode(), $order->getBaseCurrencyCode())) ?></th> <td><?= $block->escapeHtml($order->getBaseToOrderRate()) ?></td> From 8261058252bbd4ea1cb424342d1a1cd7bb3d6831 Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Thu, 26 Sep 2019 12:17:42 -0300 Subject: [PATCH 0663/2437] set customer contryid and vatid to address --- .../Observer/Frontend/Quote/Address/CollectTotalsObserver.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index 77dfec9603a5c..f3edce45d5335 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -111,6 +111,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) if (empty($customerCountryCode) && empty($customerVatNumber) && $customer->getDefaultShipping()) { $customerAddress = $this->addressRepository->getById($customer->getDefaultShipping()); + $address->setCountryId($customerAddress->getCountryId()); + $address->setVatId($customerAddress->getVatId()); $customerCountryCode = $customerAddress->getCountryId(); $customerVatNumber = $customerAddress->getVatId(); } From 357b449e3d9f6a522802561cb46c7610d5e6ac9b Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Thu, 26 Sep 2019 13:04:21 -0300 Subject: [PATCH 0664/2437] changes to unit tests --- .../Frontend/Quote/Address/CollectTotalsObserver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index f3edce45d5335..a94157b46b88b 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -111,10 +111,11 @@ public function execute(\Magento\Framework\Event\Observer $observer) if (empty($customerCountryCode) && empty($customerVatNumber) && $customer->getDefaultShipping()) { $customerAddress = $this->addressRepository->getById($customer->getDefaultShipping()); - $address->setCountryId($customerAddress->getCountryId()); - $address->setVatId($customerAddress->getVatId()); $customerCountryCode = $customerAddress->getCountryId(); $customerVatNumber = $customerAddress->getVatId(); + $address->setCountryId($customerCountryCode); + $address->setVatId($customerVatNumber); + } $groupId = null; From 024d7c54ad0b901b1dada878814bd463c5ca67ba Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Thu, 26 Sep 2019 11:06:37 -0500 Subject: [PATCH 0665/2437] MC-19451: Adapt functional tests to Elasticsearch * Reverting default search engine config --- app/code/Magento/CatalogSearch/etc/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index 911d9da2fa5e6..7ea15c6caa590 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -12,7 +12,7 @@ <search_terms>1</search_terms> </seo> <search> - <engine>elasticsearch6</engine> + <engine>mysql</engine> <min_query_length>3</min_query_length> <max_query_length>128</max_query_length> <max_count_cacheable_search_terms>100</max_count_cacheable_search_terms> From 5d4452531f0ddd6ef973220447220f61a6d44378 Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Thu, 26 Sep 2019 14:45:12 -0300 Subject: [PATCH 0666/2437] adjust unnecessary new line --- .../Observer/Frontend/Quote/Address/CollectTotalsObserver.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index a94157b46b88b..a1228903e2323 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -115,7 +115,6 @@ public function execute(\Magento\Framework\Event\Observer $observer) $customerVatNumber = $customerAddress->getVatId(); $address->setCountryId($customerCountryCode); $address->setVatId($customerVatNumber); - } $groupId = null; From d657e00c8bc70aa073f074d8a72f960e45cd486f Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Thu, 26 Sep 2019 13:21:24 -0500 Subject: [PATCH 0667/2437] MC-20336: Update tests related to Catalog Search and Swatches --- .../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 4 ++++ var/.htaccess | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 var/.htaccess diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml index 210b474af2e02..c8f84c732d6ba 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml @@ -57,6 +57,10 @@ <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> <waitForPageLoad stepKey="waitForSimpleProductSaved2"/> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Navigate to category on Storefront--> <comment userInput="Navigate to category on Storefront" stepKey="comment3"/> <amOnPage url="{{StorefrontCategoryPage.url($$subCategory.name$$)}}" stepKey="goToCategoryStorefront"/> diff --git a/var/.htaccess b/var/.htaccess deleted file mode 100644 index 707c26b075e16..0000000000000 --- a/var/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ -<IfVersion < 2.4> - order allow,deny - deny from all -</IfVersion> -<IfVersion >= 2.4> - Require all denied -</IfVersion> - From 50cd54614e0de959fbc9d94eb93cf9f9a5e90fce Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Thu, 26 Sep 2019 15:02:51 -0500 Subject: [PATCH 0668/2437] MC-20333: Fix All Catalog Module MFTF tests that are failing with the use of Elastic Search functionality "Add re-index task as few tests were failing in the mainline branch" "Reverted MAGETWO-93794 changes as there is a bug created for https://jira.corp.magento.com/browse/MC-17380 where sorting by position is not working with Elastic Search feature." --- .../ProductAvailableAfterEnablingSubCategoriesTest.xml | 4 ++++ .../Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml index 461ebde29fcad..c8a7cdee66b53 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAvailableAfterEnablingSubCategoriesTest.xml @@ -49,6 +49,10 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryWithProducts"/> <waitForPageLoad stepKey="waitForCategorySaved"/> <see userInput="You saved the category." stepKey="seeSuccessMessage"/> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryStorefront"/> <waitForPageLoad stepKey="waitForCategoryStorefrontPage"/> <seeElement selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct.name$$)}}" stepKey="seeCreatedProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index f2c3f15ab4343..be5d9dca425a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -41,6 +41,9 @@ </actionGroup> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Check product in category listing--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="goToCategoryPage"/> <seeElement selector="{{StorefrontCategoryProductSection.ProductImageByNameAndSrc(SimpleProductNameWithDoubleQuote.name, ProductImage.fileName)}}" stepKey="seeCorrectImageCategoryPage"/> @@ -88,6 +91,9 @@ <deleteData createDataKey="createCategoryOne" stepKey="deleteCategory"/> </after> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Check product in category listing--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategoryOne.name$$)}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitforCategoryPageToLoad"/> @@ -111,7 +117,7 @@ <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('2')}}" stepKey="goToProduct2DisplayPage"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="goToProduct2DisplayPage"/> <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/>--> <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> From c5e08c86766f206845bca6cc3e1ebb26b9e90865 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Thu, 26 Sep 2019 16:43:55 -0500 Subject: [PATCH 0669/2437] MC-20336: Update tests related to Catalog Search and Swatches "Fixed tests that are failing using elastic search engine." --- .../Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml | 3 +++ .../Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml | 3 +++ .../Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml | 3 +++ .../Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml index 470421776cf8f..1bcdd6fcf9a3a 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml @@ -29,6 +29,9 @@ <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('3')}}" userInput="123456789012345678901" stepKey="fillSwatch3" after="clickAddSwatch3"/> <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('3')}}" userInput="123456789012345678901BrownD" stepKey="fillDescription3" after="fillSwatch3"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <see selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '3')}}" userInput="123456789012345678901" stepKey="seeGreen" after="seeBlue"/> <see selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '4')}}" userInput="123456789012345678901" stepKey="seeBrown" after="seeGreen"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index b1ae06428c0ab..b213537ca1ab9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -103,6 +103,9 @@ <argument name="image" value="TestImageAdobe"/> </actionGroup> + <!-- Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!-- Go to the category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPage"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml index 28df5ffd53436..85da0e34ef410 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -82,6 +82,9 @@ <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!-- Go to the category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPage"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index d12cb0433fed1..34d440dc54219 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -94,6 +94,9 @@ <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> + <!-- Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!-- Go to the category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPage"/> From 31875feda09ec5b2edb96e7c07d05c9cf807d8b1 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 26 Sep 2019 17:02:10 -0500 Subject: [PATCH 0670/2437] MC-20334: Update tests related to SKU field issue in AdvancedSearch --- .../Bundle/Test/Mftf/Data/ProductData.xml | 14 +++ .../AdvanceCatalogSearchBundleProductTest.xml | 2 +- ...CatalogSearchBundleBySkuWithHyphenTest.xml | 47 ++++++++++ .../Catalog/Test/Mftf/Data/ProductData.xml | 28 ++++++ .../AdvanceCatalogSearchSimpleProductTest.xml | 2 +- ...AdvanceCatalogSearchVirtualProductTest.xml | 2 +- ...SearchSimpleProductBySkuWithHyphenTest.xml | 29 +++++++ ...earchVirtualProductBySkuWithHyphenTest.xml | 26 ++++++ .../Mftf/Data/ConfigurableProductData.xml | 13 +++ .../AdvanceCatalogSearchConfigurableTest.xml | 2 +- ...gSearchConfigurableBySkuWithHyphenTest.xml | 86 +++++++++++++++++++ .../Test/Mftf/Data/ProductData.xml | 15 ++++ ...ceCatalogSearchDownloadableProductTest.xml | 2 +- ...gSearchDownloadableBySkuWithHyphenTest.xml | 32 +++++++ .../Test/Mftf/Data/GroupedProductData.xml | 11 +++ ...AdvanceCatalogSearchGroupedProductTest.xml | 2 +- ...logSearchGroupedProductBySkuWithHyphenTest | 42 +++++++++ 17 files changed, 349 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchSimpleProductBySkuWithHyphenTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchVirtualProductBySkuWithHyphenTest.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml index 6e7e4a7a16573..e5f557dd22ded 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Data/ProductData.xml @@ -61,6 +61,20 @@ <requiredEntity type="custom_attribute">CustomAttributeDynamicPrice</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributePriceView</requiredEntity> </entity> + <entity name="ApiBundleProductUnderscoredSku" type="product2"> + <data key="name" unique="suffix">Api Bundle Product</data> + <data key="sku" unique="suffix">api_bundle_product</data> + <data key="type_id">bundle</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="urlKey" unique="suffix">api-bundle-product</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute">ApiProductShortDescription</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributeDynamicPrice</requiredEntity> + <requiredEntity type="custom_attribute">CustomAttributePriceView</requiredEntity> + </entity> <entity name="ApiBundleProductPriceViewRange" type="product2"> <data key="name" unique="suffix">Api Bundle Product</data> <data key="sku" unique="suffix">api-bundle-product</data> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml index 52bce67600888..c6775641fe214 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdvanceCatalogSearchBundleProductTest.xml @@ -56,7 +56,7 @@ <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="ApiBundleProductUnderscoredSku" stepKey="product"/> <createData entity="DropDownBundleOption" stepKey="bundleOption"> <requiredEntity createDataKey="product"/> </createData> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml new file mode 100644 index 0000000000000..d8d6034cd1a21 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchBundleBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Bundle"/> + <stories value="Advanced Catalog Product Search for all product types "/> + <title value="Guest customer should be able to advance search Bundle product with product sku that contains hyphen"/> + <description value="Guest customer should be able to advance search Bundle product with product sku that contains hyphen"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20359"/> + <group value="Bundle"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiBundleProduct" stepKey="product"/> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="simple2"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index e122615eb8aa4..ec3bf326b1240 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -438,6 +438,20 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_simple_product</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Simple Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-simple-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="_newDefaultProduct" type="product"> <data key="sku" unique="suffix">testSku</data> <data key="type_id">simple</data> @@ -531,6 +545,20 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiVirtualProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_virtual_product</data> + <data key="type_id">virtual</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Virtual Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="SimpleProductWithNewFromDate" type="product"> <data key="sku" unique="suffix">SimpleProduct</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml index a4c8b492d9d84..867f097042a17 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml @@ -36,7 +36,7 @@ <group value="Catalog"/> </annotations> <before> - <createData entity="ApiProductWithDescription" stepKey="product"/> + <createData entity="ApiProductWithDescriptionAndUnderscoredSku" stepKey="product"/> </before> <after> <deleteData createDataKey="product" stepKey="delete"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml index 84c3f81ef6dbf..07b802637b2e7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchVirtualProductTest.xml @@ -33,7 +33,7 @@ <group value="Catalog"/> </annotations> <before> - <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + <createData entity="ApiVirtualProductWithDescriptionAndUnderscoredSku" stepKey="product"/> </before> </test> <test name="AdvanceCatalogSearchVirtualProductByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchSimpleProductBySkuWithHyphenTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchSimpleProductBySkuWithHyphenTest.xml new file mode 100644 index 0000000000000..3b1cd7ff02e6a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchSimpleProductBySkuWithHyphenTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchSimpleProductBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search simple product with product sku that contains hyphen"/> + <description value="Guest customer should be able to advance search simple product with product sku that contains hyphen"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20361"/> + <group value="Catalog"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="product"/> + </before> + <after> + <deleteData createDataKey="product" stepKey="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchVirtualProductBySkuWithHyphenTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchVirtualProductBySkuWithHyphenTest.xml new file mode 100644 index 0000000000000..d6b3a060ffd3a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontAdvanceCatalogSearchVirtualProductBySkuWithHyphenTest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchVirtualProductBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Catalog"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search virtual product with product sku that contains hyphen"/> + <description value="Guest customer should be able to advance search virtual product with product sku that contains hyphen"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20385"/> + <group value="Catalog"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiVirtualProductWithDescription" stepKey="product"/> + </before> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index 3f21c98068d8a..6ae3c4f4e16cf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -60,6 +60,19 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiConfigurableProductWithDescriptionUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_configurable_product</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">API Configurable Product</data> + <data key="urlKey" unique="suffix">api-configurable-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="ConfigurableProductAddChild" type="ConfigurableProductAddChild"> <var key="sku" entityKey="sku" entityType="product" /> <var key="childSku" entityKey="sku" entityType="product2"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml index c303e4d19db81..0d1925ef538af 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml @@ -104,7 +104,7 @@ </createData> <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> - <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + <createData entity="ApiConfigurableProductWithDescriptionUnderscoredSku" stepKey="product"/> <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml new file mode 100644 index 0000000000000..8a7d7d5394ee6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search configurable product with product sku that contains hyphen"/> + <description value="Guest customer should be able to advance search configurable product with product sku that contains hyphen"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20389"/> + <group value="ConfigurableProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="categoryHandle" before="simple1Handle"/> + + <createData entity="SimpleProduct" stepKey="simple1Handle" before="simple2Handle"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <createData entity="SimpleProduct" stepKey="simple2Handle" before="product"> + <requiredEntity createDataKey="categoryHandle"/> + </createData> + + <!-- TODO: Move configurable product creation to an actionGroup when MQE-697 is fixed --> + <createData entity="ApiConfigurableProductWithDescription" stepKey="product"/> + + <createData entity="productDropDownAttribute" stepKey="productAttributeHandle"/> + + <createData entity="productAttributeOption1" stepKey="productAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + <createData entity="productAttributeOption2" stepKey="productAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addToAttributeSetHandle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getAttributeOption1Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getAttributeOption2Handle"> + <requiredEntity createDataKey="productAttributeHandle"/> + </getData> + + <createData entity="SimpleOne" stepKey="childProductHandle1"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + </createData> + <createData entity="SimpleOne" stepKey="childProductHandle2"> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="configProductOptionHandle"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="productAttributeHandle"/> + <requiredEntity createDataKey="getAttributeOption1Handle"/> + <requiredEntity createDataKey="getAttributeOption2Handle"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle1"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="configProductHandle2"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="childProductHandle2"/> + </createData> + </before> + <after> + <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml index 1a6be43b38d2c..2986532ef1138 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/ProductData.xml @@ -74,6 +74,21 @@ <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> <requiredEntity type="downloadable_link">apiDownloadableLink</requiredEntity> </entity> + <entity name="ApiDownloadableProductUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_downloadable_product</data> + <data key="type_id">downloadable</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Downloadable Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-downloadable-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + <requiredEntity type="downloadable_link">apiDownloadableLink</requiredEntity> + </entity> <entity name="DownloadableProductWithTwoLink100" type="product"> <data key="sku" unique="suffix">downloadableproduct</data> <data key="type_id">downloadable</data> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml index 66177b6875dd9..39260b897ee19 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdvanceCatalogSearchDownloadableProductTest.xml @@ -39,7 +39,7 @@ <group value="Downloadable"/> </annotations> <before> - <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableProductUnderscoredSku" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> </createData> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml new file mode 100644 index 0000000000000..7174122760576 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> + <!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> + + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="Downloadable"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Downloadable product with product sku that contains hyphen"/> + <description value="Guest customer should be able to advance search Downloadable product with product that contains hyphen"/> + <severity value="MAJOR"/> + <testCaseId value="MC-252"/> + <group value="Downloadable"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiDownloadableProduct" stepKey="product"/> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> + <requiredEntity createDataKey="product"/> + </createData> + <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink2"> + <requiredEntity createDataKey="product"/> + </createData> + </before> + </test> + </tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml index ba3703e7b0edc..e6d7588289c39 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Data/GroupedProductData.xml @@ -28,6 +28,17 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiGroupedProductAndUnderscoredSku" type="product3"> + <data key="sku" unique="suffix">api_grouped_product</data> + <data key="type_id">grouped</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">Api Grouped Product</data> + <data key="status">1</data> + <data key="urlKey" unique="suffix">api-grouped-product</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="ApiGroupedProduct2" type="product3"> <data key="sku" unique="suffix">apiGroupedProduct</data> <data key="type_id">grouped</data> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml index 2a600d38250f8..8b2daa1570771 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdvanceCatalogSearchGroupedProductTest.xml @@ -51,7 +51,7 @@ <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> - <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="ApiGroupedProductAndUnderscoredSku" stepKey="product"/> <createData entity="OneSimpleProductLink" stepKey="addProductOne"> <requiredEntity createDataKey="product"/> <requiredEntity createDataKey="simple1"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest b/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest new file mode 100644 index 0000000000000..5220349a4aac3 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> + <annotations> + <features value="GroupedProduct"/> + <stories value="Advanced Catalog Product Search for all product types"/> + <title value="Guest customer should be able to advance search Grouped product with product sku that in camelCase format"/> + <description value="Guest customer should be able to advance search Grouped product with product sku that in camelCase format"/> + <severity value="MAJOR"/> + <testCaseId value="MC-20519"/> + <group value="GroupedProduct"/> + <group value="SearchEngineMysql"/> + </annotations> + <before> + <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> + <createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/> + <createData entity="ApiGroupedProduct" stepKey="product"/> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple1"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="product"/> + <requiredEntity createDataKey="simple2"/> + </updateData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/> + <deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/> + </after> + </test> +</tests> From 1e6023399c47ed1c251ea045a920296081bad090 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Thu, 26 Sep 2019 21:33:38 -0500 Subject: [PATCH 0671/2437] MC-20336: Update tests related to Catalog Search and Swatches "Reverted previous commit "d657e00c8bc70aa073f074d8a72f960e45cd486f" as it deleted var/.htaccess file and added only changes to Test file" --- .../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 4 ---- var/.htaccess | 8 ++++++++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 var/.htaccess diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml index c8f84c732d6ba..210b474af2e02 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml @@ -57,10 +57,6 @@ <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> <waitForPageLoad stepKey="waitForSimpleProductSaved2"/> - - <!--Run re-index task--> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <!--Navigate to category on Storefront--> <comment userInput="Navigate to category on Storefront" stepKey="comment3"/> <amOnPage url="{{StorefrontCategoryPage.url($$subCategory.name$$)}}" stepKey="goToCategoryStorefront"/> diff --git a/var/.htaccess b/var/.htaccess new file mode 100644 index 0000000000000..707c26b075e16 --- /dev/null +++ b/var/.htaccess @@ -0,0 +1,8 @@ +<IfVersion < 2.4> + order allow,deny + deny from all +</IfVersion> +<IfVersion >= 2.4> + Require all denied +</IfVersion> + From 4e4320f5fa662ee23580a036830094388da6c830 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Thu, 26 Sep 2019 21:36:25 -0500 Subject: [PATCH 0672/2437] MC-20336: Update tests related to Catalog Search and Swatches "Reverted previous commit "d657e00c8bc70aa073f074d8a72f960e45cd486f" as it accidentally deleted var/.htaccess file and made now changes only to Test file" --- .../Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml index 210b474af2e02..c8f84c732d6ba 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml @@ -57,6 +57,10 @@ <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton2"/> <waitForPageLoad stepKey="waitForSimpleProductSaved2"/> + + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Navigate to category on Storefront--> <comment userInput="Navigate to category on Storefront" stepKey="comment3"/> <amOnPage url="{{StorefrontCategoryPage.url($$subCategory.name$$)}}" stepKey="goToCategoryStorefront"/> From 038f134d3070d9f6b26da3000a3957e077c6e6d4 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 27 Sep 2019 10:08:53 +0400 Subject: [PATCH 0673/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6411 --- .../AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml | 2 +- .../tests/_data/{test_tablerates.csv => usa_tablerates.csv} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename dev/tests/acceptance/tests/_data/{test_tablerates.csv => usa_tablerates.csv} (100%) diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index 6f9327d68d451..3decca9a971d1 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -61,7 +61,7 @@ <argument name="status" value="1"/> </actionGroup> <actionGroup ref="AdminImportFileTableRatesShippingMethodActionGroup" stepKey="importCSVFile"> - <argument name="file" value="test_tablerates.csv"/> + <argument name="file" value="usa_tablerates.csv"/> </actionGroup> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> diff --git a/dev/tests/acceptance/tests/_data/test_tablerates.csv b/dev/tests/acceptance/tests/_data/usa_tablerates.csv similarity index 100% rename from dev/tests/acceptance/tests/_data/test_tablerates.csv rename to dev/tests/acceptance/tests/_data/usa_tablerates.csv From acc65002f2b14daf6052bdb34d22ed3815bb6507 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 27 Sep 2019 10:30:09 +0400 Subject: [PATCH 0674/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6405 --- app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 4 ++-- app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml | 2 +- app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 756574b76d4e8..cfcb563fb67cf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -312,7 +312,7 @@ <data key="postcode">90230</data> <data key="telephone">555-55-555-55</data> </entity> - <entity name="US_Address_AFE" type="address"> + <entity name="US_Address_AE" type="address"> <data key="firstname">John</data> <data key="lastname">Doe</data> <data key="company">Magento</data> @@ -328,6 +328,6 @@ <data key="telephone">512-345-6789</data> <data key="default_billing">Yes</data> <data key="default_shipping">Yes</data> - <requiredEntity type="region">RegionAFE</requiredEntity> + <requiredEntity type="region">RegionAE</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index f10732c559038..89dc8c7e100f1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -320,6 +320,6 @@ <data key="password">pwdTest123!</data> <data key="store_id">0</data> <data key="website_id">0</data> - <requiredEntity type="address">US_Address_AFE</requiredEntity> + <requiredEntity type="address">US_Address_AE</requiredEntity> </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml index 1e6589057fa7f..0a956f16767be 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/RegionData.xml @@ -32,7 +32,7 @@ <data key="region_code">UT</data> <data key="region_id">58</data> </entity> - <entity name="RegionAFE" type="region"> + <entity name="RegionAE" type="region"> <data key="region">Armed Forces Europe</data> <data key="region_code">AFE</data> <data key="region_id">9</data> From 62ac658d22f4abc4c73803a68edccc06876e9869 Mon Sep 17 00:00:00 2001 From: George Babarus <george.babarus@emag.ro> Date: Fri, 27 Sep 2019 09:32:41 +0300 Subject: [PATCH 0675/2437] replace DIRECTORY_SEPARATOR with / --- lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php index 9c5cd2fe12324..a53ea9423d449 100644 --- a/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php +++ b/lib/internal/Magento/Framework/App/DeploymentConfig/Reader.php @@ -108,7 +108,7 @@ public function load($fileKey = null) } else { $configFiles = $this->getFiles(); foreach ($configFiles as $file) { - $configFile = $path . DIRECTORY_SEPARATOR . $file; + $configFile = $path . '/' . $file; if ($fileDriver->isExists($configFile)) { $fileData = include $configFile; if (!is_array($fileData)) { From 39c9c80c589e86f8d1586400b7166d5149f495fb Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Fri, 27 Sep 2019 09:45:53 +0300 Subject: [PATCH 0676/2437] graphQl-961: ShippingAddressInput.postcode: String, is not required by Schema --- .../Model/Cart/QuoteAddressFactory.php | 4 +++ .../Model/Cart/SetBillingAddressOnCart.php | 27 ++++++++++++++++ .../Model/Cart/SetShippingAddressesOnCart.php | 26 +++++++++++++++ .../Customer/SetBillingAddressOnCartTest.php | 32 ++++++++++++++----- .../Customer/SetShippingAddressOnCartTest.php | 30 +++++++++++++---- 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php index afc88f026ed62..52f5387f15785 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -66,6 +66,10 @@ public function createBasedOnInputData(array $addressInput): QuoteAddress $addressInput['country_id'] = $addressInput['country_code']; } + if (isset($addressInput['region'])) { + $addressInput['region_code'] = $addressInput['region']; + } + $maxAllowedLineCount = $this->addressHelper->getStreetLines(); if (is_array($addressInput['street']) && count($addressInput['street']) > $maxAllowedLineCount) { throw new GraphQlInputException( diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 673debefd0874..cf8e38ebbfcc6 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -111,6 +111,33 @@ private function createBillingAddress( (int)$context->getUserId() ); } + + $errors = $billingAddress->validate(); + + if (true !== $errors) { + throw new GraphQlInputException( + __('Shipping address error: %message', ['message' => $this->getAddressErrors($errors)]) + ); + } + return $billingAddress; } + + /** + * Collecting errors. + * + * @param array $errors + * @return string + */ + private function getAddressErrors(array $errors): string + { + $errorMessages = []; + + /** @var \Magento\Framework\Phrase $error */ + foreach ($errors as $error) { + $errorMessages[] = $error->render(); + } + + return implode(PHP_EOL, $errorMessages); + } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 260f1343556f0..9e39992eed830 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -82,6 +82,32 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } + $errors = $shippingAddress->validate(); + + if (true !== $errors) { + throw new GraphQlInputException( + __('Shipping address error: %message', ['message' => $this->getAddressErrors($errors)]) + ); + } + $this->assignShippingAddressToCart->execute($cart, $shippingAddress); } + + /** + * Collecting errors. + * + * @param array $errors + * @return string + */ + private function getAddressErrors(array $errors): string + { + $errorMessages = []; + + /** @var \Magento\Framework\Phrase $error */ + foreach ($errors as $error) { + $errorMessages[] = $error->render(); + } + + return implode(PHP_EOL, $errorMessages); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index 011930e723273..29109f89352ff 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -77,7 +77,7 @@ public function testSetNewBillingAddress() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -136,7 +136,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -297,7 +297,7 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -379,7 +379,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -573,7 +573,7 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st QUERY; $this->expectExceptionMessage($message); - $this->graphQlMutation($query); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -590,7 +590,23 @@ public function dataProviderSetWithoutRequiredParameters(): array 'missed_cart_id' => [ 'billing_address: {}', 'Required parameter "cart_id" is missing' - ] + ], + 'missed_region' => [ + 'cart_id: "cart_id_value" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + }', + '"regionId" is required. Enter and try again.' + ], ]; } @@ -616,7 +632,7 @@ public function testSetNewBillingAddressWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -659,7 +675,7 @@ public function testSetBillingAddressWithLowerCaseCountry() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "us" telephone: "88776655" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index e74b7c41b3983..fd21475f12504 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -78,7 +78,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -144,7 +144,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -272,7 +272,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -424,7 +424,23 @@ public function dataProviderUpdateWithMissedRequiredParameters(): array 'missed_cart_id' => [ 'shipping_addresses: {}', 'Required parameter "cart_id" is missing' - ] + ], + 'missed_region' => [ + 'cart_id: "cart_id_value" + shipping_addresses: [{ + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + }]', + '"regionId" is required. Enter and try again.' + ], ]; } @@ -454,7 +470,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -468,7 +484,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company 2" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -512,7 +528,7 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" From 5dd4a1085b02c25d061b3d0ab85783823de068cf Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Fri, 27 Sep 2019 09:48:13 +0300 Subject: [PATCH 0677/2437] graphQl-903: added description to the same_as_shipping field --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index ae0a1bc34866a..e97582b9ae52d 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -97,7 +97,7 @@ input BillingAddressInput { customer_address_id: Int address: CartAddressInput use_for_shipping: Boolean @doc(description: "Deprecated. Use same_as_shipping") - same_as_shipping: Boolean + same_as_shipping: Boolean @doc(description: "Set billing address same as shipping") } input CartAddressInput { From 863d178be4baf88fffc7b252c0a92f6edb0410e5 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Fri, 27 Sep 2019 10:57:22 +0300 Subject: [PATCH 0678/2437] graphQl-890: fixed deprecation messages --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 2edb3f1e196ab..cd72e90344c6f 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -220,7 +220,7 @@ type ShippingCartAddress implements CartAddressInterface { available_shipping_methods: [AvailableShippingMethod] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\AvailableShippingMethods") selected_shipping_method: SelectedShippingMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\SelectedShippingMethod") customer_notes: String - items_weight: Float @deprecated + items_weight: Float @deprecated(reason: "This information shoud not be exposed on frontend") cart_items: [CartItemInterface] } @@ -228,7 +228,7 @@ type BillingCartAddress implements CartAddressInterface { customer_notes: String @deprecated (reason: "The field is used only in shipping address") } -type CartItemQuantity @deprecated(reason: "Use CartItemInterface instead") { +type CartItemQuantity @deprecated(reason: "All fields in CartItemQuantity should be deprecated (so this type can be completely removed in the future releases)") { cart_item_id: Int! quantity: Float! } From aeb0175397d78221372cc21d8db74168afb77c5f Mon Sep 17 00:00:00 2001 From: Vitaliy <v.boyko@atwix.com> Date: Fri, 27 Sep 2019 13:19:01 +0300 Subject: [PATCH 0679/2437] graphQl-903: Update app/code/Magento/QuoteGraphQl/etc/schema.graphqls Co-Authored-By: Lena Orobei <oorobei@magento.com> --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index e97582b9ae52d..1c60f18c5bc26 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -96,7 +96,7 @@ input SetBillingAddressOnCartInput { input BillingAddressInput { customer_address_id: Int address: CartAddressInput - use_for_shipping: Boolean @doc(description: "Deprecated. Use same_as_shipping") + use_for_shipping: Boolean @doc(description: "Deprecated: use `same_as_shipping` field instead") same_as_shipping: Boolean @doc(description: "Set billing address same as shipping") } From 4c85d8439053b9df7644f3e86105b4d5b2570522 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 27 Sep 2019 13:43:20 +0300 Subject: [PATCH 0680/2437] [Wishlist] Remove name from WishlistOutput #920 --- .../WishlistGraphQl/etc/schema.graphqls | 10 +++--- .../Wishlist/CustomerWishlistsTest.php | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index b7cc60fe100c6..28d80c4a21884 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -10,11 +10,11 @@ type Customer { } type WishlistOutput @doc(description: "Deprecated: 'Wishlist' type should be used instead") { - items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "Deprecated: use field `items` from type `Wishlist`"), - items_count: Int @doc(description: "Deprecated: use field `items_count` from type `Wishlist`"), - name: String @doc(description: "Deprecated."), - sharing_code: String @doc(description: "Deprecated: use field `sharing_code` from type `Wishlist`"), - updated_at: String @doc(description: "Deprecated: use field `updated_at` from type `Wishlist`") + items: [WishlistItem] @deprecated(reason: "Use field `items` from type `Wishlist` instead") @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"), + items_count: Int @deprecated(reason: "Use field `items_count` from type `Wishlist` instead") @doc(description: "The number of items in the wish list"), + name: String @deprecated(reason: "This field is related to Commerce functionality and is always null in Open source edition") @doc(description: "When multiple wish lists are enabled, the name the customer assigns to the wishlist"), + sharing_code: String @deprecated(reason: "Use field `sharing_code` from type `Wishlist` instead") @doc(description: "An encrypted code that Magento uses to link to the wish list"), + updated_at: String @deprecated(reason: "Use field `updated_at` from type `Wishlist` instead") @doc(description: "The time of the last modification to the wish list") } type Wishlist { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php index 2a6c70161a623..2d6c3ff34b0ab 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php @@ -73,6 +73,37 @@ public function testGetCustomerWishlists(): void $this->assertEquals('simple', $response['customer']['wishlists'][0]['items'][0]['product']['sku']); } + public function testCustomerWithoutWishlists(): void + { + $query = + <<<QUERY +{ + customer + { + wishlists { + items_count + sharing_code + updated_at + items { + product { + sku + } + } + } + } +} +QUERY; + + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', 'password') + ); + + $this->assertEquals([], $response['customer']['wishlists']); + } + /** * @expectedException \Exception * @expectedExceptionMessage The current customer isn't authorized. From 3846ddd8b04cdbd027ad8b5625767c3dbbdc4436 Mon Sep 17 00:00:00 2001 From: Vaha <vaha@atwix.com> Date: Fri, 27 Sep 2019 13:48:07 +0300 Subject: [PATCH 0681/2437] magento/magento2# improved getCustomerName method, added prefix, middlename, suffix if configure --- app/code/Magento/Sales/Model/Order.php | 84 +++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 48deddb2fe5ac..062c51bef9c04 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -5,8 +5,11 @@ */ namespace Magento\Sales\Model; +use Magento\Config\Model\Config\Source\Nooptreq; use Magento\Directory\Model\Currency; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; @@ -14,6 +17,7 @@ use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Api\Data\OrderStatusHistoryInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\ProductOption; use Magento\Sales\Model\ResourceModel\Order\Address\Collection; @@ -24,8 +28,7 @@ use Magento\Sales\Model\ResourceModel\Order\Shipment\Collection as ShipmentCollection; use Magento\Sales\Model\ResourceModel\Order\Shipment\Track\Collection as TrackCollection; use Magento\Sales\Model\ResourceModel\Order\Status\History\Collection as HistoryCollection; -use Magento\Sales\Api\OrderItemRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Store\Model\ScopeInterface; /** * Order model @@ -299,6 +302,11 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface */ private $searchCriteriaBuilder; + /** + * @var ScopeConfigInterface; + */ + private $scopeConfig; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -331,6 +339,7 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface * @param ProductOption|null $productOption * @param OrderItemRepositoryInterface $itemRepository * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param ScopeConfigInterface $scopeConfig * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -364,7 +373,8 @@ public function __construct( ResolverInterface $localeResolver = null, ProductOption $productOption = null, OrderItemRepositoryInterface $itemRepository = null, - SearchCriteriaBuilder $searchCriteriaBuilder = null + SearchCriteriaBuilder $searchCriteriaBuilder = null, + ScopeConfigInterface $scopeConfig = null ) { $this->_storeManager = $storeManager; $this->_orderConfig = $orderConfig; @@ -392,6 +402,7 @@ public function __construct( ->get(OrderItemRepositoryInterface::class); $this->searchCriteriaBuilder = $searchCriteriaBuilder ?: ObjectManager::getInstance() ->get(SearchCriteriaBuilder::class); + $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); parent::__construct( $context, @@ -1111,7 +1122,7 @@ public function addStatusHistoryComment($comment, $status = false) { return $this->addCommentToStatusHistory($comment, $status, false); } - + /** * Add a comment to order status history. * @@ -1965,16 +1976,71 @@ public function getRelatedObjects() * * @return string */ - public function getCustomerName() + public function getCustomerName(): string { - if ($this->getCustomerFirstname()) { - $customerName = $this->getCustomerFirstname() . ' ' . $this->getCustomerLastname(); - } else { - $customerName = (string)__('Guest'); + if (null === $this->getCustomerFirstname()) { + return (string)__('Guest'); + } + + $customerName = ''; + if ($this->isVisibleCustomerPrefix() && strlen($this->getCustomerPrefix())) { + $customerName .= $this->getCustomerPrefix() . ' '; + } + $customerName .= $this->getCustomerFirstname(); + if ($this->isVisibleCustomerMiddlename() && strlen($this->getCustomerMiddlename())) { + $customerName .= ' ' . $this->getCustomerMiddlename(); + } + $customerName .= ' ' . $this->getCustomerLastname(); + if ($this->isVisibleCustomerSuffix() && strlen($this->getCustomerSuffix())) { + $customerName .= ' ' . $this->getCustomerSuffix(); } + return $customerName; } + /** + * Is visible customer middlename + * + * @return bool + */ + private function isVisibleCustomerMiddlename(): bool + { + return $this->scopeConfig->isSetFlag( + 'customer/address/middlename_show', + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Is visible customer prefix + * + * @return bool + */ + private function isVisibleCustomerPrefix(): bool + { + $prefixShowValue = $this->scopeConfig->getValue( + 'customer/address/prefix_show', + ScopeInterface::SCOPE_STORE + ); + + return $prefixShowValue !== Nooptreq::VALUE_NO; + } + + /** + * Is visible customer suffix + * + * @return bool + */ + private function isVisibleCustomerSuffix(): bool + { + $prefixShowValue = $this->scopeConfig->getValue( + 'customer/address/suffix_show', + ScopeInterface::SCOPE_STORE + ); + + return $prefixShowValue !== Nooptreq::VALUE_NO; + } + /** * Add New object to related array * From b52fe3e5f67a4e2d670a8599568da8266614b51f Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 27 Sep 2019 14:39:40 +0300 Subject: [PATCH 0682/2437] [Wishlist] Remove name from WishlistOutput #920 --- .../Magento/GraphQl/Wishlist/CustomerWishlistsTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php index 2d6c3ff34b0ab..74b91cfb85209 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistsTest.php @@ -73,6 +73,9 @@ public function testGetCustomerWishlists(): void $this->assertEquals('simple', $response['customer']['wishlists'][0]['items'][0]['product']['sku']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ public function testCustomerWithoutWishlists(): void { $query = From b94085ecc4a6a48c318a7c2465f69cfa44277606 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 27 Sep 2019 15:01:01 +0300 Subject: [PATCH 0683/2437] Magento 2.3.2 - PWA - graphQl fetching Issue for phtml file called in static block #960 --- .../Model/Resolver/DataProvider/Block.php | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php index fa4944381b858..9cbb34a939109 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php @@ -11,6 +11,7 @@ use Magento\Cms\Api\Data\BlockInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Widget\Model\Template\FilterEmulate; +use Magento\Framework\App\State; /** * Cms block data provider @@ -27,16 +28,24 @@ class Block */ private $widgetFilter; + /** + * @var State + */ + private $state; + /** * @param BlockRepositoryInterface $blockRepository * @param FilterEmulate $widgetFilter + * @param State $state */ public function __construct( BlockRepositoryInterface $blockRepository, - FilterEmulate $widgetFilter + FilterEmulate $widgetFilter, + State $state ) { $this->blockRepository = $blockRepository; $this->widgetFilter = $widgetFilter; + $this->state = $state; } /** @@ -56,7 +65,11 @@ public function getData(string $blockIdentifier): array ); } - $renderedContent = $this->widgetFilter->filter($block->getContent()); + $renderedContent = $this->state->emulateAreaCode( + 'frontend', + [$this, 'getRenderedBlockContent'], + [$block->getContent()] + ); $blockData = [ BlockInterface::BLOCK_ID => $block->getId(), @@ -66,4 +79,14 @@ public function getData(string $blockIdentifier): array ]; return $blockData; } + + /** + * @param string $blockContent + * + * @return string + */ + public function getRenderedBlockContent(string $blockContent) : string + { + return $this->widgetFilter->filter($blockContent); + } } From 5b0b948319eaf4a64dfd5217c9076ca8d88c311d Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Fri, 27 Sep 2019 14:45:12 +0300 Subject: [PATCH 0684/2437] MC-18822: Increase test coverage for Content functional area - MC-11441 should be covered by Integration test --- .../Newsletter/Controller/SubscriberTest.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php index 35f9cb4a5a11c..b7b87d3b9e20d 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php @@ -8,10 +8,13 @@ namespace Magento\Newsletter\Controller; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\AccountConfirmation; +use Magento\Framework\App\Config\MutableScopeConfigInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\Data\Form\FormKey; use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberLoader; use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\AbstractController; @@ -76,10 +79,17 @@ public function testNewActionOwnerEmail() /** * Check that Customer still subscribed for newsletters emails after registration. * - * @magentoConfigFixture ccustomer/create/account_confirm 1 + * @magentoDbIsolation enabled */ public function testCreatePosWithSubscribeEmailAction() { + $config = Bootstrap::getObjectManager()->get(MutableScopeConfigInterface::class); + $accountConfirmationRequired = $config->getValue( + AccountConfirmation::XML_PATH_IS_CONFIRM, + ScopeInterface::SCOPE_WEBSITES + ); + $config->setValue(AccountConfirmation::XML_PATH_IS_CONFIRM, 1, ScopeInterface::SCOPE_WEBSITES); + $subscriber = Bootstrap::getObjectManager()->create(Subscriber::class); $customerEmail = 'subscribeemail@example.com'; // Subscribe by email @@ -100,6 +110,12 @@ public function testCreatePosWithSubscribeEmailAction() // check customer subscribed to newsletter $this->assertTrue($subscriberResource->loadByCustomerData($customer)['subscriber_status'] === "1"); + + $config->setValue( + AccountConfirmation::XML_PATH_IS_CONFIRM, + $accountConfirmationRequired, + ScopeInterface::SCOPE_WEBSITES + ); } /** From 0ee4cdd10392fa42dc3ea148d27bc557a3ce167b Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 27 Sep 2019 15:24:44 +0300 Subject: [PATCH 0685/2437] Magento 2.3.2 - PWA - graphQl fetching Issue for phtml file called in static block #960 --- .../Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php index 9cbb34a939109..8eda6d27a1c72 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php @@ -66,7 +66,7 @@ public function getData(string $blockIdentifier): array } $renderedContent = $this->state->emulateAreaCode( - 'frontend', + \Magento\Framework\App\Area::AREA_FRONTEND, [$this, 'getRenderedBlockContent'], [$block->getContent()] ); @@ -81,6 +81,8 @@ public function getData(string $blockIdentifier): array } /** + * Get block data as it rendered on frontend + * * @param string $blockContent * * @return string From 0c41aeaa7f831c534360dee7a7e0518c30df6b05 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 27 Sep 2019 15:35:02 +0400 Subject: [PATCH 0686/2437] MAGETWO-67450: The rate in order is duplicated - Updated automated test script --- .../Test/Mftf/Data/CatalogPriceConfigData.xml | 16 +++++++++ .../Mftf/Data/CurrencyRatesConfigData.xml | 33 +++++++++++++++++++ ...ayWhenChooseThreeAllowedCurrenciesTest.xml | 6 ++-- .../AdminOrderRateDisplayedInOneLineTest.xml | 28 ++++++++-------- 4 files changed, 66 insertions(+), 17 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml new file mode 100644 index 0000000000000..a16acbf4c90b3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CatalogPriceScopeConfigData"> + <data key="path">catalog/price/scope</data> + <data key="value_website">1</data> + <data key="value_global">0</data> + </entity> +</entities> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml new file mode 100644 index 0000000000000..5f4e3dc17341d --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SetCurrencyBaseConfig"> + <data key="path">currency/options/base</data> + <data key="value_usd">USD</data> + <data key="value_eur">EUR</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetAllowedCurrenciesConfig"> + <data key="path">currency/options/allow</data> + <data key="value_three_currencies">EUR,USD,RUB</data> + <data key="value_two_currencies">EUR,USD</data> + <data key="value_usd">USD</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetDefaultCurrencyConfig"> + <data key="path">currency/options/default</data> + <data key="value_eur">EUR</data> + <data key="value_usd">USD</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> +</entities> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index 823e9d1f15578..2cb5ea81e5f31 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -24,9 +24,9 @@ <!--Create product--> <createData entity="SimpleProduct2" stepKey="createNewProduct"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseUSDWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow EUR,USD,RUB" stepKey="setAllowedCurrencyWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default EUR" stepKey="setCurrencyDefaultUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_three_currencies}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> <!--Delete created product--> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index 1d96fa1de6119..7ac01c0792810 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -24,28 +24,28 @@ <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> <!--Set price scope website--> - <magentoCLI command="config:set catalog/price/scope 1" stepKey="setCatalogPriceScopeWebsite"/> + <magentoCLI command="config:set {{CatalogPriceScopeConfigData.path}} {{CatalogPriceScopeConfigData.value_website}}" stepKey="setCatalogPriceScopeWebsite"/> <!--Set Currency options for Default Config--> - <magentoCLI command="config:set currency/options/base EUR" stepKey="setCurrencyBaseEUR"/> - <magentoCLI command="config:set currency/options/allow EUR,USD" stepKey="setAllowedCurrencyEUR"/> - <magentoCLI command="config:set currency/options/default EUR" stepKey="setCurrencyDefaultEUR"/> + <magentoCLI command="config:set {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_eur}}" stepKey="setCurrencyBaseEUR"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_two_currencies}}" stepKey="setAllowedCurrencyEUR"/> + <magentoCLI command="config:set {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEUR"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseEURWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow EUR,USD" stepKey="setAllowedCurrencyEURWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default EUR" stepKey="setCurrencyDefaultEURWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseEURWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_two_currencies}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> <!--Delete created product--> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!--Reset configurations--> - <magentoCLI command="config:set catalog/price/scope 0" stepKey="setCatalogPriceScopeGlobal"/> - <magentoCLI command="config:set currency/options/base USD" stepKey="setCurrencyBaseUSD"/> - <magentoCLI command="config:set currency/options/default USD" stepKey="setCurrencyDefaultUSD"/> - <magentoCLI command="config:set currency/options/allow USD" stepKey="setAllowedCurrencyUSD"/> + <magentoCLI command="config:set {{CatalogPriceScopeConfigData.path}} {{CatalogPriceScopeConfigData.value_global}}" stepKey="setCatalogPriceScopeGlobal"/> + <magentoCLI command="config:set {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSD"/> + <magentoCLI command="config:set {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_usd}}" stepKey="setCurrencyDefaultUSD"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_usd}}" stepKey="setAllowedCurrencyUSD"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/base USD" stepKey="setCurrencyBaseUSDWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/default USD" stepKey="setCurrencyDefaultUSDWebsites"/> - <magentoCLI command="config:set --scope=websites --scope-code=base currency/options/allow USD" stepKey="setAllowedCurrencyUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_usd}}" stepKey="setCurrencyDefaultUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_usd}}" stepKey="setAllowedCurrencyUSDWebsites"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Open created product on Storefront and place for order--> From 3489da5cf2cb98ad9ff758b3a3c8f6783339436b Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Fri, 27 Sep 2019 16:57:21 +0300 Subject: [PATCH 0687/2437] GraphQl-972: added support of the Global scope in the config fixture --- .../Annotation/ApiConfigFixture.php | 27 +++++++++++ ...peConfig.php => ApiMutableScopeConfig.php} | 10 +--- .../GraphQl/Catalog/StoreConfigTest.php | 46 +++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) rename dev/tests/api-functional/framework/Magento/TestFramework/App/{MutableScopeConfig.php => ApiMutableScopeConfig.php} (90%) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php index a5be493032836..8e6ae6190a6b2 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php @@ -10,6 +10,7 @@ use Magento\Config\Model\Config; use Magento\Config\Model\ResourceModel\Config as ConfigResource; use Magento\Config\Model\ResourceModel\Config\Data\CollectionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Store\Model\StoreManagerInterface; use PHPUnit\Framework\TestCase; @@ -156,4 +157,30 @@ private function getStoreIdByCode(string $storeCode): int $store = $storeManager->getStore($storeCode); return (int)$store->getId(); } + + /** + * @inheritDoc + */ + protected function _setConfigValue($configPath, $value, $storeCode = false) + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + if ($storeCode === false) { + $objectManager->get( + \Magento\TestFramework\App\ApiMutableScopeConfig::class + )->setValue( + $configPath, + $value, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT + ); + } else { + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\TestFramework\App\ApiMutableScopeConfig::class + )->setValue( + $configPath, + $value, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeCode + ); + } + } } diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/App/MutableScopeConfig.php b/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php similarity index 90% rename from dev/tests/api-functional/framework/Magento/TestFramework/App/MutableScopeConfig.php rename to dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php index efcb5be34e594..63a0ec0f7f48d 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/App/MutableScopeConfig.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php @@ -17,7 +17,7 @@ /** * @inheritdoc */ -class MutableScopeConfig implements MutableScopeConfigInterface +class ApiMutableScopeConfig implements MutableScopeConfigInterface { /** * @var Config @@ -56,7 +56,6 @@ public function setValue( /** * Clean app config cache * - * @param string|null $type * @return void */ public function clean() @@ -89,18 +88,13 @@ private function getTestAppConfig() private function persistConfig($path, $value, $scopeType, $scopeCode): void { $pathParts = explode('/', $path); - $store = ''; + $store = 0; if ($scopeType === \Magento\Store\Model\ScopeInterface::SCOPE_STORE) { if ($scopeCode !== null) { $store = ObjectManager::getInstance() ->get(\Magento\Store\Api\StoreRepositoryInterface::class) ->get($scopeCode) ->getId(); - } else { - $store = ObjectManager::getInstance() - ->get(\Magento\Store\Model\StoreManagerInterface::class) - ->getStore() - ->getId(); } } $configData = [ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php index 7a30023c89f7e..0982007daaa44 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php @@ -59,4 +59,50 @@ public function testGetStoreConfig() $this->assertEquals('asc', $response['storeConfig']['catalog_default_sort_by']); $this->assertEquals(2, $response['storeConfig']['root_category_id']); } + + /** + * @magentoApiDataFixture Magento/Store/_files/store.php + * @magentoConfigFixture catalog/seo/product_url_suffix global_test_product_suffix + * @magentoConfigFixture catalog/seo/category_url_suffix global_test_category_suffix + * @magentoConfigFixture catalog/seo/title_separator __ + * @magentoConfigFixture catalog/frontend/list_mode 3 + * @magentoConfigFixture catalog/frontend/grid_per_page_values 16 + * @magentoConfigFixture catalog/frontend/list_per_page_values 8 + * @magentoConfigFixture catalog/frontend/grid_per_page 16 + * @magentoConfigFixture catalog/frontend/list_per_page 8 + * @magentoConfigFixture catalog/frontend/default_sort_by asc + */ + public function testGetStoreConfigGlobal() + { + $query + = <<<QUERY +{ + storeConfig{ + product_url_suffix, + category_url_suffix, + title_separator, + list_mode, + grid_per_page_values, + list_per_page_values, + grid_per_page, + list_per_page, + catalog_default_sort_by, + root_category_id + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('storeConfig', $response); + + $this->assertEquals('global_test_product_suffix', $response['storeConfig']['product_url_suffix']); + $this->assertEquals('global_test_category_suffix', $response['storeConfig']['category_url_suffix']); + $this->assertEquals('__', $response['storeConfig']['title_separator']); + $this->assertEquals('3', $response['storeConfig']['list_mode']); + $this->assertEquals('16', $response['storeConfig']['grid_per_page_values']); + $this->assertEquals(16, $response['storeConfig']['grid_per_page']); + $this->assertEquals('8', $response['storeConfig']['list_per_page_values']); + $this->assertEquals(8, $response['storeConfig']['list_per_page']); + $this->assertEquals('asc', $response['storeConfig']['catalog_default_sort_by']); + $this->assertEquals(2, $response['storeConfig']['root_category_id']); + } } From 63bd2327cdaba3f75b9d6e955e181f2c4a18a13c Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Fri, 27 Sep 2019 17:08:05 +0300 Subject: [PATCH 0688/2437] GraphQl-972: removed redundant else --- .../Annotation/ApiConfigFixture.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php index 8e6ae6190a6b2..8061cb138660d 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiConfigFixture.php @@ -172,15 +172,16 @@ protected function _setConfigValue($configPath, $value, $storeCode = false) $value, ScopeConfigInterface::SCOPE_TYPE_DEFAULT ); - } else { - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\TestFramework\App\ApiMutableScopeConfig::class - )->setValue( - $configPath, - $value, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $storeCode - ); + + return; } + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\TestFramework\App\ApiMutableScopeConfig::class + )->setValue( + $configPath, + $value, + \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + $storeCode + ); } } From 6bc920a871788aba4ff4e1d2a43743c2411e4475 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Fri, 27 Sep 2019 09:32:36 -0500 Subject: [PATCH 0689/2437] MC-20334: Update tests related to SKU field issue in AdvancedSearch --- .../Magento/Catalog/Test/Mftf/Data/ProductData.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 131d89ffd44e9..ed436d9926ae3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -559,6 +559,20 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiVirtualProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_virtual_product</data> + <data key="type_id">virtual</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Virtual Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="SimpleProductWithNewFromDate" type="product"> <data key="sku" unique="suffix">SimpleProduct</data> <data key="type_id">simple</data> From 2826f74772af2091e8b5262d06beac7b79eaa700 Mon Sep 17 00:00:00 2001 From: skylineop <skylineop@Olegs-MacBook-Pro.local> Date: Fri, 27 Sep 2019 18:00:22 +0300 Subject: [PATCH 0690/2437] 15959 Extension attributes of quote on checkout page --- app/code/Magento/Checkout/Model/DefaultConfigProvider.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index 70352b50d8de4..15c22b1f72573 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -397,6 +397,9 @@ private function getQuoteData() if ($this->checkoutSession->getQuote()->getId()) { $quote = $this->quoteRepository->get($this->checkoutSession->getQuote()->getId()); $quoteData = $quote->toArray(); + if (is_object($quote->getExtensionAttributes())) { + $quoteData['extension_attributes'] = $quote->getExtensionAttributes()->__toArray(); + } $quoteData['is_virtual'] = $quote->getIsVirtual(); if (!$quote->getCustomer()->getId()) { From d7aba3d377ab4f72a265c24f16577b3049dffc60 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Fri, 27 Sep 2019 11:37:25 -0500 Subject: [PATCH 0691/2437] MC-20119: Spike to figure out how to persist promotions data on quote - added POC --- .../Quote/Model/Quote/Address/Total.php | 15 +++++++++ .../Model/Quote/Item/Plugin/Discount.php | 23 ++++++++++++++ app/code/Magento/Quote/etc/db_schema.xml | 5 ++- app/code/Magento/Quote/etc/di.xml | 3 ++ .../SalesRule/Model/Quote/Discount.php | 31 +++++++++++++++++++ .../Magento/SalesRule/Model/RulesApplier.php | 12 +++---- 6 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index d8dd0953407d4..3ed9f7f984334 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -200,4 +200,19 @@ public function getFullInfo() } return $fullInfo; } + + public function getDiscountBreakdown() { + $fullInfo = $this->getData('discount_breakdown'); + if (is_string($fullInfo)) { + $fullInfo = $this->serializer->unserialize($fullInfo); + } + return $fullInfo; + } + + public function setDiscountBreakdown($discount) { + if (isset($discount)) { + $this->setData('discount_breakdown', $this->serializer->serialize($discount)); + } + return $this; + } } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php b/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php new file mode 100644 index 0000000000000..134258c2e09ab --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php @@ -0,0 +1,23 @@ +<?php +/** + * Created by PhpStorm. + * User: pganapat + * Date: 9/25/19 + * Time: 7:42 PM + */ + +namespace Magento\Quote\Model\Quote\Item\Plugin; + +use Magento\Quote\Model\Quote\Item\CartItemPersister; +use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Quote\Api\Data\CartInterface; + +class Discount +{ + + public function beforeSave(CartItemPersister $subject, CartInterface $quote, CartItemInterface $cartItem) { + $extension = $cartItem->getExtensionAttributes(); + $cartItem->setDiscounts(\GuzzleHttp\json_encode($extension->getDiscounts())); + return [$quote, $cartItem]; + } +} \ No newline at end of file diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index b4c75fc1d21d0..cf3ce416e24c5 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -173,8 +173,10 @@ default="0" comment="Base Grand Total"/> <column xsi:type="text" name="customer_notes" nullable="true" comment="Customer Notes"/> <column xsi:type="text" name="applied_taxes" nullable="true" comment="Applied Taxes"/> - <column xsi:type="varchar" name="discount_description" nullable="true" length="255" + <column xsi:type="varchar" name="discount_description" nullable="true" length="25500" comment="Discount Description"/> + <column xsi:type="text" name="discount_breakdown" nullable="true" length="255" + comment="Discount Breakdown"/> <column xsi:type="decimal" name="shipping_discount_amount" scale="4" precision="20" unsigned="false" nullable="true" comment="Shipping Discount Amount"/> <column xsi:type="decimal" name="base_shipping_discount_amount" scale="4" precision="20" unsigned="false" @@ -234,6 +236,7 @@ <column xsi:type="varchar" name="name" nullable="true" length="255" comment="Name"/> <column xsi:type="text" name="description" nullable="true" comment="Description"/> <column xsi:type="text" name="applied_rule_ids" nullable="true" comment="Applied Rule Ids"/> + <column xsi:type="text" name="discounts" nullable="true" comment="Discounts"/> <column xsi:type="text" name="additional_data" nullable="true" comment="Additional Data"/> <column xsi:type="smallint" name="is_qty_decimal" padding="5" unsigned="true" nullable="true" identity="false" comment="Is Qty Decimal"/> diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index cd5e62307fdca..6060e3e2845a1 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -101,6 +101,9 @@ <type name="Magento\Catalog\Model\Product\Action"> <plugin name="quoteProductMassChange" type="Magento\Quote\Model\Product\Plugin\MarkQuotesRecollectMassDisabled"/> </type> + <type name="Magento\Quote\Model\Quote\Item\CartItemPersister"> + <plugin name="discountItemPlugin" type="Magento\Quote\Model\Quote\Item\Plugin\Discount"/> + </type> <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> <arguments> <argument name="validationRules" xsi:type="array"> diff --git a/app/code/Magento/SalesRule/Model/Quote/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Discount.php index 315ce874513a3..47aaee2bd8fa7 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Discount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Discount.php @@ -131,6 +131,7 @@ public function collect( $this->calculator->prepareDescription($address); $total->setDiscountDescription($address->getDiscountDescription()); + $total->setDiscountBreakdown($this->aggregateDiscountPerRule($quote)); $total->setSubtotalWithDiscount($total->getSubtotal() + $total->getDiscountAmount()); $total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $total->getBaseDiscountAmount()); @@ -218,4 +219,34 @@ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Qu } return $result; } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @return array + */ + private function aggregateDiscountPerRule( + \Magento\Quote\Model\Quote $quote + ) { + $items = $quote->getItems(); + $discountPerRule = []; + if ($items) { + foreach ($items as $item) { + $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); + if ($discountBreakdown) { + foreach ($discountBreakdown as $key => $value) { + /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ + $discount = $value['discount']; + $ruleLabel = $value['rule']; + if (isset($discountPerRule[$key])) { + $discountPerRule[$key]['discount'] += $discount; + } else { + $discountPerRule[$key]['discount'] = $discount; + } + $discountPerRule[$key]['rule'] = $ruleLabel; + } + } + } + } + return $discountPerRule; + } } diff --git a/app/code/Magento/SalesRule/Model/RulesApplier.php b/app/code/Magento/SalesRule/Model/RulesApplier.php index 34b8ba89fafa8..f09d02b31dfb4 100644 --- a/app/code/Magento/SalesRule/Model/RulesApplier.php +++ b/app/code/Magento/SalesRule/Model/RulesApplier.php @@ -156,7 +156,7 @@ public function addDiscountDescription($address, $rule) */ protected function applyRule($item, $rule, $address, $couponCode) { - $discountData = $this->getDiscountData($item, $rule); + $discountData = $this->getDiscountData($item, $rule, $address); $this->setDiscountData($discountData, $item); $this->maintainAddressCouponCode($address, $rule, $couponCode); @@ -172,7 +172,7 @@ protected function applyRule($item, $rule, $address, $couponCode) * @param \Magento\SalesRule\Model\Rule $rule * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data */ - protected function getDiscountData($item, $rule) + protected function getDiscountData($item, $rule, $address) { $qty = $this->validatorUtility->getItemQty($item, $rule); @@ -181,7 +181,7 @@ protected function getDiscountData($item, $rule) $discountData = $discountCalculator->calculate($rule, $item, $qty); $this->eventFix($discountData, $item, $rule, $qty); $this->validatorUtility->deltaRoundingFix($discountData, $item); - $this->setDiscountBreakdown($discountData, $item, $rule); + $this->setDiscountBreakdown($discountData, $item, $rule, $address); /** * We can't use row total here because row total not include tax @@ -201,7 +201,7 @@ protected function getDiscountData($item, $rule) * @param \Magento\SalesRule\Model\Rule $rule * @return $this */ - private function setDiscountBreakdown($discountData, $item, $rule) + private function setDiscountBreakdown($discountData, $item, $rule, $address) { if ($discountData->getAmount() > 0) { /** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ @@ -211,8 +211,8 @@ private function setDiscountBreakdown($discountData, $item, $rule) $discount->setBaseAmount($discountData->getBaseAmount()); $discount->setOriginalAmount($discountData->getOriginalAmount()); $discountBreakdown = $item->getExtensionAttributes()->getDiscounts() ?? []; - $discountBreakdown[$rule->getId()]['discount'] = $discount; - $discountBreakdown[$rule->getId()]['rule'] = $rule; + $discountBreakdown[$rule->getId()]['discount'] = $discountData->getAmount(); + $discountBreakdown[$rule->getId()]['rule'] = $rule->getStoreLabel($address->getQuote()->getStore()) ?: __('Discount'); $item->getExtensionAttributes()->setDiscounts($discountBreakdown); } return $this; From a24ebcfe8fe147ac6d3651a3b23b82e78604d2a3 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Fri, 27 Sep 2019 14:17:10 -0500 Subject: [PATCH 0692/2437] MC-20182: Update AdvancedSearch related MTF tests to Elasticsearch --- .../Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml | 2 +- .../StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml | 2 +- .../Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml | 2 +- .../Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml | 1 + .../Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml index 0ebcf59dae2b3..9ad868ff6db7e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByAllParametersTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> - <argument name="productName" value="abc_dfj"/> + <argument name="productName" value="$$createProduct.name$$"/> <argument name="sku" value="abc_dfj"/> <argument name="description" value="adc_Full"/> <argument name="short_description" value="abc_short"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml index 71b6ca064e0c8..4d3ba22f79356 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameSkuDescriptionPriceTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> - <argument name="productName" value="abc_dfj"/> + <argument name="productName" value="$$createProduct.name$$"/> <argument name="sku" value="abc_dfj"/> <argument name="description" value="adc_Full"/> <argument name="short_description" value="abc_short"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml index e6020a65cb62d..f0b81e08252fc 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByNameTest.xml @@ -22,7 +22,7 @@ <createData entity="ABC_123_SimpleProduct" stepKey="createProduct"/> </before> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> - <argument name="productName" value="adc_123"/> + <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml index bf2b2f7bbbee9..f875021bd9669 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPartialNameTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <group value="searchFrontend"/> <group value="mtf_migrated"/> + <group value="SearchEngineMysql"/> </annotations> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> <argument name="productName" value="abc"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml index 16bc36f907a61..c55b3f8b8b7db 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml @@ -35,7 +35,7 @@ <!-- 3. Fill test data in to field(s) 4. Click "Search" button--> <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> - <argument name="productName" value="abc_dfj"/> + <argument name="productName" value="$$createProduct.name$$"/> <argument name="sku" value="abc_dfj"/> </actionGroup> From c614d21e600246583a4339bb8307631c02cd12b7 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Fri, 27 Sep 2019 15:11:14 -0500 Subject: [PATCH 0693/2437] MC-20337: Update tests related to Checkout, Configurable, Layered Navigation --- .../Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml | 3 +++ ...torefrontVerifyConfigurableProductLayeredNavigationTest.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml index 925e7a890cead..9796c14f5519a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml @@ -129,6 +129,9 @@ <!-- Save product --> <actionGroup ref="saveConfigurableProductAddToCurrentAttributeSet" stepKey="saveProduct"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!-- Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index bb69122dc0be9..182c8c069ab23 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -135,6 +135,9 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Open Category in Store Front and select product attribute option from sidebar --> <actionGroup ref="SelectStorefrontSideBarAttributeOption" stepKey="selectStorefrontProductAttributeOption"> <argument name="categoryName" value="$$createCategory.name$$"/> From beb1791e2df296e29f2d0350ec282cf2f22d0f46 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Fri, 27 Sep 2019 15:23:30 -0500 Subject: [PATCH 0694/2437] MC-20336: Update tests related to Catalog Search and Swatches --- .../Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml index 08cffcccd5203..f93b9ffd83b83 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml @@ -27,6 +27,7 @@ <fillField selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" userInput="{{minLength}}" stepKey="setMinQueryLength"/> <!--Scrolling till Category Permissions to make sure the collapse tab for Catalog Search is visible--> <scrollTo selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="scrollToCatalogSearchTab1"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="waitForElementVisibleWhileScrolling"/> <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseTab"/> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> From 4bad17ba7651575b420ef6433c901670bd4f2a96 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Fri, 27 Sep 2019 17:17:19 -0500 Subject: [PATCH 0695/2437] MC-20336: Update tests related to Catalog Search and Swatches "Added wait condition to make the page gets loaded till it finishes the text to be filled" --- .../Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml index f93b9ffd83b83..e1c2a7867850f 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml @@ -25,9 +25,9 @@ <see userInput="{{MinMaxQueryLength.Hint}}" selector="{{AdminCatalogSearchConfigurationSection.maxQueryLengthHint}}" stepKey="seeHint2"/> <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.minQueryLengthInherit}}" stepKey="uncheckSystemValue"/> <fillField selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" userInput="{{minLength}}" stepKey="setMinQueryLength"/> + <waitForPageLoad stepKey="waitForTextToBeFilled"/> <!--Scrolling till Category Permissions to make sure the collapse tab for Catalog Search is visible--> <scrollTo selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="scrollToCatalogSearchTab1"/> - <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="waitForElementVisibleWhileScrolling"/> <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseTab"/> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> From 18a32604d8069af1823c7a4306f3dcf16deb1f84 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Sun, 29 Sep 2019 13:29:42 +0300 Subject: [PATCH 0696/2437] graphQl-961: ShippingAddressInput.postcode: String, is not required by Schema --- .../Model/Cart/QuoteAddressFactory.php | 25 ++++++++++++++++--- .../Guest/SetBillingAddressOnCartTest.php | 14 +++++------ .../Guest/SetShippingAddressOnCartTest.php | 12 ++++----- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php index 52f5387f15785..9fe8d34435d5d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -9,12 +9,14 @@ use Magento\Customer\Helper\Address as AddressHelper; use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; +use Magento\Directory\Api\CountryInformationAcquirerInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote\Address as QuoteAddress; use Magento\Quote\Model\Quote\AddressFactory as BaseQuoteAddressFactory; +use Magento\Framework\App\ObjectManager; /** * Create QuoteAddress @@ -36,38 +38,53 @@ class QuoteAddressFactory */ private $addressHelper; + /** + * @var CountryInformationAcquirerInterface + */ + private $countryInformationAcquirer; + /** * @param BaseQuoteAddressFactory $quoteAddressFactory * @param GetCustomerAddress $getCustomerAddress * @param AddressHelper $addressHelper + * @param CountryInformationAcquirerInterface|null $countryInformationAcquirer */ public function __construct( BaseQuoteAddressFactory $quoteAddressFactory, GetCustomerAddress $getCustomerAddress, - AddressHelper $addressHelper + AddressHelper $addressHelper, + CountryInformationAcquirerInterface $countryInformationAcquirer = null ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->getCustomerAddress = $getCustomerAddress; $this->addressHelper = $addressHelper; + $this->countryInformationAcquirer = $countryInformationAcquirer; + $this->countryInformationAcquirer = $countryInformationAcquirer + ?: ObjectManager::getInstance()->get(CountryInformationAcquirerInterface::class); } /** * Create QuoteAddress based on input data * * @param array $addressInput + * * @return QuoteAddress * @throws GraphQlInputException */ public function createBasedOnInputData(array $addressInput): QuoteAddress { $addressInput['country_id'] = ''; - if ($addressInput['country_code']) { + if (isset($addressInput['country_code']) && $addressInput['country_code']) { $addressInput['country_code'] = strtoupper($addressInput['country_code']); $addressInput['country_id'] = $addressInput['country_code']; } - if (isset($addressInput['region'])) { - $addressInput['region_code'] = $addressInput['region']; + if ($addressInput['country_id'] && isset($addressInput['region'])) { + $countryInformation = $this->countryInformationAcquirer->getCountryInfo($addressInput['country_id']); + $availableRegions = $countryInformation->getAvailableRegions(); + if (null !== $availableRegions) { + $addressInput['region_code'] = $addressInput['region']; + } } $maxAllowedLineCount = $this->addressHelper->getStreetLines(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 730e65b4ba8aa..dd113c2a0205e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -48,7 +48,7 @@ public function testSetNewBillingAddress() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -106,7 +106,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -182,7 +182,7 @@ public function testSetBillingAddressToCustomerCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -259,7 +259,7 @@ public function testSetBillingAddressOnNonExistentCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -387,7 +387,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -433,7 +433,7 @@ public function testSetNewBillingAddressRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -476,7 +476,7 @@ public function testSetBillingAddressWithLowerCaseCountry() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "us" telephone: "88776655" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 0351a4f58a8e0..217759edf10fd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -49,7 +49,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -114,7 +114,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -266,7 +266,7 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -335,7 +335,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -349,7 +349,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company 2" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -389,7 +389,7 @@ public function testSetShippingAddressOnNonExistentCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" From 18784c217649e6f6eb2137dc939845bb12a79755 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Sun, 29 Sep 2019 13:58:46 +0300 Subject: [PATCH 0697/2437] graphQl-961: ShippingAddressInput.postcode: String, is not required by Schema --- .../Model/Cart/SetBillingAddressOnCart.php | 18 +++++++++++++++--- .../Model/Cart/SetShippingAddressesOnCart.php | 17 +++++++++++++++-- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index cf8e38ebbfcc6..dd2daa6cb24ff 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -112,15 +112,27 @@ private function createBillingAddress( ); } - $errors = $billingAddress->validate(); + $this->validateAddress($billingAddress); + + return $billingAddress; + } + + /** + * Validate quote address. + * + * @param Address $shippingAddress + * + * @throws GraphQlInputException + */ + private function validateAddress(Address $shippingAddress) + { + $errors = $shippingAddress->validate(); if (true !== $errors) { throw new GraphQlInputException( __('Shipping address error: %message', ['message' => $this->getAddressErrors($errors)]) ); } - - return $billingAddress; } /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 9e39992eed830..a15398806efa6 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -11,6 +11,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote\Address; /** * Set single shipping address for a specified shopping cart @@ -82,6 +83,20 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } + $this->validateAddress($shippingAddress); + + $this->assignShippingAddressToCart->execute($cart, $shippingAddress); + } + + /** + * Validate quote address. + * + * @param Address $shippingAddress + * + * @throws GraphQlInputException + */ + private function validateAddress(Address $shippingAddress) + { $errors = $shippingAddress->validate(); if (true !== $errors) { @@ -89,8 +104,6 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('Shipping address error: %message', ['message' => $this->getAddressErrors($errors)]) ); } - - $this->assignShippingAddressToCart->execute($cart, $shippingAddress); } /** From cee3afc81c64d497e10e1ae2f1f02da6e7c1032f Mon Sep 17 00:00:00 2001 From: Bohdan Shevchenko <1408sheva@gmail.com> Date: Mon, 30 Sep 2019 09:18:28 +0300 Subject: [PATCH 0698/2437] MC-20481: [API Test] Revoke all access Tokens for Customer --- .../Customer/Api/CustomerRepositoryTest.php | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php index 709abbbb8fbf9..7a02e2f843719 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php @@ -10,6 +10,9 @@ use Magento\Customer\Api\Data\AddressInterface as Address; use Magento\Framework\Api\SortOrder; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Webapi\Rest\Request; +use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Helper\Customer as CustomerHelper; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -780,6 +783,66 @@ public function testSearchCustomersMultipleFilterGroups() $this->assertEquals(0, $searchResults['total_count']); } + /** + * Test revoking all access Tokens for customer + */ + public function testRevokeAllAccessTokensForCustomer() + { + $customerData = $this->_createCustomer(); + + /** @var CustomerTokenServiceInterface $customerTokenService */ + $customerTokenService = Bootstrap::getObjectManager()->create(CustomerTokenServiceInterface::class); + $token = $customerTokenService->createCustomerAccessToken( + $customerData[Customer::EMAIL], + CustomerHelper::PASSWORD + ); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/me', + 'httpMethod' => Request::HTTP_METHOD_GET, + 'token' => $token, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'GetSelf', + 'token' => $token, + ], + ]; + + $customerLoadedData = $this->_webApiCall($serviceInfo, ['customerId' => $customerData[Customer::ID]]); + self::assertGreaterThanOrEqual($customerData[Customer::UPDATED_AT], $customerLoadedData[Customer::UPDATED_AT]); + unset($customerData[Customer::UPDATED_AT]); + self::assertArraySubset($customerData, $customerLoadedData); + + $revokeToken = $customerTokenService->revokeCustomerAccessToken($customerData[Customer::ID]); + self::assertTrue($revokeToken); + + try { + $customerTokenService->revokeCustomerAccessToken($customerData[Customer::ID]); + } catch (\Throwable $exception) { + $this->assertInstanceOf(LocalizedException::class, $exception); + $this->assertEquals('This customer has no tokens.', $exception->getMessage()); + } + + $expectedMessage = 'The consumer isn\'t authorized to access %resources.'; + + try { + $this->_webApiCall($serviceInfo, ['customerId' => $customerData[Customer::ID]]); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + 'SoapFault does not contain expected message.' + ); + } catch (\Throwable $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + $this->assertEquals(['resources' => 'self'], $errorObj['parameters']); + $this->assertEquals(HTTPExceptionCodes::HTTP_UNAUTHORIZED, $e->getCode()); + } + } + /** * Retrieve customer data by Id * From 8d10c41f8e84cb6d6ace926afdc66c572f5b5487 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Mon, 30 Sep 2019 13:24:40 +0300 Subject: [PATCH 0699/2437] MAGETWO-67450: The rate in order is duplicated - Fix CR comments --- .../view/adminhtml/templates/order/view/info.phtml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml index 8ada10450c967..825ac8205772e 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/view/info.phtml @@ -9,6 +9,10 @@ */ $order = $block->getOrder(); +$baseCurrencyCode = (string)$order->getBaseCurrencyCode(); +$globalCurrencyCode = (string)$order->getGlobalCurrencyCode(); +$orderCurrencyCode = (string)$order->getOrderCurrencyCode(); + $orderAdminDate = $block->formatDate( $block->getOrderAdminDate($order->getCreatedAt()), \IntlDateFormatter::MEDIUM, @@ -94,15 +98,15 @@ $allowedAddressHtmlTags = ['b', 'br', 'em', 'i', 'li', 'ol', 'p', 'strong', 'sub <td><?= $block->escapeHtml($order->getRemoteIp()); ?><?= $order->getXForwardedFor() ? ' (' . $block->escapeHtml($order->getXForwardedFor()) . ')' : ''; ?></td> </tr> <?php endif; ?> - <?php if ($order->getGlobalCurrencyCode() != $order->getBaseCurrencyCode()) : ?> + <?php if ($globalCurrencyCode !== $baseCurrencyCode) : ?> <tr> - <th><?= $block->escapeHtml(__('%1 / %2 rate:', $order->getGlobalCurrencyCode(), $order->getBaseCurrencyCode())) ?></th> + <th><?= $block->escapeHtml(__('%1 / %2 rate:', $globalCurrencyCode, $baseCurrencyCode)) ?></th> <td><?= $block->escapeHtml($order->getBaseToGlobalRate()) ?></td> </tr> <?php endif; ?> - <?php if ($order->getBaseCurrencyCode() != $order->getOrderCurrencyCode() && $order->getGlobalCurrencyCode() != $order->getOrderCurrencyCode()) : ?> + <?php if ($baseCurrencyCode !== $orderCurrencyCode && $globalCurrencyCode !== $orderCurrencyCode) : ?> <tr> - <th><?= $block->escapeHtml(__('%1 / %2 rate:', $order->getOrderCurrencyCode(), $order->getBaseCurrencyCode())) ?></th> + <th><?= $block->escapeHtml(__('%1 / %2 rate:', $orderCurrencyCode, $baseCurrencyCode)) ?></th> <td><?= $block->escapeHtml($order->getBaseToOrderRate()) ?></td> </tr> <?php endif; ?> From ea68f57e55b07c8418a8f709d87f9ed059e54aed Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 30 Sep 2019 16:03:28 +0400 Subject: [PATCH 0700/2437] MC-18215: Error message while creating shipping label - Updated automated test script --- app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 1 - .../Mftf/Data/{FexExConfigData.xml => FedExConfigData.xml} | 0 .../Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml | 4 ++-- 3 files changed, 2 insertions(+), 3 deletions(-) rename app/code/Magento/Fedex/Test/Mftf/Data/{FexExConfigData.xml => FedExConfigData.xml} (100%) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 986369f4ebc66..4d7a39b3246e1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -291,7 +291,6 @@ <data key="company">Magento</data> <array key="street"> <item>Augsburger Strabe 41</item> - <item>Augsburger Strabe 42</item> </array> <data key="city">Berlin</data> <data key="country_id">DE</data> diff --git a/app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml b/app/code/Magento/Fedex/Test/Mftf/Data/FedExConfigData.xml similarity index 100% rename from app/code/Magento/Fedex/Test/Mftf/Data/FexExConfigData.xml rename to app/code/Magento/Fedex/Test/Mftf/Data/FedExConfigData.xml diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index 4b4654abb2bf0..e50d484df27c4 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -42,14 +42,14 @@ <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setStoreInformationCity"/> <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setStoreInformationPostcode"/> <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setStoreInformationStreetAddress"/> - <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[1]}}'" stepKey="setStoreInformationStreetAddress2"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setStoreInformationStreetAddress2"/> <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} {{AdminGeneralSetVatNumberConfigData.value}}" stepKey="setStoreInformationVatNumber"/> <!--Set Shipping settings origin data--> <magentoCLI command="config:set {{AdminShippingSettingsOriginCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setOriginCountry"/> <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setOriginCity"/> <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setOriginZipCode"/> <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setOriginStreetAddress"/> - <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[1]}}'" stepKey="setOriginStreetAddress2"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setOriginStreetAddress2"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> From 3cd8e6ec5ba041d72d939747ec5bc5804f3d23b1 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 30 Sep 2019 16:47:49 +0400 Subject: [PATCH 0701/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml | 2 +- ... => StorefrontElasticsearchSearchInvalidValueTest.xml} | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) rename app/code/Magento/Elasticsearch6/Test/Mftf/Test/{StrorefrontElasticsearchSearchInvalidValueTest.xml => StorefrontElasticsearchSearchInvalidValueTest.xml} (94%) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index d8af28a23671f..38abc678dddd5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -65,7 +65,7 @@ <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> <entity name="ProductWithSpecialSymbols" extends="SimpleProduct" type="product"> - <data key="name">/s\i’m“p:l\$e#@!,.`=%&^</data> + <data key="name">SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^</data> </entity> <entity name="SimpleProductAfterImport1" type="product"> <data key="sku">SimpleProductForTest1</data> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml similarity index 94% rename from app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml rename to app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml index 84bbe00d2b971..932bef3a1452b 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StrorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StrorefrontElasticsearchSearchInvalidValueTest"> + <test name="StorefrontElasticsearchSearchInvalidValueTest"> <annotations> <features value="Search"/> <stories value="Search Product on Storefront"/> @@ -47,6 +47,8 @@ <waitForPageLoad stepKey="waitForProductIndexPage"/> <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteProduct"/> <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> + <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> + <magentoCLI command="cache:flush config" stepKey="flushCache"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Create new searchable product attribute--> @@ -94,11 +96,11 @@ </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbols"> - <argument name="phrase" value="?/s\i’m“p:l\$e#@!,.`=%&^;"/> + <argument name="phrase" value="?SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^"/> </actionGroup> <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbols"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbolsSecondTime"> - <argument name="phrase" value="? /s\i’m“p:l\$e#@!,.`=%&^ ;"/> + <argument name="phrase" value="? SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^ ;"/> </actionGroup> <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbolsSecondTime"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForThirdSearchTerm"> From 72d20aee6d7d37f46b205207053467a5ca57cf80 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Mon, 30 Sep 2019 16:18:12 +0300 Subject: [PATCH 0702/2437] MC-18165: Quick search with two chars shows all products --- .../StorefrontCatalogSearchActionGroup.xml | 5 +++- .../Test/Mftf/Data/ConfigData.xml | 19 +++++++++++++ .../Mftf/Test/SearchEntityResultsTest.xml | 27 ++++++++++++++----- 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index a907df2d718df..1c1bd95dd7105 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -45,8 +45,11 @@ <annotations> <description>Fill the Storefront Search field. Submits the Form. Validates that 'Minimum Search query length' warning appears.</description> </annotations> + <arguments> + <argument name="minQueryLength" type="string"/> + </arguments> - <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Minimum Search query length is 3" stepKey="assertQuickSearchNeedThreeOrMoreChars"/> + <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Minimum Search query length is {{minQueryLength}}" stepKey="assertQuickSearchNeedThreeOrMoreChars"/> </actionGroup> <actionGroup name="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup"> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..dd8c426592619 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="MinimalQueryLengthDefaultConfigData"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">3</data> + </entity> + <entity name="MinimalQueryLengthFourConfigData"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">4</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 47d107148a574..ba751fe34bf08 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -74,6 +74,7 @@ </test> <test name="QuickSearchEmptyResults"> <annotations> + <features value="CatalogSearch"/> <stories value="Search Product on Storefront"/> <title value="User should not get search results on query that doesn't return anything"/> <description value="Use invalid query to return no products"/> @@ -89,8 +90,8 @@ </createData> </before> <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> @@ -99,6 +100,7 @@ </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmpty"/> </test> + <test name="QuickSearchWithTwoCharsEmptyResults" extends="QuickSearchEmptyResults"> <annotations> <features value="CatalogSearch"/> @@ -110,19 +112,30 @@ <group value="CatalogSearch"/> <group value="mtf_migrated"/> </annotations> + + <before> + <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> + </before> + + <after> + <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> + </after> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,2); return ret;" before="searchStorefront" stepKey="getFirstTwoLetters"/> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" before="searchStorefront" stepKey="getFirstThreeLetters"/> + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}}); return ret;" after="getFirstTwoLetters" stepKey="getFirstConfigLetters"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontThreeLetters"> - <argument name="phrase" value="$getFirstThreeLetters"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> + <argument name="phrase" value="$getFirstConfigLetters"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontThreeLetters" stepKey="checkCannotSearchWithTooShortString"> + <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> <argument name="phrase" value="$getFirstTwoLetters"/> + <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> </actionGroup> <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> - <argument name="term" value="$getFirstThreeLetters"/> + <argument name="term" value="$getFirstConfigLetters"/> </actionGroup> </test> + <test name="QuickSearchProductByNameWithThreeLetters" extends="QuickSearchProductBySku"> <annotations> <stories value="Search Product on Storefront"/> From e1948fcaaa06c8cf412a9d45904d9fa335eaec2a Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Fri, 27 Sep 2019 14:12:50 -0500 Subject: [PATCH 0703/2437] MC-20394: [Magento Cloud] Redirect URL is getting cut off after 3 slashes - Fix redirect to base URL --- .../Plugin/RequestPreprocessor.php | 7 +- .../Plugin/RequestPreprocessorTest.php | 198 +++++++++++++++++- 2 files changed, 193 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php b/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php index 5df50581792ce..de2da54423822 100644 --- a/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php +++ b/app/code/Magento/Store/App/FrontController/Plugin/RequestPreprocessor.php @@ -5,6 +5,9 @@ */ namespace Magento\Store\App\FrontController\Plugin; +/** + * Class RequestPreprocessor + */ class RequestPreprocessor { /** @@ -52,6 +55,7 @@ public function __construct( /** * Auto-redirect to base url (without SID) if the requested url doesn't match it. + * * By default this feature is enabled in configuration. * * @param \Magento\Framework\App\FrontController $subject @@ -72,10 +76,11 @@ public function aroundDispatch( $this->_storeManager->getStore()->isCurrentlySecure() ); if ($baseUrl) { + // phpcs:disable Magento2.Functions.DiscouragedFunction $uri = parse_url($baseUrl); if (!$this->getBaseUrlChecker()->execute($uri, $request)) { $redirectUrl = $this->_url->getRedirectUrl( - $this->_url->getUrl(ltrim($request->getPathInfo(), '/'), ['_nosid' => true]) + $this->_url->getDirectUrl(ltrim($request->getPathInfo(), '/'), ['_nosid' => true]) ); $redirectCode = (int)$this->_scopeConfig->getValue( 'web/url/redirect_to_base', diff --git a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php index 0e158821f1802..a1a99ecd32b89 100644 --- a/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php +++ b/dev/tests/integration/testsuite/Magento/Store/App/FrontController/Plugin/RequestPreprocessorTest.php @@ -14,7 +14,7 @@ use Zend\Stdlib\Parameters; /** - * Class RequestPreprocessorTest @covers \Magento\Store\App\FrontController\Plugin\RequestPreprocessor. + * Tests \Magento\Store\App\FrontController\Plugin\RequestPreprocessor. */ class RequestPreprocessorTest extends \Magento\TestFramework\TestCase\AbstractController { @@ -24,6 +24,28 @@ class RequestPreprocessorTest extends \Magento\TestFramework\TestCase\AbstractCo * @var string */ private $baseUrl; + /** + * @var array; + */ + private $config; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $this->config = []; + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + $this->setConfig($this->config); + parent::tearDown(); + } /** * Test non-secure POST request is redirected right away on completely secure frontend. @@ -62,6 +84,115 @@ public function testHttpsPassSecureLoginPost() $this->setFrontendCompletelySecureRollback(); } + /** + * Test auto redirect to base URL + * + * @param array $config + * @param string $requestUrl + * @param string $redirectUrl + * @dataProvider autoRedirectToBaseURLDataProvider + */ + public function testAutoRedirectToBaseURL(array $config, string $requestUrl, string $redirectUrl) + { + $request = [ + 'REQUEST_SCHEME' => parse_url($requestUrl, PHP_URL_SCHEME), + 'SERVER_NAME' => parse_url($requestUrl, PHP_URL_HOST), + 'SCRIPT_NAME' => '/index.php', + 'SCRIPT_FILENAME' => 'index.php', + 'REQUEST_URI' => parse_url($requestUrl, PHP_URL_PATH), + ]; + $this->setConfig($config); + $this->setServer($request); + $app = $this->_objectManager->create(\Magento\Framework\App\Http::class, ['_request' => $this->getRequest()]); + $this->_response = $app->launch(); + $this->assertRedirect($this->equalTo($redirectUrl)); + } + + /** + * @return array + */ + public function autoRedirectToBaseURLDataProvider(): array + { + $baseConfig = [ + 'web/unsecure/base_url' => 'http://magento.com/us/', + 'web/session/use_frontend_sid' => 0, + 'web/seo/use_rewrites' => 1, + ]; + + return [ + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c/d/e.html', + 'redirectUrl' => 'http://magento.com/us/a/b/c/d/e.html' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c/d.html', + 'redirectUrl' => 'http://magento.com/us/a/b/c/d.html' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c.html', + 'redirectUrl' => 'http://magento.com/us/a/b/c.html' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b.html', + 'redirectUrl' => 'http://magento.com/us/a/b.html' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a.html', + 'redirectUrl' => 'http://magento.com/us/a.html' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c/d/e', + 'redirectUrl' => 'http://magento.com/us/a/b/c/d/e' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c/d', + 'redirectUrl' => 'http://magento.com/us/a/b/c/d' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b/c', + 'redirectUrl' => 'http://magento.com/us/a/b/c' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a/b', + 'redirectUrl' => 'http://magento.com/us/a/b' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/a', + 'redirectUrl' => 'http://magento.com/us/a' + ], + [ + 'config' => $baseConfig, + 'request' => 'http://magento.com/', + 'redirectUrl' => 'http://magento.com/us/' + ], + [ + 'config' => array_merge($baseConfig, ['web/seo/use_rewrites' => 0]), + 'request' => 'http://magento.com/', + 'redirectUrl' => 'http://magento.com/us/index.php/' + ], + [ + 'config' => array_merge($baseConfig, ['web/seo/use_rewrites' => 0]), + 'request' => 'http://magento.com/a/b/c/d.html', + 'redirectUrl' => 'http://magento.com/us/index.php/a/b/c/d.html' + ], + [ + 'config' => array_merge($baseConfig, ['web/seo/use_rewrites' => 0]), + 'request' => 'http://magento.com/a/b/c/d', + 'redirectUrl' => 'http://magento.com/us/index.php/a/b/c/d' + ], + ]; + } + /** * Assert response is redirect with https protocol. * @@ -83,22 +214,26 @@ private function assertResponseRedirect(Response $response, string $redirectUrl) */ private function prepareRequest(bool $isSecure = false) { - $post = new Parameters([ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'login' => [ - 'username' => 'customer@example.com', - 'password' => 'password' + $post = new Parameters( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'login' => [ + 'username' => 'customer@example.com', + 'password' => 'password' + ] ] - ]); + ); $request = $this->getRequest(); $request->setMethod(\Magento\TestFramework\Request::METHOD_POST); $request->setRequestUri('customer/account/loginPost/'); $request->setPost($post); if ($isSecure) { - $server = new Parameters([ - 'HTTPS' => 'on', - 'SERVER_PORT' => 443 - ]); + $server = new Parameters( + [ + 'HTTPS' => 'on', + 'SERVER_PORT' => 443 + ] + ); $request->setServer($server); } @@ -151,4 +286,45 @@ private function setFrontendCompletelySecureRollback() $reinitibleConfig = $this->_objectManager->create(ReinitableConfigInterface::class); $reinitibleConfig->reinit(); } + + private function setConfig(array $config): void + { + foreach ($config as $path => $value) { + $model = $this->_objectManager->create(Value::class); + $model->load($path, 'path'); + if (!isset($this->config[$path])) { + $this->config[$path] = $model->getValue(); + } + if (!$model->getPath()) { + $model->setPath($path); + } + if ($value !== null) { + $model->setValue($value); + $model->save(); + } elseif ($model->getId()) { + $model->delete(); + } + } + $this->_objectManager->create(ReinitableConfigInterface::class)->reinit(); + } + + private function setServer(array $server) + { + $request = $this->getRequest(); + $properties = [ + 'baseUrl', + 'basePath', + 'requestUri', + 'originalPathInfo', + 'pathInfo', + ]; + $reflection = new \ReflectionClass($request); + + foreach ($properties as $name) { + $property = $reflection->getProperty($name); + $property->setAccessible(true); + $property->setValue($request, null); + } + $request->setServer(new Parameters($server)); + } } From a056502ee6110506f81b5f7732cf9d3587f091c4 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 30 Sep 2019 17:25:29 +0300 Subject: [PATCH 0704/2437] MC-19031: Sorting the product grid by custom product attribute sorts by value ID instead of Alphabetically --- .../Mftf/Data/ProductAttributeOptionData.xml | 8 ++ .../Catalog/Test/Mftf/Data/StoreLabelData.xml | 8 ++ ...ductGridFilteringByCustomAttributeTest.xml | 104 ++++++++++++++++++ .../Model/Entity/Attribute/Source/Table.php | 2 +- .../ResourceModel/Entity/Attribute/Option.php | 16 ++- .../Entity/Attribute/Source/TableTest.php | 2 +- 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index bb0e85bcbb40b..a8646a58ae39c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -86,6 +86,14 @@ <data key="label" unique="suffix">White</data> <data key="value" unique="suffix">white</data> </entity> + <entity name="ProductAttributeOption9" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">Blue</data> + <data key="is_default">false</data> + <data key="sort_order">3</data> + <requiredEntity type="StoreLabel">Option11Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option11Store1</requiredEntity> + </entity> <!-- Product attribute options from file "export_import_configurable_product.csv" --> <entity name="ProductAttributeOptionOneForExportImport" extends="productAttributeOption1" type="ProductAttributeOption"> <data key="label">option1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index 0e51995ac72e8..dcd7fde92283c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -72,4 +72,12 @@ <data key="store_id">1</data> <data key="label">Red</data> </entity> + <entity name="Option11Store0" type="StoreLabel"> + <data key="store_id">0</data> + <data key="label">Blue</data> + </entity> + <entity name="Option11Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">Blue</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml new file mode 100644 index 0000000000000..b0832b8f5944c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductGridFilteringByCustomAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product grid"/> + <title value="Sorting the product grid by custom product attribute"/> + <description value="Sorting the product grid by custom product attribute should sort Alphabetically instead of value id"/> + <severity value="MAJOR"/> + <useCaseId value="MC-19031"/> + <testCaseId value="MC-20329"/> + <group value="catalog"/> + </annotations> + <before> + <!--Login as admin and delete all products --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="deleteAllProductsUsingProductGrid" stepKey="deleteAllProducts"/> + <!--Create dropdown product attribute--> + <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> + <!--Create attribute options--> + <createData entity="ProductAttributeOption7" stepKey="createFirstProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="ProductAttributeOption8" stepKey="createSecondProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="ProductAttributeOption9" stepKey="createThirdProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <!--Add attribute to default attribute set--> + <createData entity="AddToDefaultSet" stepKey="addAttributeToDefaultSet"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Create 3 products--> + <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSecondProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createThirdProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Update first product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForFirstProduct"> + <argument name="product" value="$$createFirstProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editFirstProduct"> + <argument name="product" value="$$createFirstProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createFirstProductAttributeOption.option[store_labels][0][label]$$" stepKey="setFirstAttributeValue"/> + <actionGroup ref="saveProductForm" stepKey="saveFirstProduct"/> + <!--Update second product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSecondProduct"> + <argument name="product" value="$$createSecondProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editSecondProduct"> + <argument name="product" value="$$createSecondProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createSecondProductAttributeOption.option[store_labels][0][label]$$" stepKey="setSecondAttributeValue"/> + <actionGroup ref="saveProductForm" stepKey="saveSecondProduct"/> + <!--Update third product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForThirdProduct"> + <argument name="product" value="$$createThirdProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editThirdProduct"> + <argument name="product" value="$$createThirdProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createThirdProductAttributeOption.option[store_labels][0][label]$$" stepKey="setThirdAttributeValue"/> + <actionGroup ref="saveProductForm" stepKey="saveThirdProduct"/> + </before> + <after> + <!--Delete products--> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> + <!--Delete attribute--> + <deleteData createDataKey="createDropdownAttribute" stepKey="deleteDropdownAttribute"/> + <!--Delete category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="NavigateToAndResetProductGridToDefaultViewAfterTest"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductGridFilters"/> + <!--Sort by custom attribute DESC using grabbed value--> + <conditionalClick selector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" dependentSelector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" visible="true" stepKey="ascendSortByCustomAttribute"/> + <waitForPageLoad stepKey="waitForProductGridLoad"/> + <!--Check products sorting. Expected result => Blue-Green-Red --> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createSecondProduct.name$$)}}" userInput="$$createSecondProduct.name$$" stepKey="seeSecondProductName"/> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createFirstProduct.name$$)}}" userInput="$$createFirstProduct.name$$" stepKey="seeFirstProductName"/> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createThirdProduct.name$$)}}" userInput="$$createThirdProduct.name$$" stepKey="seeThirdProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index f9aa1a9ed3ba1..46e4b665cabdf 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -213,7 +213,7 @@ public function addValueSortToCollection($collection, $dir = \Magento\Framework\ $valueExpr ); - $collection->getSelect()->order("{$attribute->getAttributeCode()} {$dir}"); + $collection->getSelect()->order("{$attribute->getAttributeCode()}_value {$dir}"); return $this; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php index 79c277dcb6a82..5a110a45d5805 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php @@ -42,10 +42,13 @@ public function addOptionValueToCollection($collection, $attribute, $valueExpr) "{$optionTable2}.option_id={$valueExpr} AND {$optionTable2}.store_id=?", $collection->getStoreId() ); - $valueExpr = $connection->getCheckSql( - "{$optionTable2}.value_id IS NULL", - "{$optionTable1}.option_id", - "{$optionTable2}.option_id" + $valueIdExpr = $connection->getIfNullSql( + "{$optionTable2}.option_id", + "{$optionTable1}.option_id" + ); + $valueExpr = $connection->getIfNullSql( + "{$optionTable2}.value", + "{$optionTable1}.value" ); $collection->getSelect()->joinLeft( @@ -55,7 +58,10 @@ public function addOptionValueToCollection($collection, $attribute, $valueExpr) )->joinLeft( [$optionTable2 => $this->getTable('eav_attribute_option_value')], $tableJoinCond2, - [$attributeCode => $valueExpr] + [ + $attributeCode => $valueIdExpr, + $attributeCode . '_value' => $valueExpr, + ] ); return $this; diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php index b68446d22f910..e61a7ebb862a9 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php @@ -314,7 +314,7 @@ public function testAddValueSortToCollection() $attrOption->expects($this->once())->method('addOptionValueToCollection') ->with($collection, $this->abstractAttributeMock, $expr) ->willReturnSelf(); - $select->expects($this->once())->method('order')->with("{$attributeCode} {$dir}"); + $select->expects($this->once())->method('order')->with("{$attributeCode}_value {$dir}"); $this->assertEquals($this->model, $this->model->addValueSortToCollection($collection, $dir)); } From 20d35fb7881c44446ba2c21d4630522f80f388c3 Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Mon, 30 Sep 2019 10:23:18 -0500 Subject: [PATCH 0705/2437] MC-20336: Update tests related to Catalog Search and Swatches "Reverting previous changes committed in e41a8166b2b48cdd88444ba4d2a9935d04fde2f8 as they fail in both mainline and elastic search branch" --- .../Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml | 3 --- .../Test/Mftf/Section/CatalogSearchAdminConfigSection.xml | 1 - 2 files changed, 4 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml index e1c2a7867850f..a6e3dfd7eaad4 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml @@ -25,9 +25,6 @@ <see userInput="{{MinMaxQueryLength.Hint}}" selector="{{AdminCatalogSearchConfigurationSection.maxQueryLengthHint}}" stepKey="seeHint2"/> <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.minQueryLengthInherit}}" stepKey="uncheckSystemValue"/> <fillField selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" userInput="{{minLength}}" stepKey="setMinQueryLength"/> - <waitForPageLoad stepKey="waitForTextToBeFilled"/> - <!--Scrolling till Category Permissions to make sure the collapse tab for Catalog Search is visible--> - <scrollTo selector="{{AdminCatalogSearchConfigurationSection.categoryPermissions}}" stepKey="scrollToCatalogSearchTab1"/> <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseTab"/> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml index ff4c0fead0ee4..e82ad4670f9b3 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml @@ -7,7 +7,6 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCatalogSearchConfigurationSection"> - <element name="categoryPermissions" type="button" selector="#catalog_magento_catalogpermissions-head"/> <element name="catalogSearchTab" type="button" selector="#catalog_search-head"/> <element name="checkIfCatalogSearchTabExpand" type="button" selector="#catalog_search-head:not(.open)"/> <element name="searchEngineDefaultSystemValue" type="checkbox" selector="#catalog_search_engine_inherit"/> From 343df48cdf1f17801ca613b549e071780d8fc97b Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 26 Sep 2019 10:39:42 +0400 Subject: [PATCH 0706/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-11332 --- ...atSomeAttributesChangedValueToEmptyAfterImportTest.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 50573faf9860a..af893b1e29e34 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -29,12 +29,10 @@ <requiredEntity createDataKey="productAttribute"/> </createData> <!--Create category--> - <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="_defaultCategory" stepKey="createCategory"/> </before> <after> <!--Delete Product and Category--> - <comment userInput="Delete Product and Category" stepKey="deleteProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> @@ -42,12 +40,10 @@ </actionGroup> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!--Delete attribute--> - <comment userInput="Delete attribute" stepKey="deleteAttribute"/> <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Create product--> - <comment userInput="Create product" stepKey="commentCreateProduct"/> <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"/> <actionGroup ref="fillMainProductForm" stepKey="fillProductFieldsInAdmin"> <argument name="product" value="simpleProductWithShortNameAndSku"/> @@ -56,25 +52,21 @@ <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> <!--Select created attribute--> - <comment userInput="Select created attribute" stepKey="selectedCreatedAttribute"/> <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="$$productAttribute.attribute_code$$"/> </actionGroup> <!--Check that attribute value is selected--> - <comment userInput="Check that attribute value is selected" stepKey="checkSelectedValue"/> <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle1"/> <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab1"/> <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="option2" stepKey="seeAttributeValueIsSelected1"/> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!--Import product with add/update behavior--> - <comment userInput="Import products with add/update behavior" stepKey="importProduct"/> <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="import_simple_product.csv"/> <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> </actionGroup> <!--Check that attribute value is empty after import--> - <comment userInput="Check that attribute value is empty after import" stepKey="checkAttrValueAfterImport"/> <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> <argument name="productSku" value="{{simpleProductWithShortNameAndSku.sku}}"/> </actionGroup> From 85bf559df58809ba8fda4e6947b0a633edf9c9b6 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 26 Sep 2019 11:23:29 +0400 Subject: [PATCH 0707/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6406 --- ...tVisibilityDifferentStoreViewsAfterImportTest.xml | 12 +++--------- app/code/Magento/Store/Test/Mftf/Data/StoreData.xml | 9 --------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml index 175a575acb188..fe7960324c0c3 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -22,10 +22,9 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create English and Chinese store views--> - <comment userInput="Create English and Chinese store views" stepKey="commentCreateTwoStoreViews"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnglishStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> - <argument name="customStore" value="storeViewEnglish"/> + <argument name="customStore" value="customStoreEN"/> </actionGroup> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createChineseStoreView"> <argument name="StoreGroup" value="_defaultStoreGroup"/> @@ -34,7 +33,6 @@ </before> <after> <!--Delete all imported products--> - <comment userInput="Delete all imported products" stepKey="commentDeleteProducts"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> <actionGroup ref="adminDataGridSelectPerPage" stepKey="selectNumberOfProductsPerPage"> @@ -42,9 +40,8 @@ </actionGroup> <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> <!--Delete store views--> - <comment userInput="Delete store views" stepKey="commentDeleteStoreViews"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteEnglishStoreView"> - <argument name="customStore" value="storeViewEnglish"/> + <argument name="customStore" value="customStoreEN"/> </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteChineseStoreView"> <argument name="customStore" value="storeViewChinese"/> @@ -52,14 +49,12 @@ <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Import products from file--> - <comment userInput="Import products from file" stepKey="commentImportProducts"/> <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="import_productsoftwostoresdata.csv"/> <argument name="importMessage" value="Created: 2, Updated: 0, Deleted: 0"/> </actionGroup> <!--Open imported name4 product--> - <comment userInput="Open imported name4 product" stepKey="commentOpenName4Product"/> <actionGroup ref="filterAndSelectProduct" stepKey="openName4Product"> <argument name="productSku" value="name4"/> </actionGroup> @@ -70,9 +65,8 @@ </actionGroup> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForChineseStore"/> <!--Switch English store view and assert visibility field--> - <comment userInput="Switch English store view and assert visibility field" stepKey="commentAssertVisibilityEnglishView"/> <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomEnglishView"> - <argument name="storeViewName" value="{{storeViewEnglish.name}}"/> + <argument name="storeViewName" value="{{customStoreEN.name}}"/> </actionGroup> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForEnglishView"/> </test> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index b0c3905c66dde..8198e87062e98 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -194,15 +194,6 @@ <data key="name">third_store_view</data> <data key="code">third_store_view</data> </entity> - <entity name="storeViewEnglish" type="store"> - <data key="group_id">1</data> - <data key="name">English</data> - <data key="code">english</data> - <data key="is_active">1</data> - <data key="store_id">null</data> - <data key="store_type">store</data> - <data key="store_action">add</data> - </entity> <entity name="storeViewChinese" type="store"> <data key="group_id">1</data> <data key="name">Chinese</data> From 95373f6833a0bb8e507a4c3bccbd29b8c698805f Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 26 Sep 2019 11:36:35 +0400 Subject: [PATCH 0708/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6317 --- ...nURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml index 42af7f67ca4ee..c395f607fb30a 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -21,7 +21,6 @@ </annotations> <before> <!--Create Product--> - <comment userInput="Create Product" stepKey="commentCreateProduct"/> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProductBeforeUpdate" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> @@ -30,20 +29,17 @@ </before> <after> <!--Delete created data--> - <comment userInput="Delete created data" stepKey="commentDeleteData"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Import product from CSV file--> - <comment userInput="Import product from CSV file" stepKey="commentImportProduct"/> <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProduct"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="simpleProductUpdate.csv"/> <argument name="importMessage" value="Created: 0, Updated: 1, Deleted: 0"/> </actionGroup> <!--Assert product's updated url--> - <comment userInput="Assert product's updated url" stepKey="commentAssertUrl"/> <amOnPage url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="navigateToProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> <seeInCurrentUrl url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="seeUpdatedUrl"/> From 5d730a109b61de6e0fb535aeddd752e01f1aa45c Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Thu, 26 Sep 2019 12:25:43 +0400 Subject: [PATCH 0709/2437] MC-18824: Increase test coverage for Import / export functional area - Automation test for MC-6416 --- .../ActionGroup/AdminImportProductsActionGroup.xml | 12 ------------ .../Test/AdminImportCSVWithSpecialCharactersTest.xml | 6 +++--- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index faa66886178dd..9063916e9f502 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -60,16 +60,4 @@ <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> </actionGroup> - <actionGroup name="AdminCheckDataForImportProductsActionGroup" extends="AdminImportProductsActionGroup"> - <arguments> - <argument name="noteMessage" type="string" defaultValue="File must be saved in UTF-8 encoding for proper import"/> - </arguments> - <remove keyForRemoval="clickImportButton"/> - <remove keyForRemoval="AdminImportMainSectionLoad2"/> - <remove keyForRemoval="assertSuccessMessage"/> - <remove keyForRemoval="AdminMessagesSection"/> - <remove keyForRemoval="seeImportMessage"/> - <see selector="{{AdminImportHeaderSection.messageNote}}" userInput="{{noteMessage}}" after="attachFileForImport" stepKey="seeNoteMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="{{importMessage}}" stepKey="seeSuccessMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml index f752cb3e7c908..38c1a09dc534c 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml @@ -25,11 +25,11 @@ <after> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> - <actionGroup ref="AdminCheckDataForImportProductsActionGroup" stepKey="adminImportProducts"> + <actionGroup ref="AdminCheckDataForImportProductActionGroup" stepKey="adminImportProducts"> <argument name="behavior" value="Add/Update"/> <argument name="importFile" value="importSpecChars.csv"/> - <argument name="importMessage" value='File is valid! To start import process press "Import" button'/> - <argument name="noteMessage" value="File must be saved in UTF-8 encoding for proper import"/> </actionGroup> + <see selector="{{AdminImportHeaderSection.messageNote}}" userInput='File must be saved in UTF-8 encoding for proper import' stepKey="seeNoteMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> </test> </tests> From b32f90fe98553ae259ca51bf2a3433f817cde8df Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Wed, 25 Sep 2019 13:38:47 +0300 Subject: [PATCH 0710/2437] MC-18824: Increase test coverage for Import / export functional area - Integration test for MC-6348 : CR comments fix. --- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index f62c84eea4057..ba5fa3816d0be 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -862,7 +862,7 @@ public function testSaveHiddenImages() $hiddenImages = array_filter( $images, static function (DataObject $image) { - return $image->getDisabled() == 1; + return $image->getDisabled() === 1; } ); From 0669c8806a8ea388e1a5f192e1aac9049cd7ba3c Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Mon, 30 Sep 2019 14:44:46 -0500 Subject: [PATCH 0711/2437] MC-20337: Update tests related to Checkout, Configurable, Layered Navigation --- .../Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml index c599a6a23f190..e8d6434dabd3f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml @@ -87,6 +87,9 @@ <see selector="{{AdminProductFormConfigurationsSection.currentVariationsSkuCells}}" userInput="LongSku-$$getConfigAttributeOption2.label$$" stepKey="seeChildProductSku2"/> <see selector="{{AdminProductFormConfigurationsSection.currentVariationsPriceCells}}" userInput="{{ProductWithLongNameSku.price}}" stepKey="seeConfigurationsPrice"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Assert storefront category list page--> <amOnPage url="/" stepKey="amOnStorefront"/> <waitForPageLoad stepKey="waitForStorefrontLoad"/> From c15e194a9c5395d6cc703813feea8ed6efc4f6d5 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 30 Sep 2019 15:28:18 -0500 Subject: [PATCH 0712/2437] MAGETWO-99043: Shipping Methods Don't Load In IE11 --- .../frontend/web/js/model/address-converter.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js index 9b20a782c38d9..c114b795a80b4 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/address-converter.js @@ -9,8 +9,9 @@ define([ 'jquery', 'Magento_Checkout/js/model/new-customer-address', 'Magento_Customer/js/customer-data', - 'mage/utils/objects' -], function ($, address, customerData, mageUtils) { + 'mage/utils/objects', + 'underscore' +], function ($, address, customerData, mageUtils, _) { 'use strict'; var countryData = customerData.get('directory-data'); @@ -59,13 +60,15 @@ define([ delete addressData['region_id']; if (addressData['custom_attributes']) { - addressData['custom_attributes'] = Object.entries(addressData['custom_attributes']) - .map(function (customAttribute) { + addressData['custom_attributes'] = _.map( + addressData['custom_attributes'], + function (value, key) { return { - 'attribute_code': customAttribute[0], - 'value': customAttribute[1] + 'attribute_code': key, + 'value': value }; - }); + } + ); } return address(addressData); From c4cf138b54ee21a936898bcbdb1fc34d8807c83b Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 30 Sep 2019 15:58:39 -0500 Subject: [PATCH 0713/2437] MC-18685: Remove custom layout updates from admin --- .../Backend/AbstractLayoutUpdate.php | 23 ++-- .../Attribute/Backend/Customlayoutupdate.php | 12 +- .../Attribute/Source/LayoutUpdate.php | 5 +- .../Catalog/Model/Category/Authorization.php | 8 +- .../Product/Attribute/Source/LayoutUpdate.php | 5 +- .../Catalog/Model/Product/Authorization.php | 22 ++-- .../Controller/Adminhtml/CategoryTest.php | 26 ++++- .../Controller/Adminhtml/ProductTest.php | 3 + .../Backend/AbstractLayoutUpdateTest.php | 109 ++++++++++++++++++ .../Backend/CustomlayoutupdateTest.php | 34 ------ 10 files changed, 177 insertions(+), 70 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php index 6aedd509af8e0..d5f1aeef5d913 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php @@ -19,6 +19,8 @@ abstract class AbstractLayoutUpdate extends AbstractBackend { public const VALUE_USE_UPDATE_XML = '__existing__'; + public const VALUE_NO_UPDATE = '__no_update__'; + /** * Extract attribute value. * @@ -52,16 +54,11 @@ private function prepareValue(AbstractModel $model): ?string $value = $this->extractAttributeValue($model); if ($value && $value !== self::VALUE_USE_UPDATE_XML + && $value !== self::VALUE_NO_UPDATE && !in_array($value, $this->listAvailableValues($model), true) ) { throw new LocalizedException(__('Selected layout update is not available')); } - if ($value === self::VALUE_USE_UPDATE_XML) { - $value = null; - } - if (!$value) { - $value = null; - } return $value; } @@ -71,11 +68,12 @@ private function prepareValue(AbstractModel $model): ?string * * @param string|null $value * @param AbstractModel $forObject + * @param string|null $attrCode * @return void */ - private function setAttributeValue(?string $value, AbstractModel $forObject): void + private function setAttributeValue(?string $value, AbstractModel $forObject, ?string $attrCode = null): void { - $attrCode = $this->getAttribute()->getAttributeCode(); + $attrCode = $attrCode ?? $this->getAttribute()->getAttributeCode(); if ($forObject->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { $forObject->setCustomAttribute($attrCode, $value); } @@ -104,7 +102,14 @@ public function validate($object) */ public function beforeSave($object) { - $this->setAttributeValue($this->prepareValue($object), $object); + $value = $this->prepareValue($object); + if ($value && ($value === self::VALUE_NO_UPDATE || $value !== self::VALUE_USE_UPDATE_XML)) { + $this->setAttributeValue(null, $object, 'custom_layout_update'); + } + if (!$value || $value === self::VALUE_USE_UPDATE_XML || $value === self::VALUE_NO_UPDATE) { + $value = null; + } + $this->setAttributeValue($value, $object); return $this; } diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index d3cdb7c545cbc..f9a4473c2c93f 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -38,10 +38,9 @@ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) * Extract an attribute value. * * @param AbstractModel $object - * @param string|null $attributeCode * @return mixed */ - private function extractValue(AbstractModel $object, ?string $attributeCode = null) + private function extractValue(AbstractModel $object) { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); $value = $object->getData($attributeCode); @@ -98,14 +97,7 @@ public function beforeSave($object) { //Validate first, validation might have been skipped. $this->validate($object); - $value = $this->extractValue($object); - //If custom file was selected we need to remove this attribute - $file = $this->extractValue($object, 'custom_layout_update_file'); - if ($file && $file !== AbstractLayoutUpdate::VALUE_USE_UPDATE_XML) { - $this->putValue($object, null); - } else { - $this->putValue($object, $value); - } + $this->putValue($object, $this->extractValue($object)); return parent::beforeSave($object); } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php index 648fe2c57290d..d3012b1b49587 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -13,6 +13,7 @@ use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as Backend; /** * List of layout updates available for a category. @@ -42,7 +43,7 @@ public function __construct(LayoutUpdateManager $manager) */ public function getAllOptions() { - $default = ''; + $default = Backend::VALUE_NO_UPDATE; $defaultText = 'No update'; $this->optionsText[$default] = $defaultText; @@ -70,7 +71,7 @@ public function getOptionsFor(CustomAttributesDataInterface $entity): array { $options = $this->getAllOptions(); if ($entity->getCustomAttribute('custom_layout_update')) { - $existingValue = \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + $existingValue = Backend::VALUE_USE_UPDATE_XML; $existingLabel = 'Use existing'; $options[] = ['label' => $existingLabel, 'value' => $existingValue]; $this->optionsText[$existingValue] = $existingLabel; diff --git a/app/code/Magento/Catalog/Model/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php index 5529b067df3a9..984adaae387f4 100644 --- a/app/code/Magento/Catalog/Model/Category/Authorization.php +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -11,10 +11,10 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\Category as CategoryModel; use Magento\Catalog\Model\CategoryFactory; -use Magento\Eav\Api\Data\AttributeInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; /** * Additional authorization for category operations. @@ -63,7 +63,11 @@ private function hasChanges(CategoryModel $category, ?CategoryModel $oldCategory $oldValues[] = $designAttribute->getDefaultValue(); } $newValue = $category->getData($designAttribute->getAttributeCode()); - if (empty($newValue)) { + if (empty($newValue) + || ($designAttribute->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { $newValue = null; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php index 78e29002beb25..467bbfc629020 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -12,6 +12,7 @@ use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate as Backend; /** * List of layout updates available for a product. @@ -41,7 +42,7 @@ public function __construct(LayoutUpdateManager $manager) */ public function getAllOptions() { - $default = ''; + $default = Backend::VALUE_NO_UPDATE; $defaultText = 'No update'; $this->optionsText[$default] = $defaultText; @@ -67,7 +68,7 @@ public function getOptionsFor(CustomAttributesDataInterface $entity): array { $options = $this->getAllOptions(); if ($entity->getCustomAttribute('custom_layout_update')) { - $existingValue = \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + $existingValue = Backend::VALUE_USE_UPDATE_XML; $existingLabel = 'Use existing'; $options[] = ['label' => $existingLabel, 'value' => $existingValue]; $this->optionsText[$existingValue] = $existingLabel; diff --git a/app/code/Magento/Catalog/Model/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php index 2500330e14968..41af459da9887 100644 --- a/app/code/Magento/Catalog/Model/Product/Authorization.php +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -14,6 +14,7 @@ use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate; /** * Additional authorization for product operations. @@ -58,12 +59,13 @@ private function hasProductChanged(ProductModel $product, ?ProductModel $oldProd 'custom_design_to', 'custom_layout_update_file' ]; - $attributes = null; - if (!$oldProduct) { - //For default values. - $attributes = $product->getAttributes(); - } + $attributes = $product->getAttributes(); + foreach ($designAttributes as $designAttribute) { + $attribute = $attributes[$designAttribute]; + if (!array_key_exists($designAttribute, $attributes)) { + continue; + } $oldValues = [null]; if ($oldProduct) { //New value may only be the saved value @@ -71,12 +73,16 @@ private function hasProductChanged(ProductModel $product, ?ProductModel $oldProd if (empty($oldValues[0])) { $oldValues[0] = null; } - } elseif (array_key_exists($designAttribute, $attributes)) { + } else { //New value can be empty or default - $oldValues[] = $attributes[$designAttribute]->getDefaultValue(); + $oldValues[] = $attribute->getDefaultValue(); } $newValue = $product->getData($designAttribute); - if (empty($newValue)) { + if (empty($newValue) + || ($attribute->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { $newValue = null; } if (!in_array($newValue, $oldValues, true)) { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index b1cbc1f544109..c6e1faadd1013 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -628,7 +628,7 @@ public function testSaveDesign(): void //Trying again with the permissions. $requestData['custom_layout_update_file'] = null; - $requestData['custom_design'] = 'test-theme'; + $requestData['page_layout'] = '2columns-left'; $this->aclBuilder->getAcl() ->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); $this->getRequest()->setDispatched(false); @@ -639,8 +639,28 @@ public function testSaveDesign(): void /** @var CategoryModel $category */ $category = $this->categoryFactory->create(); $category->load(2); - $this->assertNotEmpty($category->getCustomDesign()); - $this->assertEquals('test-theme', $category->getCustomDesign()); + $this->assertEquals('2columns-left', $category->getData('page_layout')); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying to save special value without the permissions. + $requestData['custom_layout_update_file'] = CategoryModel\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + $requestData['description'] = 'test'; + $this->aclBuilder->getAcl()->deny(null, ['Magento_Catalog::edit_category_design']); + $this->getRequest()->setDispatched(false); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->assertEquals('2columns-left', $category->getData('page_layout')); + $this->assertEmpty($category->getData('custom_layout_update_file')); + $this->assertEquals('test', $category->getData('description')); //No new error messages $this->assertSessionMessages( self::equalTo($sessionMessages), diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index d6f82ccaea648..1f2569a6ffdb6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate; use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; @@ -485,6 +486,8 @@ public function testSaveDesignWithDefaults(): void //Updating product's design settings without proper permissions. $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); + //Testing that special "No Update" value is treated as no change. + $requestData['product']['custom_layout_update_file'] = LayoutUpdate::VALUE_NO_UPDATE; $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($requestData); $this->dispatch($uri); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php new file mode 100644 index 0000000000000..b4022dc147b36 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Backend; + +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Category; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; + +/** + * Test 'custom layout file' attribute. + */ +class AbstractLayoutUpdateTest extends TestCase +{ + /** + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * @var AbstractLayoutUpdate + */ + private $attribute; + + /** + * @var Category + */ + private $category; + /** + * @var CategoryLayoutUpdateManager + */ + private $layoutManager; + + /** + * Recreate the category model. + * + * @return void + */ + private function recreateCategory(): void + { + $this->category = $this->categoryFactory->create(); + $this->category->load(2); + } + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryFactory::class); + $this->recreateCategory(); + $this->attribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); + $this->layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + } + + /** + * Check that custom layout update file's values erase the old attribute's value. + * + * @return void + * @throws \Throwable + */ + public function testDependsOnNewUpdate(): void + { + //New selected file value is set + $this->layoutManager->setCategoryFakeFiles(2, ['new']); + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setCustomAttribute('custom_layout_update_file', 'new'); + $this->attribute->beforeSave($this->category); + $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); + $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + $this->assertEmpty($this->category->getData('custom_layout_update')); + $this->assertEquals('new', $this->category->getData('custom_layout_update_file')); + + //Existing update chosen + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML + ); + $this->attribute->beforeSave($this->category); + $this->assertEquals('test', $this->category->getData('custom_layout_update')); + /** @var AbstractBackend $fileAttribute */ + $fileAttribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); + $fileAttribute->beforeSave($this->category); + $this->assertEquals(null, $this->category->getData('custom_layout_update_file')); + + //Removing custom layout update by explicitly selecting the new file (or an empty file). + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_NO_UPDATE + ); + $this->attribute->beforeSave($this->category); + $this->assertEmpty($this->category->getData('custom_layout_update')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index 7447950ea2ab6..a340094b01040 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -95,38 +95,4 @@ public function testImmutable(): void $this->attribute->beforeSave($this->category); $this->assertNull($this->category->getCustomAttribute('custom_layout_update')->getValue()); } - - /** - * Check that custom layout update file's values erase the old attribute's value. - * - * @return void - * @throws \Throwable - */ - public function testDependsOnNewUpdate(): void - { - //New selected file value is set - $this->category->setCustomAttribute('custom_layout_update', 'test'); - $this->category->setOrigData('custom_layout_update', 'test'); - $this->category->setCustomAttribute('custom_layout_update_file', 'new'); - $this->attribute->beforeSave($this->category); - $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); - $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); - $this->assertEmpty($this->category->getData('custom_layout_update')); - $this->assertEquals('new', $this->category->getData('custom_layout_update_file')); - - //Existing update chosen - $this->recreateCategory(); - $this->category->setData('custom_layout_update', 'test'); - $this->category->setOrigData('custom_layout_update', 'test'); - $this->category->setData( - 'custom_layout_update_file', - \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML - ); - $this->attribute->beforeSave($this->category); - $this->assertEquals('test', $this->category->getData('custom_layout_update')); - /** @var AbstractBackend $fileAttribute */ - $fileAttribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); - $fileAttribute->beforeSave($this->category); - $this->assertEquals(null, $this->category->getData('custom_layout_update_file')); - } } From 3b3531a5bb06135e807bcf589a2a209094342dfe Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Mon, 30 Sep 2019 16:04:02 -0500 Subject: [PATCH 0714/2437] MC-20536: Make remaining failed MTF tests running with MySQL only --- .../InjectableTests/elastic_search.xml | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/elastic_search.xml diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/elastic_search.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/elastic_search.xml new file mode 100644 index 0000000000000..6141151518332 --- /dev/null +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/elastic_search.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../vendor/magento/mtf/Magento/Mtf/TestRunner/etc/testRunner.xsd"> + <rule scope="testcase"> + <deny> + <tag group="stable" value="no" /> + <tag group="to_maintain" value="yes" /> + <tag group="mftf_migrated" value="yes" /> + </deny> + </rule> + <rule scope="testsuite"> + <deny> + <module value="Magento_Setup" strict="1" /> + <module value="Magento_SampleData" strict="1" /> + </deny> + </rule> + <rule scope="variation"> + <deny> + <tag group="test_type" value="3rd_party_test, 3rd_party_test_single_flow, mysql_search" /> + <tag group="stable" value="no" /> + <tag group="mftf_migrated" value="yes" /> + <tag group="to_maintain" value="yes" /> + </deny> + </rule> +</config> From 502d526817e431706eeca656e3b92edcda75b174 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 30 Sep 2019 19:42:41 -0500 Subject: [PATCH 0715/2437] MC-18685: Remove custom layout updates from admin --- .../Backend/AbstractLayoutUpdate.php | 3 + .../Attribute/Backend/Customlayoutupdate.php | 2 +- .../Catalog/Model/Category/Authorization.php | 90 +++++++++++++----- .../Catalog/Model/Product/Authorization.php | 91 ++++++++++++++----- .../Catalog/Api/CategoryRepositoryTest.php | 15 ++- .../Api/ProductRepositoryInterfaceTest.php | 16 +++- .../Backend/AbstractLayoutUpdateTest.php | 12 +++ .../Backend/CustomlayoutupdateTest.php | 2 +- .../Model/Category/DataProviderTest.php | 12 ++- .../Form/Modifier/LayoutUpdateTest.php | 12 ++- 10 files changed, 196 insertions(+), 59 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php index d5f1aeef5d913..1aa7ab7e5880f 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php @@ -52,6 +52,9 @@ abstract protected function listAvailableValues(AbstractModel $forModel): array; private function prepareValue(AbstractModel $model): ?string { $value = $this->extractAttributeValue($model); + if (!is_string($value)) { + $value = null; + } if ($value && $value !== self::VALUE_USE_UPDATE_XML && $value !== self::VALUE_NO_UPDATE diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index f9a4473c2c93f..759bdb54439b6 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -44,7 +44,7 @@ private function extractValue(AbstractModel $object) { $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); $value = $object->getData($attributeCode); - if (!$value) { + if (!$value || !is_string($value)) { $value = null; } diff --git a/app/code/Magento/Catalog/Model/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php index 984adaae387f4..407ce2c045b25 100644 --- a/app/code/Magento/Catalog/Model/Category/Authorization.php +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\Category as CategoryModel; use Magento\Catalog\Model\CategoryFactory; +use Magento\Eav\Api\Data\AttributeInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; use Magento\Framework\Exception\NoSuchEntityException; @@ -41,6 +42,61 @@ public function __construct(AuthorizationInterface $authorization, CategoryFacto $this->categoryFactory = $factory; } + /** + * Extract attribute value from the model. + * + * @param CategoryModel $category + * @param AttributeInterface $attr + * @throws \RuntimeException When no new value is present. + * @return mixed + */ + private function extractAttributeValue(CategoryModel $category, AttributeInterface $attr) + { + if ($category->hasData($attr->getAttributeCode())) { + $newValue = $category->getData($attr->getAttributeCode()); + } elseif ($category->hasData(CategoryModel::CUSTOM_ATTRIBUTES) + && $attrValue = $category->getCustomAttribute($attr->getAttributeCode()) + ) { + $newValue = $attrValue->getValue(); + } else { + throw new \RuntimeException('New value is not set'); + } + + if (empty($newValue) + || ($attr->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { + $newValue = null; + } + + return $newValue; + } + + /** + * Find values to compare the new one. + * + * @param AttributeInterface $attribute + * @param CategoryModel|null $oldCategory + * @return mixed[] + */ + private function fetchOldValue(AttributeInterface $attribute, ?CategoryModel $oldCategory): array + { + $oldValues = [null]; + if ($oldCategory) { + //New value must match saved value exactly + $oldValues = [$oldCategory->getData($attribute->getAttributeCode())]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } + } else { + //New value can be either empty or default value. + $oldValues[] = $attribute->getDefaultValue(); + } + + return $oldValues; + } + /** * Determine whether a category has design properties changed. * @@ -51,24 +107,12 @@ public function __construct(AuthorizationInterface $authorization, CategoryFacto private function hasChanges(CategoryModel $category, ?CategoryModel $oldCategory): bool { foreach ($category->getDesignAttributes() as $designAttribute) { - $oldValues = [null]; - if ($oldCategory) { - //New value must match saved value exactly - $oldValues = [$oldCategory->getData($designAttribute->getAttributeCode())]; - if (empty($oldValues[0])) { - $oldValues[0] = null; - } - } else { - //New value can be either empty or default value. - $oldValues[] = $designAttribute->getDefaultValue(); - } - $newValue = $category->getData($designAttribute->getAttributeCode()); - if (empty($newValue) - || ($designAttribute->getBackend() instanceof LayoutUpdate - && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) - ) - ) { - $newValue = null; + $oldValues = $this->fetchOldValue($designAttribute, $oldCategory); + try { + $newValue = $this->extractAttributeValue($category, $designAttribute); + } catch (\RuntimeException $exception) { + //No new value + continue; } if (!in_array($newValue, $oldValues, true)) { @@ -94,9 +138,13 @@ public function authorizeSavingOf(CategoryInterface $category): void if ($category->getId()) { /** @var CategoryModel $savedCategory */ $savedCategory = $this->categoryFactory->create(); - $savedCategory->load($category->getId()); - if (!$savedCategory->getName()) { - throw NoSuchEntityException::singleField('id', $category->getId()); + if ($category->getOrigData()) { + $savedCategory->setData($category->getOrigData()); + } else { + $savedCategory->load($category->getId()); + if (!$savedCategory->getName()) { + throw NoSuchEntityException::singleField('id', $category->getId()); + } } } diff --git a/app/code/Magento/Catalog/Model/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php index 41af459da9887..13147d5787bd1 100644 --- a/app/code/Magento/Catalog/Model/Product/Authorization.php +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product as ProductModel; use Magento\Catalog\Model\ProductFactory; +use Magento\Eav\Api\Data\AttributeInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Exception\AuthorizationException; use Magento\Framework\Exception\NoSuchEntityException; @@ -41,6 +42,60 @@ public function __construct(AuthorizationInterface $authorization, ProductFactor $this->productFactory = $factory; } + /** + * Extract attribute value from the model. + * + * @param ProductModel $product + * @param AttributeInterface $attr + * @return mixed + * @throws \RuntimeException When no new value is present. + */ + private function extractAttributeValue(ProductModel $product, AttributeInterface $attr) + { + if ($product->hasData($attr->getAttributeCode())) { + $newValue = $product->getData($attr->getAttributeCode()); + } elseif ($product->hasData(ProductModel::CUSTOM_ATTRIBUTES) + && $attrValue = $product->getCustomAttribute($attr->getAttributeCode()) + ) { + $newValue = $attrValue->getValue(); + } else { + throw new \RuntimeException('No new value is present'); + } + + if (empty($newValue) + || ($attr->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { + $newValue = null; + } + + return $newValue; + } + + /** + * Prepare old values to compare to. + * + * @param AttributeInterface $attribute + * @param ProductModel|null $oldProduct + * @return array + */ + private function fetchOldValues(AttributeInterface $attribute, ?ProductModel $oldProduct): array + { + if ($oldProduct) { + //New value may only be the saved value + $oldValues = [$oldProduct->getData($attribute->getAttributeCode())]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } + } else { + //New value can be empty or default + $oldValues[] = $attribute->getDefaultValue(); + } + + return $oldValues; + } + /** * Check whether the product has changed. * @@ -62,28 +117,16 @@ private function hasProductChanged(ProductModel $product, ?ProductModel $oldProd $attributes = $product->getAttributes(); foreach ($designAttributes as $designAttribute) { - $attribute = $attributes[$designAttribute]; if (!array_key_exists($designAttribute, $attributes)) { continue; } - $oldValues = [null]; - if ($oldProduct) { - //New value may only be the saved value - $oldValues = [$oldProduct->getData($designAttribute)]; - if (empty($oldValues[0])) { - $oldValues[0] = null; - } - } else { - //New value can be empty or default - $oldValues[] = $attribute->getDefaultValue(); - } - $newValue = $product->getData($designAttribute); - if (empty($newValue) - || ($attribute->getBackend() instanceof LayoutUpdate - && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) - ) - ) { - $newValue = null; + $attribute = $attributes[$designAttribute]; + $oldValues = $this->fetchOldValues($attribute, $oldProduct); + try { + $newValue = $this->extractAttributeValue($product, $attribute); + } catch (\RuntimeException $exception) { + //No new value + continue; } if (!in_array($newValue, $oldValues, true)) { return true; @@ -108,9 +151,13 @@ public function authorizeSavingOf(ProductInterface $product): void if ($product->getId()) { /** @var ProductModel $savedProduct */ $savedProduct = $this->productFactory->create(); - $savedProduct->load($product->getId()); - if (!$savedProduct->getSku()) { - throw NoSuchEntityException::singleField('id', $product->getId()); + if ($product->getOrigData()) { + $savedProduct->setData($product->getOrigData()); + } else { + $savedProduct->load($product->getId()); + if (!$savedProduct->getSku()) { + throw NoSuchEntityException::singleField('id', $product->getId()); + } } } if ($this->hasProductChanged($product, $savedProduct)) { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index e0c45967b214e..d614f6e913dc5 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -423,16 +423,21 @@ public function testSaveDesign(): void //Updating our role to remove design properties access. $this->updateRoleResources($roleName, ['Magento_Catalog::categories']); //Updating the category but with the same design properties values. + //Omitting existing design attribute and keeping it's existing value + $attributes = $categoryData['custom_attributes']; + foreach ($attributes as $index => $attrData) { + if ($attrData['attribute_code'] === 'custom_design') { + unset($categoryData['custom_attributes'][$index]); + break; + } + } + unset($attributes, $index, $attrData); $result = $this->updateCategory($categoryData['id'], $categoryData, $token); //We haven't changed the design so operation is successful. $this->assertArrayHasKey('id', $result); //Changing a design property. - foreach ($categoryData['custom_attributes'] as &$customAttribute) { - if ($customAttribute['attribute_code'] === 'custom_design') { - $customAttribute['value'] = 'test2'; - } - } + $categoryData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => 'test2']; $exceptionMessage = null; try { $this->updateCategory($categoryData['id'], $categoryData, $token); diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index c5014ed391fb3..cde593c9fad2b 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -1678,6 +1678,7 @@ public function testSaveDesign(): void foreach ($productSaved['custom_attributes'] as $customAttribute) { if ($customAttribute['attribute_code'] === 'custom_design') { $savedCustomDesign = $customAttribute['value']; + break; } } $this->assertEquals('1', $savedCustomDesign); @@ -1690,16 +1691,21 @@ public function testSaveDesign(): void $rules->setResources(['Magento_Catalog::products']); $rules->saveRel(); //Updating the product but with the same design properties values. + //Removing the design attribute and keeping existing value. + $attributes = $productData['custom_attributes']; + foreach ($attributes as $i => $attribute) { + if ($attribute['attribute_code'] === 'custom_design') { + unset($productData['custom_attributes'][$i]); + break; + } + } + unset($attributes, $attribute, $i); $result = $this->updateProduct($productData, $token); //We haven't changed the design so operation is successful. $this->assertArrayHasKey('id', $result); //Changing a design property. - foreach ($productData['custom_attributes'] as &$customAttribute) { - if ($customAttribute['attribute_code'] === 'custom_design') { - $customAttribute['value'] = '2'; - } - } + $productData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => '2']; $exceptionMessage = null; try { $this->updateProduct($productData, $token); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php index b4022dc147b36..40725d3ee58be 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php @@ -105,5 +105,17 @@ public function testDependsOnNewUpdate(): void ); $this->attribute->beforeSave($this->category); $this->assertEmpty($this->category->getData('custom_layout_update')); + + //Empty value doesn't change the old attribute. Any non-string value can be used to represent an empty value. + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + false + ); + $this->attribute->beforeSave($this->category); + $this->assertEquals('test', $this->category->getData('custom_layout_update')); + $this->assertNull($this->category->getData('custom_layout_update_file')); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index a340094b01040..308a577a6aa6f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -65,7 +65,7 @@ protected function setUp() public function testImmutable(): void { //Value is empty - $this->category->setCustomAttribute('custom_layout_update', null); + $this->category->setCustomAttribute('custom_layout_update', false); $this->category->setOrigData('custom_layout_update', null); $this->attribute->beforeSave($this->category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index 49cba292f974a..6d66055cd1548 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -186,7 +186,11 @@ public function testCustomLayoutMeta(): void $meta = $this->dataProvider->getMeta(); $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ - ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] ]; @@ -200,7 +204,11 @@ public function testCustomLayoutMeta(): void $meta = $this->dataProvider->getMeta(); $expectedList = [ - ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], [ 'label' => 'Use existing', 'value' => LayoutUpdate::VALUE_USE_UPDATE_XML, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php index 4a928fb1386c0..ebfbd06d7edad 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -149,7 +149,11 @@ public function testEntitySpecificData(): void $meta = $this->eavModifier->modifyMeta([]); $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ - ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], ['label' => 'testOne', 'value' => 'testOne', '__disableTmpl' => true], ['label' => 'test_two', 'value' => 'test_two', '__disableTmpl' => true] ]; @@ -164,7 +168,11 @@ public function testEntitySpecificData(): void $meta = $this->eavModifier->modifyMeta([]); $list = $this->extractCustomLayoutOptions($meta); $expectedList = [ - ['label' => 'No update', 'value' => '', '__disableTmpl' => true], + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], [ 'label' => 'Use existing', 'value' => LayoutUpdateAttribute::VALUE_USE_UPDATE_XML, From 6a418305949865eeb50d64ade70f87890c2a4a88 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 30 Sep 2019 20:00:43 -0500 Subject: [PATCH 0716/2437] MC-18685: Remove custom layout updates from admin --- app/code/Magento/Cms/Api/Data/PageInterface.php | 2 ++ .../Magento/Cms/Controller/Adminhtml/Page/Save.php | 1 + app/code/Magento/Cms/Model/Page/DataProvider.php | 6 +++--- app/code/Magento/Cms/Model/PageRepository.php | 5 +++++ .../Cms/Controller/Adminhtml/PageDesignTest.php | 4 ++++ .../Magento/Cms/Model/PageRepositoryTest.php | 12 ++++++++++++ .../Magento/Cms/_files/pages_with_layout_xml.php | 1 + 7 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Api/Data/PageInterface.php b/app/code/Magento/Cms/Api/Data/PageInterface.php index 38d8feb953319..7a31ab1b9a94f 100644 --- a/app/code/Magento/Cms/Api/Data/PageInterface.php +++ b/app/code/Magento/Cms/Api/Data/PageInterface.php @@ -125,6 +125,7 @@ public function getSortOrder(); * Get layout update xml * * @return string|null + * @deprecated Existing updates are applied, new are not accepted. */ public function getLayoutUpdateXml(); @@ -274,6 +275,7 @@ public function setSortOrder($sortOrder); * * @param string $layoutUpdateXml * @return \Magento\Cms\Api\Data\PageInterface + * @deprecated Existing updates are applied, new are not accepted. */ public function setLayoutUpdateXml($layoutUpdateXml); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 1e4896c449d59..38901c6c00dbe 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -102,6 +102,7 @@ public function execute() $customLayoutFile = (string)$this->getRequest()->getParam('layout_update_selected'); if ($customLayoutFile !== '_existing_') { $data['custom_layout_update_xml'] = null; + $data['layout_update_xml'] = null; } else { $customLayoutFile = null; } diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index d1f148fe0198a..b9fb05e0f39e4 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -109,7 +109,7 @@ public function getData() /** @var $page \Magento\Cms\Model\Page */ foreach ($items as $page) { $this->loadedData[$page->getId()] = $page->getData(); - if ($page->getCustomLayoutUpdateXml()) { + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { //Deprecated layout update exists. $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; } @@ -120,7 +120,7 @@ public function getData() $page = $this->collection->getNewEmptyItem(); $page->setData($data); $this->loadedData[$page->getId()] = $page->getData(); - if ($page->getCustomLayoutUpdateXml()) { + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; } $this->dataPersistor->clear('cms_page'); @@ -175,7 +175,7 @@ public function getMeta() } //If custom layout XML is set then displaying this special option. if ($found) { - if ($found->getCustomLayoutUpdateXml()) { + if ($found->getCustomLayoutUpdateXml() || $found->getLayoutUpdateXml()) { $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; } foreach ($this->customLayoutManager->fetchAvailableFiles($found) as $layoutFile) { diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index 4492ea55da3a2..b555123af8967 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -136,6 +136,11 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) ) { throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); } + if ($page->getLayoutUpdateXml() + && (!$savedPage || $page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml()) + ) { + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); + } $this->resource->save($page); $this->identityMap->add($page); diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php index a13dc4e1f200b..ab0a5aa72f35e 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php @@ -173,6 +173,7 @@ public function testSaveLayoutXml(): void PageInterface::IDENTIFIER => 'test_custom_layout_page_1', PageInterface::TITLE => 'Page title', PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + PageInterface::LAYOUT_UPDATE_XML => $page->getLayoutUpdateXml(), 'layout_update_selected' => '_existing_' ]; @@ -183,12 +184,14 @@ public function testSaveLayoutXml(): void $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); $this->assertEquals($updated->getCustomLayoutUpdateXml(), $page->getCustomLayoutUpdateXml()); + $this->assertEquals($updated->getLayoutUpdateXml(), $page->getLayoutUpdateXml()); $requestData = [ Page::PAGE_ID => $page->getId(), PageInterface::IDENTIFIER => 'test_custom_layout_page_1', PageInterface::TITLE => 'Page title', PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + PageInterface::LAYOUT_UPDATE_XML => $page->getLayoutUpdateXml(), 'layout_update_selected' => '' ]; $this->getRequest()->setMethod(HttpRequest::METHOD_POST); @@ -198,5 +201,6 @@ public function testSaveLayoutXml(): void $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); $this->assertEmpty($updated->getCustomLayoutUpdateXml()); + $this->assertEmpty($updated->getLayoutUpdateXml()); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php index 5bbb8b870aad5..145830ab08259 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php @@ -63,9 +63,21 @@ public function testSaveUpdateXml(): void } $this->assertTrue($forbidden); + //New value is not accepted. + $page->setLayoutUpdateXml($page->getLayoutUpdateXml() .'TEST'); + $forbidden = false; + try { + $page = $this->repo->save($page); + } catch (CouldNotSaveException $exception) { + $forbidden = true; + } + $this->assertTrue($forbidden); + //Can be removed $page->setCustomLayoutUpdateXml(null); + $page->setLayoutUpdateXml(null); $page = $this->repo->save($page); $this->assertEmpty($page->getCustomLayoutUpdateXml()); + $this->assertEmpty($page->getLayoutUpdateXml()); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php index 80f0c8757a579..550b40a1bfec6 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -17,6 +17,7 @@ $page->setIdentifier('test_custom_layout_page_1'); $page->setTitle('Test Page'); $page->setCustomLayoutUpdateXml('tst'); +$page->setLayoutUpdateXml('tst_current'); $page->setIsActive(true); $page->setStoreId(0); $page->save(); From 8caaf36696dd84c2b75771368f306e1ec4fc7671 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Tue, 1 Oct 2019 09:01:59 +0300 Subject: [PATCH 0717/2437] MC-18165: Quick search with two chars shows all products --- .../Model/ResourceModel/Fulltext/CollectionTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php index 3f70b329c5693..8863834078214 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php @@ -50,6 +50,9 @@ public function testLoadWithFilterQuickSearch($filters, $expectedCount) foreach ($filters as $field => $value) { $fulltextCollection->addFieldToFilter($field, $value); } + if (isset($filters['search_term'])) { + $fulltextCollection->addSearchFilter($filters['search_term']); + } $fulltextCollection->loadWithFilter(); $items = $fulltextCollection->getItems(); $this->assertCount($expectedCount, $items); From f70fd6827e42ee759e668deaaed093e284fa2cb4 Mon Sep 17 00:00:00 2001 From: vital_sery <vital_sery@epam.com> Date: Tue, 1 Oct 2019 09:05:10 +0300 Subject: [PATCH 0718/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Integration test for MC-11299 --- .../Sales/Model/Order/ShipmentTest.php | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php index c52450d413ad6..11499a024b44d 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/ShipmentTest.php @@ -155,7 +155,6 @@ private function getOrder(string $incrementId): OrderInterface */ public function testGetTracksCollection() { - $trackCount = 1; $order = $this->getOrder('100000001'); $items = []; foreach ($order->getItems() as $item) { @@ -197,25 +196,10 @@ public function testGetTracksCollection() $this->shipmentRepository->save($secondOrderShipment); $secondShipmentTrackCollection = $secondOrderShipment->getTracksCollection(); - $shipmentId = $shipment->getId(); - $shipmentTrackIds = $shipmentTracksCollection->getColumnValues('parent_id'); - foreach ($shipmentTrackIds as $trackShipmentId) { - self::assertEquals($shipmentId, $trackShipmentId); - } - self::assertCount($trackCount, $shipmentTrackIds); - - $secondShipmentId = $secondOrderShipment->getId(); - $secondShipmentTrackIds = $secondShipmentTrackCollection->getColumnValues('parent_id'); - foreach ($secondShipmentTrackIds as $trackShipmentId) { - self::assertEquals($secondShipmentId, $trackShipmentId); - } - self::assertCount($trackCount, $secondShipmentTrackIds); - - self::assertEmpty( - array_intersect( - $shipmentTracksCollection->getColumnValues('id'), - $secondShipmentTrackCollection->getColumnValues('id') - ) + self::assertEquals($shipmentTracksCollection->getColumnValues('id'), [$track->getEntityId()]); + self::assertEquals( + $secondShipmentTrackCollection->getColumnValues('id'), + [$secondShipmentTrack->getEntityId()] ); } } From 867a00c5e14e052177204182225a93aa0d34c501 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 1 Oct 2019 10:13:28 +0300 Subject: [PATCH 0719/2437] MC-19031: Sorting the product grid by custom product attribute sorts by value ID instead of Alphabetically --- .../Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml index b0832b8f5944c..72c270aad585c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -92,6 +92,7 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductGridPageLoad"/> <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductGridFilters"/> <!--Sort by custom attribute DESC using grabbed value--> <conditionalClick selector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" dependentSelector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" visible="true" stepKey="ascendSortByCustomAttribute"/> From f255faefc34b9b7a70c74da407401af93d5ff554 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 1 Oct 2019 10:01:26 +0300 Subject: [PATCH 0720/2437] MC-20624: Automate MC-11459 --- .../_files/import_export/customers.php | 34 ++--- .../import_export/customers_rollback.php | 36 +++++ .../Model/Export/CustomerTest.php | 142 ++++++++++-------- 3 files changed, 129 insertions(+), 83 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php index 2f9c3dc31ef3d..9b989779e4cbd 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php @@ -3,16 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -$customers = []; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Customer\Model\Customer; +use Magento\Framework\Registry; + +/** @var $objectManager ObjectManagerInterface */ +$objectManager = Bootstrap::getObjectManager(); + +$customers = []; +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 1 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -40,13 +44,9 @@ $customer->save(); $customers[] = $customer; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 2 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -74,13 +74,9 @@ $customer->save(); $customers[] = $customer; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 3 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -108,9 +104,7 @@ $customer->save(); $customers[] = $customer; -/** @var $objectManager \Magento\TestFramework\ObjectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$objectManager->get(\Magento\Framework\Registry::class) +$objectManager->get(Registry::class) ->unregister('_fixture/Magento_ImportExport_Customer_Collection'); -$objectManager->get(\Magento\Framework\Registry::class) +$objectManager->get(Registry::class) ->register('_fixture/Magento_ImportExport_Customer_Collection', $customers); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php new file mode 100644 index 0000000000000..4630236c17b06 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Model\Customer; + +/** @var $objectManager ObjectManagerInterface */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var $registry Registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var $customer Customer */ +$customer = $objectManager->create(Customer::class); + +$emailsToDelete = [ + 'customer@example.com', + 'julie.worrell@example.com', + 'david.lamar@example.com', +]; +foreach ($emailsToDelete as $email) { + try { + $customer->loadByEmail($email)->delete(); + } catch (\Exception $e) { + } +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index 88b748f8bbbae..7cde07675ae27 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -4,48 +4,61 @@ * See COPYING.txt for license details. */ -/** - * Test for customer export model - */ namespace Magento\CustomerImportExport\Model\Export; +use Magento\Framework\Registry; +use Magento\Customer\Model\Attribute; +use Magento\ImportExport\Model\Export; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\ImportExport\Model\Export\Adapter\Csv; +use Magento\Customer\Model\Customer as CustomerModel; +use Magento\CustomerImportExport\Model\Export\Customer; +use Magento\Customer\Model\ResourceModel\Attribute\Collection; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; + +/** + * Tests for customer export model. + */ class CustomerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\CustomerImportExport\Model\Export\Customer + * @var Customer */ protected $_model; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritdoc + */ protected function setUp() { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CustomerImportExport\Model\Export\Customer::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->_model = $this->objectManager->create(Customer::class); } /** - * Test export method + * Export "Customer Main File". * * @magentoDataFixture Magento/Customer/_files/import_export/customers.php */ public function testExport() { $expectedAttributes = []; - /** @var $collection \Magento\Customer\Model\ResourceModel\Attribute\Collection */ - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class - ); - /** @var $attribute \Magento\Customer\Model\Attribute */ + /** @var $collection Collection */ + $collection = $this->objectManager->create(Collection::class); + /** @var $attribute Attribute */ foreach ($collection as $attribute) { $expectedAttributes[] = $attribute->getAttributeCode(); } $expectedAttributes = array_diff($expectedAttributes, $this->_model->getDisabledAttributes()); - $this->_model->setWriter( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\ImportExport\Model\Export\Adapter\Csv::class - ) - ); + $this->_model->setWriter($this->objectManager->get(Csv::class)); $data = $this->_model->export(); $this->assertNotEmpty($data); @@ -54,32 +67,45 @@ public function testExport() $this->assertEquals( count($expectedAttributes), count(array_intersect($expectedAttributes, $lines['header'])), - 'Expected attribute codes were not exported' + 'Expected attribute codes were not exported.' ); - $this->assertNotEmpty($lines['data'], 'No data was exported'); - - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $customers \Magento\Customer\Model\Customer[] */ - $customers = $objectManager->get( - \Magento\Framework\Registry::class - )->registry( - '_fixture/Magento_ImportExport_Customer_Collection' - ); - foreach ($customers as $key => $customer) { - foreach ($expectedAttributes as $code) { - if (!in_array($code, $this->_model->getDisabledAttributes()) && isset($lines[$key][$code])) { - $this->assertEquals( - $customer->getData($code), - $lines[$key][$code], - 'Attribute "' . $code . '" is not equal' - ); + $this->assertNotEmpty($lines['data'], 'No data was exported.'); + + /** @var $customers CustomerModel[] */ + $customers = $this->objectManager->create(CustomerCollection::class)->getItems(); + foreach ($customers as $customer) { + $data = $customer->getData(); + $exportData = $lines['data'][$data['email']]; + $exportData = $this->unsetDuplicateData($exportData); + array_walk( + $exportData, + function (&$value) { + if (is_string($value) && $value === '') { + $value = null; + } } - } + ); + + $this->assertArraySubset($exportData, $data); } } + /** + * Unset non-useful or duplicate data from exported file data. + * + * @param array $data + * @return array + */ + private function unsetDuplicateData(array $data): array + { + unset($data['_website']); + unset($data['_store']); + unset($data['password']); + + return $data; + } + /** * Test entity type code value */ @@ -93,10 +119,7 @@ public function testGetEntityTypeCode() */ public function testGetAttributeCollection() { - $this->assertInstanceOf( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class, - $this->_model->getAttributeCollection() - ); + $this->assertInstanceOf(Collection::class, $this->_model->getAttributeCollection()); } /** @@ -104,14 +127,14 @@ public function testGetAttributeCollection() */ public function testFilterAttributeCollection() { - /** @var $collection \Magento\Customer\Model\ResourceModel\Attribute\Collection */ + /** @var $collection Collection */ $collection = $this->_model->getAttributeCollection(); $collection = $this->_model->filterAttributeCollection($collection); /** * Check that disabled attributes is not existed in attribute collection */ $existedAttributes = []; - /** @var $attribute \Magento\Customer\Model\Attribute */ + /** @var $attribute Attribute */ foreach ($collection as $attribute) { $existedAttributes[] = $attribute->getAttributeCode(); } @@ -127,7 +150,7 @@ public function testFilterAttributeCollection() * Check that all overridden attributes were affected during filtering process */ $overriddenAttributes = $this->_model->getOverriddenAttributes(); - /** @var $attribute \Magento\Customer\Model\Attribute */ + /** @var $attribute Attribute */ foreach ($collection as $attribute) { if (isset($overriddenAttributes[$attribute->getAttributeCode()])) { foreach ($overriddenAttributes[$attribute->getAttributeCode()] as $propertyKey => $property) { @@ -150,27 +173,22 @@ public function testFilterEntityCollection() { $createdAtDate = '2038-01-01'; - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var $objectManager ObjectManagerInterface */ + $objectManager = $this->objectManager; /** * Change created_at date of first customer for future filter test. */ - $customers = $objectManager->get( - \Magento\Framework\Registry::class - )->registry( - '_fixture/Magento_ImportExport_Customer_Collection' - ); + $customers = $objectManager->get(Registry::class) + ->registry('_fixture/Magento_ImportExport_Customer_Collection'); $customers[0]->setCreatedAt($createdAtDate); $customers[0]->save(); /** * Change type of created_at attribute. In this case we have possibility to test date rage filter */ - $attributeCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class - ); + $attributeCollection = $this->objectManager->create(Collection::class); $attributeCollection->addFieldToFilter('attribute_code', 'created_at'); - /** @var $createdAtAttribute \Magento\Customer\Model\Attribute */ + /** @var $createdAtAttribute Attribute */ $createdAtAttribute = $attributeCollection->getFirstItem(); $createdAtAttribute->setBackendType('datetime'); $createdAtAttribute->save(); @@ -178,19 +196,17 @@ public function testFilterEntityCollection() * Prepare filter.asd */ $parameters = [ - \Magento\ImportExport\Model\Export::FILTER_ELEMENT_GROUP => [ + Export::FILTER_ELEMENT_GROUP => [ 'email' => 'example.com', 'created_at' => [$createdAtDate, ''], - 'store_id' => \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Store\Model\StoreManagerInterface::class - )->getStore()->getId() + 'store_id' => $this->objectManager->get(StoreManagerInterface::class)->getStore()->getId() ] ]; $this->_model->setParameters($parameters); - /** @var $customers \Magento\Customer\Model\ResourceModel\Customer\Collection */ + /** @var $customers Collection */ $collection = $this->_model->filterEntityCollection( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Customer\Collection::class + $this->objectManager->create( + CustomerCollection::class ) ); $collection->load(); From 9517396a09a8eea3db0533a54beb1e7836402cc8 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Mon, 30 Sep 2019 17:28:23 +0400 Subject: [PATCH 0721/2437] MAGETWO-67450: The rate in order is duplicated - Updated automated test script --- .../Test/Mftf/Data/CatalogPriceConfigData.xml | 9 +++-- .../Mftf/Data/CurrencyRatesConfigData.xml | 40 ++++++++++++++----- .../Test/Mftf/Page/AdminCurrencyRatesPage.xml | 2 +- ...ayWhenChooseThreeAllowedCurrenciesTest.xml | 6 +-- .../AdminOrderRateDisplayedInOneLineTest.xml | 28 ++++++------- 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml index a16acbf4c90b3..50ce7f2da18c7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogPriceConfigData.xml @@ -8,9 +8,12 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="CatalogPriceScopeConfigData"> + <entity name="CatalogPriceScopeWebsiteConfigData"> <data key="path">catalog/price/scope</data> - <data key="value_website">1</data> - <data key="value_global">0</data> + <data key="value">1</data> + </entity> + <entity name="CatalogPriceScopeGlobalConfigData"> + <data key="path">catalog/price/scope</data> + <data key="value">0</data> </entity> </entities> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml index 5f4e3dc17341d..b7b6bc8690bd0 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml @@ -8,25 +8,45 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="SetCurrencyBaseConfig"> + <entity name="SetCurrencyUSDBaseConfig"> <data key="path">currency/options/base</data> - <data key="value_usd">USD</data> - <data key="value_eur">EUR</data> + <data key="value">USD</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> - <entity name="SetAllowedCurrenciesConfig"> + <entity name="SetCurrencyEURBaseConfig"> + <data key="path">currency/options/base</data> + <data key="value">EUR</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetAllowedCurrenciesConfigForThree"> + <data key="path">currency/options/allow</data> + <data key="value">EUR,USD,RUB</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetAllowedCurrenciesConfigForTwo"> <data key="path">currency/options/allow</data> - <data key="value_three_currencies">EUR,USD,RUB</data> - <data key="value_two_currencies">EUR,USD</data> - <data key="value_usd">USD</data> + <data key="value">EUR,USD</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetAllowedCurrenciesConfigForOne"> + <data key="path">currency/options/allow</data> + <data key="value">USD</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + </entity> + <entity name="SetDefaultCurrencyEURConfig"> + <data key="path">currency/options/default</data> + <data key="value">EUR</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> - <entity name="SetDefaultCurrencyConfig"> + <entity name="SetDefaultCurrencyUSDConfig"> <data key="path">currency/options/default</data> - <data key="value_eur">EUR</data> - <data key="value_usd">USD</data> + <data key="value">USD</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml index 7cfb177ecec31..d31dd71d474bb 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Page/AdminCurrencyRatesPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCurrencyRatesPage" url="admin/system_currency/" area="admin" module="Currency"> + <page name="AdminCurrencyRatesPage" url="admin/system_currency/" area="admin" module="CurrencySymbol"> <section name="AdminCurrencyRatesSection"/> </page> </pages> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index 2cb5ea81e5f31..fb71777b086dc 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -24,9 +24,9 @@ <!--Create product--> <createData entity="SimpleProduct2" stepKey="createNewProduct"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSDWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_three_currencies}}" stepKey="setAllowedCurrencyWebsites"/> - <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEURWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForThree.scope}} --scope-code={{SetAllowedCurrenciesConfigForThree.scope_code}} {{SetAllowedCurrenciesConfigForThree.path}} {{SetAllowedCurrenciesConfigForThree.value}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyEURConfig.scope}} --scope-code={{SetDefaultCurrencyEURConfig.scope_code}} {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> <!--Delete created product--> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index 7ac01c0792810..514d4c5d54f89 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -24,28 +24,28 @@ <!--Create product--> <createData entity="SimpleProduct2" stepKey="createProduct"/> <!--Set price scope website--> - <magentoCLI command="config:set {{CatalogPriceScopeConfigData.path}} {{CatalogPriceScopeConfigData.value_website}}" stepKey="setCatalogPriceScopeWebsite"/> + <magentoCLI command="config:set {{CatalogPriceScopeWebsiteConfigData.path}} {{CatalogPriceScopeWebsiteConfigData.value}}" stepKey="setCatalogPriceScopeWebsite"/> <!--Set Currency options for Default Config--> - <magentoCLI command="config:set {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_eur}}" stepKey="setCurrencyBaseEUR"/> - <magentoCLI command="config:set {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_two_currencies}}" stepKey="setAllowedCurrencyEUR"/> - <magentoCLI command="config:set {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEUR"/> + <magentoCLI command="config:set {{SetCurrencyEURBaseConfig.path}} {{SetCurrencyEURBaseConfig.value}}" stepKey="setCurrencyBaseEUR"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForTwo.path}} {{SetAllowedCurrenciesConfigForTwo.value}}" stepKey="setAllowedCurrencyEUR"/> + <magentoCLI command="config:set {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEUR"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseEURWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_two_currencies}}" stepKey="setAllowedCurrencyWebsites"/> - <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_eur}}" stepKey="setCurrencyDefaultEURWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseEURWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForTwo.scope}} --scope-code={{SetAllowedCurrenciesConfigForTwo.scope_code}} {{SetAllowedCurrenciesConfigForTwo.path}} {{SetAllowedCurrenciesConfigForTwo.value}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyEURConfig.scope}} --scope-code={{SetDefaultCurrencyEURConfig.scope_code}} {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> <!--Delete created product--> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <!--Reset configurations--> - <magentoCLI command="config:set {{CatalogPriceScopeConfigData.path}} {{CatalogPriceScopeConfigData.value_global}}" stepKey="setCatalogPriceScopeGlobal"/> - <magentoCLI command="config:set {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSD"/> - <magentoCLI command="config:set {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_usd}}" stepKey="setCurrencyDefaultUSD"/> - <magentoCLI command="config:set {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_usd}}" stepKey="setAllowedCurrencyUSD"/> + <magentoCLI command="config:set {{CatalogPriceScopeGlobalConfigData.path}} {{CatalogPriceScopeGlobalConfigData.value}}" stepKey="setCatalogPriceScopeGlobal"/> + <magentoCLI command="config:set {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSD"/> + <magentoCLI command="config:set {{SetDefaultCurrencyUSDConfig.path}} {{SetDefaultCurrencyUSDConfig.value}}" stepKey="setCurrencyDefaultUSD"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForOne.path}} {{SetAllowedCurrenciesConfigForOne.value}}" stepKey="setAllowedCurrencyUSD"/> <!--Set Currency options for Website--> - <magentoCLI command="config:set --scope={{SetCurrencyBaseConfig.scope}} --scope-code={{SetCurrencyBaseConfig.scope_code}} {{SetCurrencyBaseConfig.path}} {{SetCurrencyBaseConfig.value_usd}}" stepKey="setCurrencyBaseUSDWebsites"/> - <magentoCLI command="config:set --scope={{SetDefaultCurrencyConfig.scope}} --scope-code={{SetDefaultCurrencyConfig.scope_code}} {{SetDefaultCurrencyConfig.path}} {{SetDefaultCurrencyConfig.value_usd}}" stepKey="setCurrencyDefaultUSDWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfig.scope}} --scope-code={{SetAllowedCurrenciesConfig.scope_code}} {{SetAllowedCurrenciesConfig.path}} {{SetAllowedCurrenciesConfig.value_usd}}" stepKey="setAllowedCurrencyUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetDefaultCurrencyUSDConfig.scope}} --scope-code={{SetDefaultCurrencyUSDConfig.scope_code}} {{SetDefaultCurrencyUSDConfig.path}} {{SetDefaultCurrencyUSDConfig.value}}" stepKey="setCurrencyDefaultUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForOne.scope}} --scope-code={{SetAllowedCurrenciesConfigForOne.scope_code}} {{SetAllowedCurrenciesConfigForOne.path}} {{SetAllowedCurrenciesConfigForOne.value}}" stepKey="setAllowedCurrencyUSDWebsites"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Open created product on Storefront and place for order--> From f8f6abe8fcda42ef4cd1eb18d665f657fd3e6df7 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 11 Jun 2019 16:57:55 +0300 Subject: [PATCH 0722/2437] MC-16455: Admin user with permission for 1 store can manage categories - Add fix --- .../Ui/view/base/web/js/form/element/ui-select.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index 5667ce5d71d81..d66b0619b5e29 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -568,8 +568,10 @@ define([ * Remove element from selected array */ removeSelected: function (value, data, event) { - event ? event.stopPropagation() : false; - this.value.remove(value); + if (! this.disabled()) { + event ? event.stopPropagation() : false; + this.value.remove(value); + } }, /** @@ -661,7 +663,9 @@ define([ * @returns {Object} Chainable */ toggleListVisible: function () { - this.listVisible(!this.listVisible()); + if (! this.disabled()) { + this.listVisible(!this.listVisible()); + } return this; }, From e55816b1131aaed94a80ba5539df40176d5558f8 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 1 Oct 2019 14:13:43 +0300 Subject: [PATCH 0723/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix merge conflict with 2.3-develop --- .../Ui/DataProvider/Product/Form/Modifier/Categories.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 5f1907344ce83..f62aacfce8e89 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -234,6 +234,7 @@ protected function customizeCategoriesField(array $meta) $fieldCode = 'category_ids'; $elementPath = $this->arrayManager->findPath($fieldCode, $meta, null, 'children'); $containerPath = $this->arrayManager->findPath(static::CONTAINER_PREFIX . $fieldCode, $meta, null, 'children'); + $fieldIsDisabled = $this->locator->getProduct()->isLockedAttribute($fieldCode); if (!$elementPath) { return $meta; @@ -250,7 +251,6 @@ protected function customizeCategoriesField(array $meta) 'componentType' => 'container', 'component' => 'Magento_Ui/js/form/components/group', 'scopeLabel' => __('[GLOBAL]'), - 'disabled' => $this->locator->getProduct()->isLockedAttribute($fieldCode), ], ], ], @@ -268,6 +268,7 @@ protected function customizeCategoriesField(array $meta) 'levelsVisibility' => '1', 'elementTmpl' => 'ui/grid/filters/elements/ui-select', 'options' => $this->getCategoriesTree(), + 'disabled' => $fieldIsDisabled, 'listens' => [ 'index=create_category:responseData' => 'setParsed', 'newOption' => 'toggleOptionSelected' @@ -315,6 +316,7 @@ protected function customizeCategoriesField(array $meta) 'displayArea' => 'insideGroup', 'sortOrder' => 20, 'dataScope' => $fieldCode, + 'disabled' => $fieldIsDisabled, ], ], ] From 49addf9963749e61a860a2db6b192291cab0b3ba Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Thu, 13 Jun 2019 16:03:13 +0300 Subject: [PATCH 0724/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix statics - Fix unit tests --- .../Magento/Ui/view/base/web/js/form/element/ui-select.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index d66b0619b5e29..9d38067d332fc 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -568,7 +568,7 @@ define([ * Remove element from selected array */ removeSelected: function (value, data, event) { - if (! this.disabled()) { + if (!this.disabled()) { event ? event.stopPropagation() : false; this.value.remove(value); } @@ -663,7 +663,7 @@ define([ * @returns {Object} Chainable */ toggleListVisible: function () { - if (! this.disabled()) { + if (!this.disabled()) { this.listVisible(!this.listVisible()); } From 5e070035e0b45a85a349181e4a0fa9b433c21dbe Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Mon, 5 Aug 2019 20:52:49 +0300 Subject: [PATCH 0725/2437] MC-16455: Admin user with permission for 1 store can manage categories - Make category page as read only for restricted-admin role --- .../Catalog/Model/Category/DataProvider.php | 77 +++++++++++-------- .../Product/Form/Modifier/Categories.php | 5 +- .../Ui/Component/Form/Element/Wysiwyg.php | 7 +- .../view/base/web/js/form/components/group.js | 18 +++++ 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index c96b2aae36059..c22ecd25512d4 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Category; use Magento\Catalog\Api\Data\CategoryInterface; @@ -20,6 +22,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Filesystem; use Magento\Framework\Stdlib\ArrayManager; +use Magento\Framework\Stdlib\ArrayUtils; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Form\Field; @@ -28,10 +31,9 @@ use Magento\Framework\AuthorizationInterface; /** - * Class DataProvider + * Category form data provider. * * @api - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) * @since 101.0.0 @@ -52,6 +54,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider /** * EAV attribute properties to fetch from meta storage + * * @var array * @since 101.0.0 */ @@ -143,6 +146,11 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ private $arrayManager; + /** + * @var ArrayUtils + */ + private $arrayUtils; + /** * @var Filesystem */ @@ -154,8 +162,6 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider private $auth; /** - * DataProvider constructor - * * @param string $name * @param string $primaryFieldName * @param string $requestFieldName @@ -186,7 +192,8 @@ public function __construct( array $meta = [], array $data = [], PoolInterface $pool = null, - ?AuthorizationInterface $auth = null + ?AuthorizationInterface $auth = null, + ?ArrayUtils $arrayUtils = null ) { $this->eavValidationRules = $eavValidationRules; $this->collection = $categoryCollectionFactory->create(); @@ -197,6 +204,7 @@ public function __construct( $this->request = $request; $this->categoryFactory = $categoryFactory; $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); + $this->arrayUtils = $arrayUtils ?? ObjectManager::getInstance()->get(ArrayUtils::class); parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); } @@ -205,7 +213,7 @@ public function __construct( * @inheritdoc * @since 101.1.0 */ - public function getMeta() + public function getMeta(): array { $meta = parent::getMeta(); $meta = $this->prepareMeta($meta); @@ -226,7 +234,7 @@ public function getMeta() * @param array $meta * @return array */ - private function addUseDefaultValueCheckbox(Category $category, array $meta) + private function addUseDefaultValueCheckbox(Category $category, array $meta): array { /** @var EavAttributeInterface $attribute */ foreach ($category->getAttributes() as $attribute) { @@ -264,10 +272,7 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta) } /** - * Prepare meta data - * - * @param array $meta - * @return array + * @inheritDoc * @since 101.0.0 */ public function prepareMeta($meta) @@ -290,7 +295,7 @@ public function prepareMeta($meta) * @param array $fieldsMeta * @return array */ - private function prepareFieldsMeta($fieldsMap, $fieldsMeta) + private function prepareFieldsMeta(array $fieldsMap, array $fieldsMeta): array { $canEditDesign = $this->auth->isAllowed('Magento_Catalog::edit_category_design'); @@ -313,12 +318,10 @@ private function prepareFieldsMeta($fieldsMap, $fieldsMeta) } /** - * Get data - * - * @return array + * @inheritDoc * @since 101.0.0 */ - public function getData() + public function getData(): array { if (isset($this->loadedData)) { return $this->loadedData; @@ -350,6 +353,7 @@ public function getAttributesMeta(Type $entityType) { $meta = []; $attributes = $entityType->getAttributeCollection(); + $fields = $this->getFields(); /* @var EavAttribute $attribute */ foreach ($attributes as $attribute) { $code = $attribute->getAttributeCode(); @@ -374,6 +378,13 @@ public function getAttributesMeta(Type $entityType) $meta[$code]['scopeLabel'] = $this->getScopeLabel($attribute); $meta[$code]['componentType'] = Field::NAME; + + // disable fields + $attributeIsLocked = $this->getCurrentCategory()->isLockedAttribute($code); + $meta[$code]['disabled'] = $attributeIsLocked; + if (array_search('use_config.' . $code, $fields, true) && $meta[$code]['disabled']) { + $meta['use_config.' . $code]['disabled'] = true; + } } $result = []; @@ -394,7 +405,7 @@ public function getAttributesMeta(Type $entityType) * @return array * @since 101.0.0 */ - protected function addUseConfigSettings($categoryData) + protected function addUseConfigSettings($categoryData): array { foreach ($this->elementsWithUseConfigSetting as $elementsWithUseConfigSetting) { if (!isset($categoryData['use_config'][$elementsWithUseConfigSetting])) { @@ -419,7 +430,7 @@ protected function addUseConfigSettings($categoryData) * @deprecated 101.1.0 * @since 101.0.0 */ - protected function addUseDefaultSettings($category, $categoryData) + protected function addUseDefaultSettings($category, $categoryData): array { if ($category->getExistsStoreValueFlag('url_key') || $category->getStoreId() === Store::DEFAULT_STORE_ID @@ -493,7 +504,7 @@ public function getScopeLabel(EavAttribute $attribute) * @return array * @since 101.0.0 */ - protected function filterFields($categoryData) + protected function filterFields($categoryData): array { return array_diff_key($categoryData, array_flip($this->ignoreFields)); } @@ -505,7 +516,7 @@ protected function filterFields($categoryData) * @param array $categoryData * @return array */ - private function convertValues($category, $categoryData) + private function convertValues($category, $categoryData): array { foreach ($category->getAttributes() as $attributeCode => $attribute) { if (!isset($categoryData[$attributeCode])) { @@ -541,10 +552,7 @@ private function convertValues($category, $categoryData) } /** - * Category's fields default values - * - * @param array $result - * @return array + * @inheritDoc * @since 101.0.0 */ public function getDefaultMetaData($result) @@ -558,9 +566,7 @@ public function getDefaultMetaData($result) } /** - * List of fields groups and fields. - * - * @return array + * @inheritDoc * @since 101.0.0 */ protected function getFieldsMap() @@ -616,13 +622,24 @@ protected function getFieldsMap() ]; } + /** + * Return list of fields names. + * + * @return array + */ + private function getFields(): array + { + $fieldsMap = $this->getFieldsMap(); + return $this->arrayUtils->flatten($fieldsMap); + } + /** * Retrieve scope overridden value * * @return ScopeOverriddenValue * @deprecated 101.1.0 */ - private function getScopeOverriddenValue() + private function getScopeOverriddenValue(): ScopeOverriddenValue { if (null === $this->scopeOverriddenValue) { $this->scopeOverriddenValue = \Magento\Framework\App\ObjectManager::getInstance()->get( @@ -639,7 +656,7 @@ private function getScopeOverriddenValue() * @return ArrayManager * @deprecated 101.1.0 */ - private function getArrayManager() + private function getArrayManager(): ArrayManager { if (null === $this->arrayManager) { $this->arrayManager = \Magento\Framework\App\ObjectManager::getInstance()->get( @@ -657,7 +674,7 @@ private function getArrayManager() * * @deprecated 101.1.0 */ - private function getFileInfo() + private function getFileInfo(): FileInfo { if ($this->fileInfo === null) { $this->fileInfo = ObjectManager::getInstance()->get(FileInfo::class); diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index f62aacfce8e89..3580ba2b467ef 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -23,7 +23,6 @@ * Data provider for categories field of product page * * @api - * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 101.0.0 */ @@ -120,7 +119,7 @@ public function __construct( * @return CacheInterface * @deprecated 101.0.3 */ - private function getCacheManager() + private function getCacheManager(): CacheInterface { if (!$this->cacheManager) { $this->cacheManager = ObjectManager::getInstance() @@ -148,7 +147,7 @@ public function modifyMeta(array $meta) * * @return bool */ - private function isAllowed() + private function isAllowed(): bool { return $this->authorization->isAllowed('Magento_Catalog::categories'); } diff --git a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php index d39d2dc3cd930..869df80159b89 100644 --- a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php +++ b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php @@ -3,12 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Ui\Component\Form\Element; use Magento\Framework\Data\Form\Element\Editor; use Magento\Framework\Data\Form; use Magento\Framework\Data\FormFactory; -use Magento\Framework\DataObject; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Wysiwyg\ConfigInterface; @@ -51,6 +52,10 @@ public function __construct( array $config = [] ) { $wysiwygConfigData = isset($config['wysiwygConfigData']) ? $config['wysiwygConfigData'] : []; + if ($config['disabled'] === true) { + $config['wysiwygConfigData']['settings']['readonly'] = 1; + } + $this->form = $formFactory->create(); $wysiwygId = $context->getNamespace() . '_' . $data['name']; $this->editor = $this->form->addField( diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/group.js b/app/code/Magento/Ui/view/base/web/js/form/components/group.js index 8c5950f7e2fa1..f74bf3f5e9195 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/group.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/group.js @@ -36,6 +36,24 @@ define([ return this; }, + /** + * @inheritdoc + */ + initElement: function (elem) { + this._super(); + + if (this.disabled) { + try { + elem.disabled(true); + } + catch (e) { + + } + } + + return this; + }, + /** * Calls initObservable of parent class. * Defines observable properties of instance. From 8a627e00b5dd681d67404de1589e89b538e9183e Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 6 Aug 2019 21:39:51 +0300 Subject: [PATCH 0726/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix unit and static tests failures. --- .../Catalog/Model/Category/DataProvider.php | 13 +++++++------ .../Unit/Model/Category/DataProviderTest.php | 18 +++++++++++++++++- .../Product/Form/Modifier/Categories.php | 4 ++-- .../Ui/Component/Form/Element/Wysiwyg.php | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index c22ecd25512d4..af517448e2efa 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -162,9 +162,9 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider private $auth; /** - * @param string $name - * @param string $primaryFieldName - * @param string $requestFieldName + * @param $name + * @param $primaryFieldName + * @param $requestFieldName * @param EavValidationRules $eavValidationRules * @param CategoryCollectionFactory $categoryCollectionFactory * @param StoreManagerInterface $storeManager @@ -176,7 +176,8 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param array $data * @param PoolInterface|null $pool * @param AuthorizationInterface|null $auth - * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @param ArrayUtils|null $arrayUtils + * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( $name, @@ -321,7 +322,7 @@ private function prepareFieldsMeta(array $fieldsMap, array $fieldsMeta): array * @inheritDoc * @since 101.0.0 */ - public function getData(): array + public function getData() { if (isset($this->loadedData)) { return $this->loadedData; @@ -382,7 +383,7 @@ public function getAttributesMeta(Type $entityType) // disable fields $attributeIsLocked = $this->getCurrentCategory()->isLockedAttribute($code); $meta[$code]['disabled'] = $attributeIsLocked; - if (array_search('use_config.' . $code, $fields, true) && $meta[$code]['disabled']) { + if (array_search('use_config.' . $code, $fields) && $meta[$code]['disabled']) { $meta['use_config.' . $code]['disabled'] = true; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php index 8f3aa66e57c5e..1511a930dd02e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php @@ -17,6 +17,7 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; +use Magento\Framework\Stdlib\ArrayUtils; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -78,6 +79,14 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $modifierPool; + /** + * @var ArrayUtils|\PHPUnit_Framework_MockObject_MockObject + */ + private $arrayUtils; + + /** + * @inheritDoc + */ protected function setUp() { $this->eavValidationRules = $this->getMockBuilder(EavValidationRules::class) @@ -128,6 +137,10 @@ protected function setUp() ->getMock(); $this->modifierPool = $this->getMockBuilder(PoolInterface::class)->getMockForAbstractClass(); + + $this->arrayUtils = $this->getMockBuilder(ArrayUtils::class) + ->setMethods(['flatten']) + ->disableOriginalConstructor()->getMock(); } /** @@ -157,7 +170,8 @@ private function getModel() 'eavConfig' => $this->eavConfig, 'request' => $this->request, 'categoryFactory' => $this->categoryFactory, - 'pool' => $this->modifierPool + 'pool' => $this->modifierPool, + 'arrayUtils' => $this->arrayUtils ] ); @@ -331,6 +345,8 @@ public function testGetData() public function testGetMetaWithoutParentInheritanceResolving() { + $this->arrayUtils->expects($this->atLeastOnce())->method('flatten')->willReturn([1,3,3]); + $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 3580ba2b467ef..c395fc49ac073 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -149,7 +149,7 @@ public function modifyMeta(array $meta) */ private function isAllowed(): bool { - return $this->authorization->isAllowed('Magento_Catalog::categories'); + return (bool) $this->authorization->isAllowed('Magento_Catalog::categories'); } /** @@ -291,6 +291,7 @@ protected function customizeCategoriesField(array $meta) 'formElement' => 'container', 'additionalClasses' => 'admin__field-small', 'componentType' => 'container', + 'disabled' => $fieldIsDisabled, 'component' => 'Magento_Ui/js/form/components/button', 'template' => 'ui/form/components/button/container', 'actions' => [ @@ -315,7 +316,6 @@ protected function customizeCategoriesField(array $meta) 'displayArea' => 'insideGroup', 'sortOrder' => 20, 'dataScope' => $fieldCode, - 'disabled' => $fieldIsDisabled, ], ], ] diff --git a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php index 869df80159b89..7005afbad44d9 100644 --- a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php +++ b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php @@ -52,7 +52,7 @@ public function __construct( array $config = [] ) { $wysiwygConfigData = isset($config['wysiwygConfigData']) ? $config['wysiwygConfigData'] : []; - if ($config['disabled'] === true) { + if (isset($config['disabled']) && $config['disabled'] === true) { $config['wysiwygConfigData']['settings']['readonly'] = 1; } From 74826150ee086441c31614d56719fcfad40defb3 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 6 Aug 2019 22:07:54 +0300 Subject: [PATCH 0727/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix static tests failures. --- .../Catalog/Model/Category/DataProvider.php | 10 ++++++---- .../Unit/Model/Category/DataProviderTest.php | 20 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index af517448e2efa..392d0b64f821c 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -162,9 +162,9 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider private $auth; /** - * @param $name - * @param $primaryFieldName - * @param $requestFieldName + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName * @param EavValidationRules $eavValidationRules * @param CategoryCollectionFactory $categoryCollectionFactory * @param StoreManagerInterface $storeManager @@ -178,6 +178,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param AuthorizationInterface|null $auth * @param ArrayUtils|null $arrayUtils * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, @@ -383,7 +384,8 @@ public function getAttributesMeta(Type $entityType) // disable fields $attributeIsLocked = $this->getCurrentCategory()->isLockedAttribute($code); $meta[$code]['disabled'] = $attributeIsLocked; - if (array_search('use_config.' . $code, $fields) && $meta[$code]['disabled']) { + $hasUseConfigField = (bool) array_search('use_config.' . $code, $fields, true); + if ($hasUseConfigField && $meta[$code]['disabled']) { $meta['use_config.' . $code]['disabled'] = true; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php index 1511a930dd02e..da5809a0f481d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php @@ -220,10 +220,12 @@ public function testGetDataNoFileExists() ->getMock(); $categoryMock->expects($this->exactly(2)) ->method('getData') - ->willReturnMap([ - ['', null, $categoryData], - ['image', null, $categoryData['image']], - ]); + ->willReturnMap( + [ + ['', null, $categoryData], + ['image', null, $categoryData['image']], + ] + ); $categoryMock->expects($this->any()) ->method('getExistsStoreValueFlag') ->with('url_key') @@ -294,10 +296,12 @@ public function testGetData() ->getMock(); $categoryMock->expects($this->exactly(2)) ->method('getData') - ->willReturnMap([ - ['', null, $categoryData], - ['image', null, $categoryData['image']], - ]); + ->willReturnMap( + [ + ['', null, $categoryData], + ['image', null, $categoryData['image']], + ] + ); $categoryMock->expects($this->any()) ->method('getExistsStoreValueFlag') ->with('url_key') From bf8d4306dc888dcffc4ce207e79d823901e2778a Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Thu, 8 Aug 2019 20:27:40 +0300 Subject: [PATCH 0728/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix integration test. --- .../Magento/Catalog/Model/Category/DataProvider.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 392d0b64f821c..21a7bfa98bf98 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -356,6 +356,7 @@ public function getAttributesMeta(Type $entityType) $meta = []; $attributes = $entityType->getAttributeCollection(); $fields = $this->getFields(); + $category = $this->getCurrentCategory(); /* @var EavAttribute $attribute */ foreach ($attributes as $attribute) { $code = $attribute->getAttributeCode(); @@ -382,11 +383,13 @@ public function getAttributesMeta(Type $entityType) $meta[$code]['componentType'] = Field::NAME; // disable fields - $attributeIsLocked = $this->getCurrentCategory()->isLockedAttribute($code); - $meta[$code]['disabled'] = $attributeIsLocked; - $hasUseConfigField = (bool) array_search('use_config.' . $code, $fields, true); - if ($hasUseConfigField && $meta[$code]['disabled']) { - $meta['use_config.' . $code]['disabled'] = true; + if ($category) { + $attributeIsLocked = $category->isLockedAttribute($code); + $meta[$code]['disabled'] = $attributeIsLocked; + $hasUseConfigField = (bool) array_search('use_config.' . $code, $fields, true); + if ($hasUseConfigField && $meta[$code]['disabled']) { + $meta['use_config.' . $code]['disabled'] = true; + } } } From 7ea4c6ce306f41683c5a7cb25879df6ca231f068 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Fri, 9 Aug 2019 14:53:17 +0300 Subject: [PATCH 0729/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix unit tests failures. --- .../Catalog/Test/Unit/Model/Category/DataProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php index da5809a0f481d..4c3450d555f1d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php @@ -354,7 +354,7 @@ public function testGetMetaWithoutParentInheritanceResolving() $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) ->disableOriginalConstructor() ->getMock(); - $this->registry->expects($this->once()) + $this->registry->expects($this->atLeastOnce()) ->method('registry') ->with('category') ->willReturn($categoryMock); From 27945bcd7e2adffe5818cd76ba4a365bbaf79f8a Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Tue, 10 Sep 2019 13:05:07 +0400 Subject: [PATCH 0730/2437] MC-16455: Admin user with permission for 1 store can manage categories - Added automation test script. --- .../Section/AdminCategoryProductsSection.xml | 1 + ...oriesByUserWithPermissionFor1StoreTest.xml | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml index e9ff40f98bb16..8a993a74a58d1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml @@ -11,5 +11,6 @@ <section name="AdminCategoryProductsSection"> <element name="sectionHeader" type="button" selector="div[data-index='assign_products']" timeout="30"/> <element name="addProducts" type="button" selector="#catalog_category_add_product_tabs" timeout="30"/> + <element name="addProductsDisabled" type="button" selector="#catalog_category_add_product_tabs[disabled]" timeout="30"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml new file mode 100644 index 0000000000000..8dd4597b0b963 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminManageCategoriesByUserWithPermissionFor1StoreTest"> + <annotations> + <features value="Catalog"/> + <stories value="user role"/> + <title value="Ability to manage categories by admin user with permission for 1 store"/> + <description value="Ability to manage categories by admin user with permission for 1 store"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19264"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create new store and store view--> + <comment userInput="Create new store and store view" stepKey="createStoreAndStoreView"/> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createStore1"> + <argument name="website" value="Main Website"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewA"/> + <!--Create simple category and product--> + <comment userInput="Create simple product and category" stepKey="createCategoryAndProduct"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Create restricted user role--> + <actionGroup ref="AdminAddNewUserRoleWithCustomRoleScopes" stepKey="createLimitedRole"> + <argument name="role" value="restrictedRole"/> + <argument name="customWebsiteName" value="{{SecondStoreGroupUnique.name}}"/> + </actionGroup> + <!--Create admin user with restricted role--> + <comment userInput="Create admin user with restricted role" stepKey="createUserWithRestrictedRole"/> + <actionGroup ref="AdminCreateUserWithRoleActionGroup" stepKey="createRestrictedAdmin"> + <argument name="role" value="restrictedRole"/> + <argument name="user" value="Admin3"/> + </actionGroup> + </before> + <after> + <!--Delete product and category--> + <comment userInput="Delete product and category" stepKey="deleteProdAndCategory"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Delete User--> + <comment userInput="Delete User" stepKey="deleteUser"/> + <actionGroup ref="AdminDeleteCreatedUserActionGroup" stepKey="deleteLimitedUser"> + <argument name="user" value="Admin3"/> + </actionGroup> + <!--Delete users roles--> + <comment userInput="Delete users roles" stepKey="deleteUserRole"/> + <actionGroup ref="AdminDeleteUserRoleWithSpecifiedPermissionActionGroup" stepKey="deleteRestrictedRole"> + <argument name="role" value="restrictedRole"/> + </actionGroup> + <!--Delete store view--> + <comment userInput="Delete store view" stepKey="deleteStoreView"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteFirstStoreView"> + <argument name="customStore" value="customStore"/> + </actionGroup> + </after> + <!--Login as restricted user--> + <comment userInput="Login as restricted user" stepKey="loginAsRestrictedUser"/> + <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"> + <argument name="adminUser" value="Admin3"/> + </actionGroup> + <!--Open Product page--> + <comment userInput="openProductPage" stepKey="openProdPage"/> + <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!--Check that ability to manage category is disabled--> + <comment userInput="Check that ability to manage category is disabled" stepKey="checkAbilityToManageCategory"/> + <grabAttributeFrom userInput="class" selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="grabAttributeFromInput"/> + <assertContains expectedType="string" expected="disabled" actual="$grabAttributeFromInput" stepKey="assertCategoryIsDisabled"/> + <!--Go to created category page--> + <comment userInput="Go to created category page" stepKey="visitAdminCategoryPage"/> + <actionGroup ref="goToAdminCategoryPageById" stepKey="goToAdminCategoryPage0"> + <argument name="id" value="$$createCategory.id$$"/> + </actionGroup> + <!--Expand products in category tab--> + <comment userInput="Expand products in category tab" stepKey="expandProductsInCategoryTab"/> + <conditionalClick selector="{{AdminCategoryProductsSection.sectionHeader}}" dependentSelector="{{AdminCategoryProductsSection.matchByRule}}" visible="false" stepKey="openProductsInCategoryTab"/> + <!--Check that the ability to add product is disabled--> + <comment userInput="Check that the ability to add product is disabled" stepKey="checkAbilityToAddProduct"/> + <seeElement selector="{{AdminCategoryProductsSection.addProductsDisabled}}" stepKey="checkAbilityToAddProductIsDisabled"/> + </test> +</tests> From ccbdcc522bcc1255f1cc7a437b7d2701403893e1 Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Tue, 10 Sep 2019 14:18:54 +0400 Subject: [PATCH 0731/2437] MC-16455: Admin user with permission for 1 store can manage categories - Updated automation test script. --- .../AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml index 8dd4597b0b963..cfb41fed61dec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml @@ -16,6 +16,7 @@ <description value="Ability to manage categories by admin user with permission for 1 store"/> <severity value="CRITICAL"/> <testCaseId value="MC-19264"/> + <useCaseId value="MC-16455"/> <group value="Catalog"/> </annotations> <before> @@ -27,7 +28,7 @@ <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> </actionGroup> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewA"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> <!--Create simple category and product--> <comment userInput="Create simple product and category" stepKey="createCategoryAndProduct"/> <createData entity="_defaultCategory" stepKey="createCategory"/> From 35328a82dc8265f6c299ab958e50979ee4df0797 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Wed, 11 Sep 2019 17:31:47 +0300 Subject: [PATCH 0732/2437] MC-16455: Admin user with permission for 1 store can manage categories - CSV test errors fixed. --- .../Catalog/Model/Category/DataProvider.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index 21a7bfa98bf98..eeb4c082ff51d 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -215,7 +215,7 @@ public function __construct( * @inheritdoc * @since 101.1.0 */ - public function getMeta(): array + public function getMeta() { $meta = parent::getMeta(); $meta = $this->prepareMeta($meta); @@ -274,7 +274,10 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta): ar } /** - * @inheritDoc + * Prepare meta data + * + * @param array $meta + * @return array * @since 101.0.0 */ public function prepareMeta($meta) @@ -320,7 +323,9 @@ private function prepareFieldsMeta(array $fieldsMap, array $fieldsMeta): array } /** - * @inheritDoc + * Get data + * + * @return array * @since 101.0.0 */ public function getData() @@ -411,7 +416,7 @@ public function getAttributesMeta(Type $entityType) * @return array * @since 101.0.0 */ - protected function addUseConfigSettings($categoryData): array + protected function addUseConfigSettings($categoryData) { foreach ($this->elementsWithUseConfigSetting as $elementsWithUseConfigSetting) { if (!isset($categoryData['use_config'][$elementsWithUseConfigSetting])) { @@ -436,7 +441,7 @@ protected function addUseConfigSettings($categoryData): array * @deprecated 101.1.0 * @since 101.0.0 */ - protected function addUseDefaultSettings($category, $categoryData): array + protected function addUseDefaultSettings($category, $categoryData) { if ($category->getExistsStoreValueFlag('url_key') || $category->getStoreId() === Store::DEFAULT_STORE_ID @@ -510,7 +515,7 @@ public function getScopeLabel(EavAttribute $attribute) * @return array * @since 101.0.0 */ - protected function filterFields($categoryData): array + protected function filterFields($categoryData) { return array_diff_key($categoryData, array_flip($this->ignoreFields)); } @@ -558,7 +563,10 @@ private function convertValues($category, $categoryData): array } /** - * @inheritDoc + * Category's fields default values + * + * @param array $result + * @return array * @since 101.0.0 */ public function getDefaultMetaData($result) @@ -572,7 +580,9 @@ public function getDefaultMetaData($result) } /** - * @inheritDoc + * List of fields groups and fields. + * + * @return array * @since 101.0.0 */ protected function getFieldsMap() From f37537b4d23d04d338beee7e515b0514b6dd297c Mon Sep 17 00:00:00 2001 From: Lilit Sargsyan <lilit_sargsyan@epam.com> Date: Thu, 12 Sep 2019 16:20:39 +0400 Subject: [PATCH 0733/2437] MC-16455: Admin user with permission for 1 store can manage categories - Moved test to ee. --- ...oriesByUserWithPermissionFor1StoreTest.xml | 99 ------------------- 1 file changed, 99 deletions(-) delete mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml deleted file mode 100644 index cfb41fed61dec..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminManageCategoriesByUserWithPermissionFor1StoreTest.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminManageCategoriesByUserWithPermissionFor1StoreTest"> - <annotations> - <features value="Catalog"/> - <stories value="user role"/> - <title value="Ability to manage categories by admin user with permission for 1 store"/> - <description value="Ability to manage categories by admin user with permission for 1 store"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-19264"/> - <useCaseId value="MC-16455"/> - <group value="Catalog"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Create new store and store view--> - <comment userInput="Create new store and store view" stepKey="createStoreAndStoreView"/> - <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createStore1"> - <argument name="website" value="Main Website"/> - <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> - <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> - </actionGroup> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> - <!--Create simple category and product--> - <comment userInput="Create simple product and category" stepKey="createCategoryAndProduct"/> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="ApiSimpleProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <!--Create restricted user role--> - <actionGroup ref="AdminAddNewUserRoleWithCustomRoleScopes" stepKey="createLimitedRole"> - <argument name="role" value="restrictedRole"/> - <argument name="customWebsiteName" value="{{SecondStoreGroupUnique.name}}"/> - </actionGroup> - <!--Create admin user with restricted role--> - <comment userInput="Create admin user with restricted role" stepKey="createUserWithRestrictedRole"/> - <actionGroup ref="AdminCreateUserWithRoleActionGroup" stepKey="createRestrictedAdmin"> - <argument name="role" value="restrictedRole"/> - <argument name="user" value="Admin3"/> - </actionGroup> - </before> - <after> - <!--Delete product and category--> - <comment userInput="Delete product and category" stepKey="deleteProdAndCategory"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="logout" stepKey="logout"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Delete User--> - <comment userInput="Delete User" stepKey="deleteUser"/> - <actionGroup ref="AdminDeleteCreatedUserActionGroup" stepKey="deleteLimitedUser"> - <argument name="user" value="Admin3"/> - </actionGroup> - <!--Delete users roles--> - <comment userInput="Delete users roles" stepKey="deleteUserRole"/> - <actionGroup ref="AdminDeleteUserRoleWithSpecifiedPermissionActionGroup" stepKey="deleteRestrictedRole"> - <argument name="role" value="restrictedRole"/> - </actionGroup> - <!--Delete store view--> - <comment userInput="Delete store view" stepKey="deleteStoreView"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteFirstStoreView"> - <argument name="customStore" value="customStore"/> - </actionGroup> - </after> - <!--Login as restricted user--> - <comment userInput="Login as restricted user" stepKey="loginAsRestrictedUser"/> - <actionGroup ref="logout" stepKey="logout"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"> - <argument name="adminUser" value="Admin3"/> - </actionGroup> - <!--Open Product page--> - <comment userInput="openProductPage" stepKey="openProdPage"/> - <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <!--Check that ability to manage category is disabled--> - <comment userInput="Check that ability to manage category is disabled" stepKey="checkAbilityToManageCategory"/> - <grabAttributeFrom userInput="class" selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="grabAttributeFromInput"/> - <assertContains expectedType="string" expected="disabled" actual="$grabAttributeFromInput" stepKey="assertCategoryIsDisabled"/> - <!--Go to created category page--> - <comment userInput="Go to created category page" stepKey="visitAdminCategoryPage"/> - <actionGroup ref="goToAdminCategoryPageById" stepKey="goToAdminCategoryPage0"> - <argument name="id" value="$$createCategory.id$$"/> - </actionGroup> - <!--Expand products in category tab--> - <comment userInput="Expand products in category tab" stepKey="expandProductsInCategoryTab"/> - <conditionalClick selector="{{AdminCategoryProductsSection.sectionHeader}}" dependentSelector="{{AdminCategoryProductsSection.matchByRule}}" visible="false" stepKey="openProductsInCategoryTab"/> - <!--Check that the ability to add product is disabled--> - <comment userInput="Check that the ability to add product is disabled" stepKey="checkAbilityToAddProduct"/> - <seeElement selector="{{AdminCategoryProductsSection.addProductsDisabled}}" stepKey="checkAbilityToAddProductIsDisabled"/> - </test> -</tests> From 0f6773d9e7b27260bd6d7c6327bb9ed3f1f20f3c Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Thu, 26 Sep 2019 20:43:12 +0300 Subject: [PATCH 0734/2437] MC-16455: Admin user with permission for 1 store can manage categories - Fix CR comments --- .../Product/Form/Modifier/Categories.php | 1 + .../Ui/Component/Form/Element/Wysiwyg.php | 4 ---- .../view/base/web/js/form/components/group.js | 18 ------------------ .../view/base/web/js/form/element/ui-select.js | 10 +++------- .../backend/web/css/source/forms/_fields.less | 13 +++++++++---- 5 files changed, 13 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index c395fc49ac073..72d078a26b57d 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -265,6 +265,7 @@ protected function customizeCategoriesField(array $meta) 'chipsEnabled' => true, 'disableLabel' => true, 'levelsVisibility' => '1', + 'disabled' => $fieldIsDisabled, 'elementTmpl' => 'ui/grid/filters/elements/ui-select', 'options' => $this->getCategoriesTree(), 'disabled' => $fieldIsDisabled, diff --git a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php index 7005afbad44d9..040c27d4939ef 100644 --- a/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php +++ b/app/code/Magento/Ui/Component/Form/Element/Wysiwyg.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Ui\Component\Form\Element; @@ -52,9 +51,6 @@ public function __construct( array $config = [] ) { $wysiwygConfigData = isset($config['wysiwygConfigData']) ? $config['wysiwygConfigData'] : []; - if (isset($config['disabled']) && $config['disabled'] === true) { - $config['wysiwygConfigData']['settings']['readonly'] = 1; - } $this->form = $formFactory->create(); $wysiwygId = $context->getNamespace() . '_' . $data['name']; diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/group.js b/app/code/Magento/Ui/view/base/web/js/form/components/group.js index f74bf3f5e9195..8c5950f7e2fa1 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/group.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/group.js @@ -36,24 +36,6 @@ define([ return this; }, - /** - * @inheritdoc - */ - initElement: function (elem) { - this._super(); - - if (this.disabled) { - try { - elem.disabled(true); - } - catch (e) { - - } - } - - return this; - }, - /** * Calls initObservable of parent class. * Defines observable properties of instance. diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index 9d38067d332fc..5667ce5d71d81 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -568,10 +568,8 @@ define([ * Remove element from selected array */ removeSelected: function (value, data, event) { - if (!this.disabled()) { - event ? event.stopPropagation() : false; - this.value.remove(value); - } + event ? event.stopPropagation() : false; + this.value.remove(value); }, /** @@ -663,9 +661,7 @@ define([ * @returns {Object} Chainable */ toggleListVisible: function () { - if (!this.disabled()) { - this.listVisible(!this.listVisible()); - } + this.listVisible(!this.listVisible()); return this; }, diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less index 08aeb35d7adb2..51402692075e9 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less @@ -156,7 +156,7 @@ .admin__field { margin-top: 8px; } - } + } } } &.composite-bundle { @@ -307,7 +307,7 @@ .admin__fieldset > & { margin-bottom: 3rem; position: relative; - + &.field-import_file { .input-file { margin-top: 6px; @@ -361,6 +361,11 @@ cursor: inherit; opacity: 1; outline: inherit; + .admin__action-multiselect-wrap { + .admin__action-multiselect { + .__form-control-pattern__disabled(); + } + } } &._hidden { @@ -664,7 +669,7 @@ display: inline-block; } } - + + .admin__field:last-child { width: auto; @@ -700,7 +705,7 @@ width: 100%; } } - & > .admin__field-label { + & > .admin__field-label { text-align: left; } From 62c3d35e3b70260071168e0f5ab9076a5da3fa46 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Fri, 27 Sep 2019 12:41:48 +0300 Subject: [PATCH 0735/2437] MC-16455: Admin user with permission for 1 store can manage categories - Unit test errors fix - Health index test errors fix --- .../Product/Form/Modifier/CategoriesTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php index 932b09f7df9cb..bceafee0f82a4 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php @@ -10,7 +10,6 @@ use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Categories; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; -use Magento\Framework\App\CacheInterface; use Magento\Framework\DB\Helper as DbHelper; use Magento\Framework\UrlInterface; use Magento\Store\Model\Store; @@ -161,7 +160,14 @@ public function testModifyMetaLocked($locked) ->willReturnArgument(2); $modifyMeta = $this->createModel()->modifyMeta($meta); - $this->assertEquals($locked, $modifyMeta['arguments']['data']['config']['disabled']); + $this->assertEquals( + $locked, + $modifyMeta['children']['category_ids']['arguments']['data']['config']['disabled'] + ); + $this->assertEquals( + $locked, + $modifyMeta['children']['create_category_button']['arguments']['data']['config']['disabled'] + ); } /** From 2a29bec42aca484288b08460e478100eb4d75f47 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Mon, 30 Sep 2019 17:18:24 -0500 Subject: [PATCH 0736/2437] MC-20622: Email sent from admin order edit includes incorrect static file paths - Fix unable to set local though \Magento\Backend\Model\Locale\Resolver::setLocale() --- .../Magento/Backend/Model/Locale/Resolver.php | 6 ++- .../Backend/Model/Locale/ResolverTest.php | 45 ++++++++++++++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Backend/Model/Locale/Resolver.php b/app/code/Magento/Backend/Model/Locale/Resolver.php index b9be471cd5990..9086e2af83e24 100644 --- a/app/code/Magento/Backend/Model/Locale/Resolver.php +++ b/app/code/Magento/Backend/Model/Locale/Resolver.php @@ -7,8 +7,10 @@ /** * Backend locale model + * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Resolver extends \Magento\Framework\Locale\Resolver { @@ -40,7 +42,7 @@ class Resolver extends \Magento\Framework\Locale\Resolver * @param Manager $localeManager * @param \Magento\Framework\App\RequestInterface $request * @param \Magento\Framework\Validator\Locale $localeValidator - * @param null $locale + * @param string|null $locale * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -76,7 +78,7 @@ public function setLocale($locale = null) $sessionLocale = $this->_session->getSessionLocale(); $userLocale = $this->_localeManager->getUserInterfaceLocale(); - $localeCodes = array_filter([$forceLocale, $sessionLocale, $userLocale]); + $localeCodes = array_filter([$forceLocale, $locale, $sessionLocale, $userLocale]); if (count($localeCodes)) { $locale = reset($localeCodes); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php index 88662a65c7428..a930244238efa 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php @@ -20,6 +20,9 @@ class ResolverTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * {@inheritDoc} + */ protected function setUp() { parent::setUp(); @@ -29,7 +32,7 @@ protected function setUp() } /** - * @covers \Magento\Backend\Model\Locale\Resolver::setLocale + * Tests setLocale() with default locale */ public function testSetLocaleWithDefaultLocale() { @@ -37,7 +40,7 @@ public function testSetLocaleWithDefaultLocale() } /** - * @covers \Magento\Backend\Model\Locale\Resolver::setLocale + * Tests setLocale() with interface locale */ public function testSetLocaleWithBaseInterfaceLocale() { @@ -55,7 +58,7 @@ public function testSetLocaleWithBaseInterfaceLocale() } /** - * @covers \Magento\Backend\Model\Locale\Resolver::setLocale + * Tests setLocale() with session locale */ public function testSetLocaleWithSessionLocale() { @@ -68,7 +71,7 @@ public function testSetLocaleWithSessionLocale() } /** - * @covers \Magento\Backend\Model\Locale\Resolver::setLocale + * Tests setLocale() with post parameter */ public function testSetLocaleWithRequestLocale() { @@ -78,13 +81,45 @@ public function testSetLocaleWithRequestLocale() $this->_checkSetLocale('de_DE'); } + /** + * Tests setLocale() with parameter + * + * @param string|null $localeParam + * @param string|null $localeRequestParam + * @param string $localeExpected + * @dataProvider setLocaleWithParameterDataProvider + */ + public function testSetLocaleWithParameter( + ?string $localeParam, + ?string $localeRequestParam, + string $localeExpected + ) { + $request = Bootstrap::getObjectManager() + ->get(\Magento\Framework\App\RequestInterface::class); + $request->setPostValue(['locale' => $localeRequestParam]); + $this->_model->setLocale($localeParam); + $this->assertEquals($localeExpected, $this->_model->getLocale()); + } + + /** + * @return array + */ + public function setLocaleWithParameterDataProvider(): array + { + return [ + ['ko_KR', 'ja_JP', 'ja_JP'], + ['ko_KR', null, 'ko_KR'], + [null, 'ja_JP', 'ja_JP'], + ]; + } + /** * Check set locale * * @param string $localeCodeToCheck * @return void */ - protected function _checkSetLocale($localeCodeToCheck) + private function _checkSetLocale($localeCodeToCheck) { $this->_model->setLocale(); $localeCode = $this->_model->getLocale(); From ad5682cda9d265a44dc6203d07b5a61aff2fbdc1 Mon Sep 17 00:00:00 2001 From: Mykola Orlenko <mo@integer-net.de> Date: Tue, 1 Oct 2019 16:03:44 +0200 Subject: [PATCH 0737/2437] [BUGFIX] DesignTheme should be initialized before all images --- .../Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index 4fcb87ab1396e..9fadb22bb15db 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -107,6 +107,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ $images = []; /** @var ThemeInterface $currentTheme */ $currentTheme = $this->design->getDesignTheme(); + $this->design->setDesignTheme($currentTheme); foreach ($this->imageCodes as $imageCode) { /** @var ImageInterface $image */ @@ -135,7 +136,6 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ $images[] = $image; } - $this->design->setDesignTheme($currentTheme); $productRender->setImages($images); } From f3bc78e8d41f9bd915e21ec87e8e57432fb04c58 Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets <yauhen_lyskavets@epam.com> Date: Tue, 1 Oct 2019 17:22:29 +0300 Subject: [PATCH 0738/2437] MC-16455: Admin user with permission for 1 store can manage categories - Static tests errors fix --- .../Ui/DataProvider/Product/Form/Modifier/Categories.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index 72d078a26b57d..c4ca5eca8e880 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -268,7 +268,6 @@ protected function customizeCategoriesField(array $meta) 'disabled' => $fieldIsDisabled, 'elementTmpl' => 'ui/grid/filters/elements/ui-select', 'options' => $this->getCategoriesTree(), - 'disabled' => $fieldIsDisabled, 'listens' => [ 'index=create_category:responseData' => 'setParsed', 'newOption' => 'toggleOptionSelected' @@ -322,11 +321,7 @@ protected function customizeCategoriesField(array $meta) ] ]; } - $meta = $this->arrayManager->merge( - $containerPath, - $meta, - $value - ); + $meta = $this->arrayManager->merge($containerPath, $meta, $value); return $meta; } From ab36bbd9afd1e7866e9e5707959a4d338e7a9241 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Tue, 1 Oct 2019 09:27:38 -0500 Subject: [PATCH 0739/2437] MC-19639: Admin Analytics modal allows user to navigate the admin - MC-19638: User can dismiss the Admin Analytics modal with ESC key --- .../ui_component/admin_usage_notification.xml | 2 +- .../view/adminhtml/web/js/modal/component.js | 80 ++++++++++++++++--- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 3c35f1937783b..fcd1c4ebbbcdf 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -85,7 +85,7 @@ <item name="text" xsi:type="string" translate="true"><![CDATA[ <p>Help us improve Magento Admin by allowing us to collect usage data.</p> <p>All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.</p> - <p>You can learn more and opt out at any time by following the instructions in <a href="https://docs.magento.com/m2/ce/user_guide/stores/admin.html" target="_blank">merchant documentation</a>.</p> + <p>You can learn more and opt out at any time by following the instructions in <a href="https://docs.magento.com/m2/ce/user_guide/stores/admin.html" target="_blank" tabindex="0">merchant documentation</a>.</p> ]]></item> </item> </argument> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index bc09890d0d0b4..dedab9f379525 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -20,16 +20,7 @@ define([ enableLogAction: '${ $.provider }:data.enableLogAction', disableLogAction: '${ $.provider }:data.disableLogAction' }, - options: { - keyEventHandlers: { - /** - * Prevents escape key from exiting out of modal - */ - escapeKey: function () { - return; - } - } - }, + options: {}, notificationWindow: null }, @@ -41,11 +32,32 @@ define([ this._super(); }, + /** + * Configure ESC and TAB so user can't leave modal + * without selecting an option + * + * @returns {Object} Chainable. + */ + initModalEvents: function () { + this._super(); + //Don't allow ESC key to close modal + this.options.keyEventHandlers.escapeKey = function(e){e.preventDefault()}; + //Restrict tab action to the modal + this.options.keyEventHandlers.tabKey = this.handleTabKey.bind(this); + + return this; + }, + /** * Once the modal is opened it hides the X */ onOpened: function () { - $('.modal-header button.action-close').hide(); + $('.modal-header button.action-close').attr("disabled", true).hide(); + + this.focusableElements = $(this.rootSelector).find("a[href], button:enabled"); + this.firstFocusableElement = this.focusableElements[0]; + this.lastFocusableElement = this.focusableElements[this.focusableElements.length - 1]; + this.firstFocusableElement.focus(); }, /** @@ -104,10 +116,52 @@ define([ * Allows admin usage popup to be shown first and then new release notification */ openReleasePopup: function () { - var notifiModal = registry.get('release_notification.release_notification.notification_modal_1'); + var notificationModal = registry.get('release_notification.release_notification.notification_modal_1'); if (analyticsPopupConfig.releaseVisible) { - notifiModal.initializeContentAfterAnalytics(); + notificationModal.initializeContentAfterAnalytics(); + } + }, + + /** + * Handle Tab and Shift+Tab key event + * + * Keep the tab actions restricted to the popup modal + * so the user must select an option to dismiss the modal + */ + handleTabKey: function(event) { + var modal = this; + var KEY_TAB = 9; + + function handleBackwardTab() { + if ( document.activeElement === modal.firstFocusableElement + || document.activeElement === $(modal.rootSelector)[0] + ) { + event.preventDefault(); + modal.lastFocusableElement.focus(); + } + } + function handleForwardTab() { + if ( document.activeElement === modal.lastFocusableElement) { + event.preventDefault(); + modal.firstFocusableElement.focus(); + } + } + + switch(event.keyCode) { + case KEY_TAB: + if ( modal.focusableElements.length === 1 ) { + event.preventDefault(); + break; + } + if ( event.shiftKey ) { + handleBackwardTab(); + break; + } + handleForwardTab(); + break; + default: + break; } } } From 424321e5a055e256d21d0764660c658c827f0369 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Tue, 1 Oct 2019 11:54:22 +0300 Subject: [PATCH 0740/2437] MAGETWO-65232: Product name does not display special characters properly - Escape html output --- .../Product/Modifier/Attributes.php | 64 +++++++++++++++++++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 19 +++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/Attributes.php diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/Attributes.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/Attributes.php new file mode 100644 index 0000000000000..01aaa6f8e0629 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Modifier/Attributes.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Modifier; + +use Magento\Framework\Escaper; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; + +/** + * Modify product listing attributes + */ +class Attributes implements ModifierInterface +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @var array + */ + private $escapeAttributes; + + /** + * @param Escaper $escaper + * @param array $escapeAttributes + */ + public function __construct( + Escaper $escaper, + array $escapeAttributes = [] + ) { + $this->escaper = $escaper; + $this->escapeAttributes = $escapeAttributes; + } + + /** + * @inheritdoc + */ + public function modifyData(array $data) + { + if (!empty($data) && !empty($this->escapeAttributes)) { + foreach ($data['items'] as &$item) { + foreach ($this->escapeAttributes as $escapeAttribute) { + if (isset($item[$escapeAttribute])) { + $item[$escapeAttribute] = $this->escaper->escapeHtml($item[$escapeAttribute]); + } + } + } + } + return $data; + } + + /** + * @inheritdoc + */ + public function modifyMeta(array $meta) + { + return $meta; + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c04cfb2dce00a..b3c9230b3cfc6 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -166,7 +166,24 @@ <argument name="pool" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool</argument> </arguments> </type> - <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Listing\Modifier\Pool" type="Magento\Ui\DataProvider\Modifier\Pool"/> + <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Listing\Modifier\Pool" type="Magento\Ui\DataProvider\Modifier\Pool"> + <arguments> + <argument name="modifiers" xsi:type="array"> + <item name="attributes" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Modifier\Attributes</item> + <item name="sortOrder" xsi:type="number">10</item> + </item> + </argument> + </arguments> + </virtualType> + <type name="Magento\Catalog\Ui\DataProvider\Product\Modifier\Attributes"> + <arguments> + <argument name="escapeAttributes" xsi:type="array"> + <item name="name" xsi:type="string">name</item> + <item name="sku" xsi:type="string">sku</item> + </argument> + </arguments> + </type> <type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions"> <arguments> <argument name="scopeName" xsi:type="string">product_form.product_form</argument> From 86f3fdbabbfd5437a6811bad07c9d04d59370aaf Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Tue, 1 Oct 2019 10:25:31 -0500 Subject: [PATCH 0741/2437] MC-20536: Make remaining failed MTF tests running with MySQL only --- .../Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml | 6 ++++++ ...tAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml | 2 ++ 2 files changed, 8 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml index 0d1925ef538af..02d3051ae1a8a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml @@ -154,6 +154,8 @@ <after> <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> @@ -228,6 +230,8 @@ <after> <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> @@ -302,6 +306,8 @@ <after> <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> </after> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml index 8a7d7d5394ee6..a8e982475253f 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml @@ -81,6 +81,8 @@ <after> <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> </after> </test> </tests> From 076295d4069e83b47f38c726cf25e1bb1efa805c Mon Sep 17 00:00:00 2001 From: Krishna Abothu <abothu@adobe.com> Date: Tue, 1 Oct 2019 11:16:18 -0500 Subject: [PATCH 0742/2437] MC-20644: Update MFTF test failures for Catalog Rules and Sales Modules --- .../Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 1ad736ade37fc..4310d412d1c98 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -55,6 +55,9 @@ <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!--Run re-index task--> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <!--Go to bundle product page--> <amOnPage url="{{StorefrontProductPage.url($$createCategory.name$$)}}" stepKey="navigateToBundleProductPage"/> From 2c926f032a4f1197b9aa6f52d4abeddc6e511e97 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 1 Oct 2019 11:57:22 -0500 Subject: [PATCH 0743/2437] MC-15986: Category Filtering - added new schema changes from approved proposal --- .../Magento/CatalogGraphQl/etc/schema.graphqls | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 24a2123ff60f7..66e6fb68728fd 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -12,9 +12,11 @@ type Query { @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") category ( id: Int @doc(description: "Id of the category.") - filter: CategoryFilterInput @doc(description: "Identifies which Category inputs to search for and return."), ): CategoryTree - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @deprecated(reason: "Use 'categoryList' query instead of 'category' query") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") + categoryList( + filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") + ): [CategoryList] @doc(description: "Categories filter.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") } type Price @doc(description: "The Price object defines the price of a product as well as any tax-related adjustments.") { @@ -278,10 +280,11 @@ input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput category_id: FilterEqualTypeInput @doc(description: "Filter product by category id") } -input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { - id: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies and filters the category."), - url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category"), - name: FilterEqualTypeInput @doc(description: "Filter by the display name of the category.") +input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") +{ + ids: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies and filters the category.") + url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category") + name: FilterMatchTypeInput @doc(description: "Filter by the display name of the category.") } input ProductFilterInput @doc(description: "ProductFilterInput is deprecated, use @ProductAttributeFilterInput instead. ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { From 2b0558bf9922a32e904e12cae67ea944569eebe7 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 1 Oct 2019 12:08:15 -0500 Subject: [PATCH 0744/2437] MC-20555: Add "Search Engine" parameter in Jenkins build --- .../Magento/TestModuleCatalogSearch/etc/module.xml | 14 ++++++++++++++ .../TestModuleCatalogSearch/registration.php | 12 ++++++++++++ .../Magento/TestModuleCatalogSearch/etc/module.xml | 14 ++++++++++++++ .../TestModuleCatalogSearch/registration.php | 12 ++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/etc/module.xml create mode 100644 dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/registration.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleCatalogSearch/etc/module.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleCatalogSearch/registration.php diff --git a/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/etc/module.xml b/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/etc/module.xml new file mode 100644 index 0000000000000..bae0739d237e1 --- /dev/null +++ b/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleCatalogSearch"> + <sequence> + <module name="Magento_CatalogSearch"/> + </sequence> + </module> +</config> diff --git a/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/registration.php b/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/registration.php new file mode 100644 index 0000000000000..78fb97a9e1134 --- /dev/null +++ b/dev/tests/api-functional/_files/Magento/TestModuleCatalogSearch/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCatalogSearch') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCatalogSearch', __DIR__); +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/etc/module.xml new file mode 100644 index 0000000000000..bae0739d237e1 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleCatalogSearch"> + <sequence> + <module name="Magento_CatalogSearch"/> + </sequence> + </module> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/registration.php b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/registration.php new file mode 100644 index 0000000000000..78fb97a9e1134 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCatalogSearch/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCatalogSearch') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCatalogSearch', __DIR__); +} From 6d642f83ad5551d5adc52930a227e66b87ddf480 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Tue, 1 Oct 2019 12:56:27 -0500 Subject: [PATCH 0745/2437] MC-18783: IE11 Checkout Error; Indefinite loading; Cant complete check out - overridden window clearTimeout function should take a parameter --- .../view/frontend/web/js/view/payment/list-mixin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js index 60172f696e9ed..8abd57cc69132 100644 --- a/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js +++ b/app/code/Magento/PaypalCaptcha/view/frontend/web/js/view/payment/list-mixin.js @@ -34,14 +34,16 @@ define([ /** * Overrides default window.clearTimeout() to catch errors from iframe and reload Captcha. + * + * @param {Number} timeoutID */ - clearTimeout: function () { + clearTimeout: function (timeoutID) { var captcha = captchaList.getCaptchaByFormId(this.formId); if (captcha !== null) { captcha.refresh(); } - clearTimeout(); + clearTimeout(timeoutID); } }; From 33772d26cf4afa7c2436023d6fa1fbfa0a88b055 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Tue, 1 Oct 2019 13:15:08 -0500 Subject: [PATCH 0746/2437] MC-20536: Make remaining failed MTF tests running with MySQL only --- .../Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml index 02d3051ae1a8a..0370280309272 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml @@ -80,6 +80,8 @@ <after> <deleteData createDataKey="simple1Handle" stepKey="deleteSimple1" before="deleteSimple2"/> <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> + <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> + <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> From 6879694ef4735ae2e6e15986660af05a4b047098 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Tue, 1 Oct 2019 13:41:01 -0500 Subject: [PATCH 0747/2437] MC-20536: Make remaining failed MTF tests running with MySQL only --- .../LayeredNavigation/Test/TestCase/FilterProductListTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml index 72b89916a08df..48129ef287498 100644 --- a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml +++ b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/TestCase/FilterProductListTest.xml @@ -94,6 +94,7 @@ <constraint name="Magento\LayeredNavigation\Test\Constraint\AssertFilterProductList" /> </variation> <variation name="FilterProductListTestVariation4" summary="Use sorting category filter when layered navigation is applied" ticketId="MAGETWO-42701"> + <data name="tag" xsi:type="string">test_type:mysql_search</data> <data name="configData" xsi:type="string">layered_navigation_manual_range_10</data> <data name="runReindex" xsi:type="boolean">true</data> <data name="category/dataset" xsi:type="string">default_anchor_subcategory</data> From a0c79b6aa4c856e2ca4da24ac854e748bee7fa84 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 1 Oct 2019 14:00:22 -0500 Subject: [PATCH 0748/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/Backend/Customlayoutupdate.php | 29 +++++++++++++- .../Magento/Cms/Model/Page/DataProvider.php | 38 ++++++++++-------- app/code/Magento/Cms/Model/PageRepository.php | 40 ++++++++++++------- .../Controller/Adminhtml/Page/SaveTest.php | 5 ++- .../Controller/Adminhtml/ProductTest.php | 1 + .../Backend/CustomlayoutupdateTest.php | 26 ++++++++++++ 6 files changed, 104 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index 759bdb54439b6..b5aa5e2035100 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -51,6 +51,31 @@ private function extractValue(AbstractModel $object) return $value; } + /** + * Extract old attribute value. + * + * @param AbstractModel $object + * @return mixed Old value or null. + */ + private function extractOldValue(AbstractModel $object) + { + if (!empty($object->getId())) { + $attr = $this->getAttribute()->getAttributeCode(); + + if ($object->getOrigData()) { + return $object->getOrigData($attr); + } + + $oldObject = clone $object; + $oldObject->unsetData(); + $oldObject->load($object->getId()); + + return $oldObject->getData($attr); + } + + return null; + } + /** * @inheritDoc * @@ -59,10 +84,10 @@ private function extractValue(AbstractModel $object) public function validate($object) { if (parent::validate($object)) { - $attrCode = $this->getAttribute()->getAttributeCode(); if ($object instanceof AbstractModel) { $value = $this->extractValue($object); - if ($value && $object->getOrigData($attrCode) !== $value) { + $oldValue = $this->extractOldValue($object); + if ($value && $oldValue !== $value) { throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); } } diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index b9fb05e0f39e4..e75097ca163e4 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -5,6 +5,7 @@ */ namespace Magento\Cms\Model\Page; +use Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; @@ -84,6 +85,20 @@ public function __construct( ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); } + /** + * Find requested page. + * + * @return Page|null + */ + private function findCurrentPage(): ?Page + { + if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { + return $this->collection->getItemById($pageId); + } + + return null; + } + /** * Prepares Meta * @@ -162,25 +177,14 @@ public function getMeta() //List of custom layout files available for current page. $options = [['label' => 'No update', 'value' => '']]; - if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { + if ($page = $this->findCurrentPage()) { //We must have a specific page selected. - //Finding our page. - $found = null; - /** @var \Magento\Cms\Model\Page $page */ - foreach ($this->collection->getItems() as $page) { - if ($page->getId() == $pageId) { - $found = $page; - break; - } - } //If custom layout XML is set then displaying this special option. - if ($found) { - if ($found->getCustomLayoutUpdateXml() || $found->getLayoutUpdateXml()) { - $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; - } - foreach ($this->customLayoutManager->fetchAvailableFiles($found) as $layoutFile) { - $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; - } + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { + $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; + } + foreach ($this->customLayoutManager->fetchAvailableFiles($page) as $layoutFile) { + $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; } } $customLayoutMeta = [ diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index b555123af8967..72f07771f59d4 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -113,6 +113,30 @@ public function __construct( $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); } + /** + * Validate new layout update values. + * + * @param Data\PageInterface $page + * @return void + * @throws \InvalidArgumentException + */ + private function validateLayoutUpdate(Data\PageInterface $page): void + { + //Persisted data + $savedPage = $page->getId() ? $this->getById($page->getId()) : null; + //Custom layout update can be removed or kept as is. + if ($page->getCustomLayoutUpdateXml() + && (!$savedPage || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml()) + ) { + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); + } + if ($page->getLayoutUpdateXml() + && (!$savedPage || $page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml()) + ) { + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); + } + } + /** * Save Page data * @@ -127,21 +151,7 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $page->setStoreId($storeId); } try { - //Persisted data - $savedPage = $page->getId() ? $this->getById($page->getId()) : null; - - //Custom layout update must be selected from available files - if ($page->getCustomLayoutUpdateXml() - && (!$savedPage || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml()) - ) { - throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); - } - if ($page->getLayoutUpdateXml() - && (!$savedPage || $page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml()) - ) { - throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); - } - + $this->validateLayoutUpdate($page); $this->resource->save($page); $this->identityMap->add($page); } catch (\Exception $exception) { diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php index bc6703eaf4426..15ba72154643c 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php @@ -295,7 +295,10 @@ public function testSaveActionThrowsException() $this->dataPersistorMock->expects($this->any()) ->method('set') - ->with('cms_page', ['page_id' => $this->pageId, 'custom_layout_update_xml' => null]); + ->with( + 'cms_page', + ['page_id' => $this->pageId, 'custom_layout_update_xml' => null, 'layout_update_xml' => null] + ); $this->resultRedirect->expects($this->atLeastOnce()) ->method('setPath') diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 1f2569a6ffdb6..3c9c00617f7de 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -19,6 +19,7 @@ /** * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendController { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php index 308a577a6aa6f..7f594d265418f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -61,6 +61,7 @@ protected function setUp() * * @return void * @throws \Throwable + * @magentoDbIsolation enabled */ public function testImmutable(): void { @@ -94,5 +95,30 @@ public function testImmutable(): void $this->category->setOrigData('custom_layout_update', 'test'); $this->attribute->beforeSave($this->category); $this->assertNull($this->category->getCustomAttribute('custom_layout_update')->getValue()); + + //Using old stored value + //Saving old value 1st + $this->recreateCategory(); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData('custom_layout_update', 'test'); + $this->category->save(); + $this->recreateCategory(); + $this->category = $this->categoryFactory->create(['data' => $this->category->getData()]); + + //Trying the same value. + $this->category->setData('custom_layout_update', 'test'); + $this->attribute->beforeSave($this->category); + //Trying new value + $this->category->setData('custom_layout_update', 'test2'); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + //Empty value + $this->category->setData('custom_layout_update', null); + $this->attribute->beforeSave($this->category); } } From 854a9d6d8b3e46aee985901d2acf4300fa513c42 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 1 Oct 2019 15:05:21 -0500 Subject: [PATCH 0749/2437] MC-15986: Category Filtering - added change to the query in schema --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 66e6fb68728fd..5e8f5590bbd35 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -16,7 +16,7 @@ type Query { @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @deprecated(reason: "Use 'categoryList' query instead of 'category' query") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") categoryList( filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") - ): [CategoryList] @doc(description: "Categories filter.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") + ): [CategoryTree] @doc(description: "Categories filter.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") } type Price @doc(description: "The Price object defines the price of a product as well as any tax-related adjustments.") { From 671d4bd099e6b597812eb087ba83fcc6e1fc1b7c Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Tue, 1 Oct 2019 15:41:11 -0500 Subject: [PATCH 0750/2437] MC-20709: [On PREM] Checkboxes inside widgets are not fully clickable in admin - fixed --- .../Magento/backend/Magento_Banner/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/Magento_Banner/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Banner/web/css/source/_module.less index d9e2cfdd66bf7..dd67220db12da 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Banner/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Banner/web/css/source/_module.less @@ -24,6 +24,7 @@ input[type='checkbox'].banner-content-checkbox { } .adminhtml-widget_instance-edit, +.adminhtml-cms_page-edit, .adminhtml-banner-edit { .admin__fieldset { .admin__field-control { From f52d0fac70114ccba75dd9c06f70af8a865c086a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Tue, 1 Oct 2019 22:42:50 +0200 Subject: [PATCH 0751/2437] Fix #13278 - Wrong return type in Salesrule Module RuleInterface.php --- .../SalesRule/Api/Data/RuleInterface.php | 24 +++++++++-------- .../Magento/SalesRule/Model/Data/Rule.php | 27 +++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/SalesRule/Api/Data/RuleInterface.php b/app/code/Magento/SalesRule/Api/Data/RuleInterface.php index 34341e16cfb64..b811289798e31 100644 --- a/app/code/Magento/SalesRule/Api/Data/RuleInterface.php +++ b/app/code/Magento/SalesRule/Api/Data/RuleInterface.php @@ -5,13 +5,15 @@ */ namespace Magento\SalesRule\Api\Data; +use Magento\Framework\Api\ExtensibleDataInterface; + /** * Interface RuleInterface * * @api * @since 100.0.2 */ -interface RuleInterface extends \Magento\Framework\Api\ExtensibleDataInterface +interface RuleInterface extends ExtensibleDataInterface { const FREE_SHIPPING_NONE = 'NONE'; const FREE_SHIPPING_MATCHING_ITEMS_ONLY = 'MATCHING_ITEMS_ONLY'; @@ -59,14 +61,14 @@ public function setName($name); /** * Get display label * - * @return \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null + * @return RuleLabelInterface[]|null */ public function getStoreLabels(); /** * Set display label * - * @param \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null $storeLabels + * @param RuleLabelInterface[]|null $storeLabels * @return $this */ public function setStoreLabels(array $storeLabels = null); @@ -173,21 +175,21 @@ public function getIsActive(); * Set whether the coupon is active * * @param bool $isActive - * @return bool + * @return $this */ public function setIsActive($isActive); /** * Get condition for the rule * - * @return \Magento\SalesRule\Api\Data\ConditionInterface|null + * @return ConditionInterface|null */ public function getCondition(); /** * Set condition for the rule * - * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $condition + * @param ConditionInterface|null $condition * @return $this */ public function setCondition(ConditionInterface $condition = null); @@ -195,14 +197,14 @@ public function setCondition(ConditionInterface $condition = null); /** * Get action condition * - * @return \Magento\SalesRule\Api\Data\ConditionInterface|null + * @return ConditionInterface|null */ public function getActionCondition(); /** * Set action condition * - * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $actionCondition + * @param ConditionInterface|null $actionCondition * @return $this */ public function setActionCondition(ConditionInterface $actionCondition = null); @@ -436,15 +438,15 @@ public function setSimpleFreeShipping($simpleFreeShipping); /** * Retrieve existing extension attributes object or create a new one. * - * @return \Magento\SalesRule\Api\Data\RuleExtensionInterface|null + * @return RuleExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param \Magento\SalesRule\Api\Data\RuleExtensionInterface $extensionAttributes + * @param RuleExtensionInterface $extensionAttributes * @return $this */ - public function setExtensionAttributes(\Magento\SalesRule\Api\Data\RuleExtensionInterface $extensionAttributes); + public function setExtensionAttributes(RuleExtensionInterface $extensionAttributes); } diff --git a/app/code/Magento/SalesRule/Model/Data/Rule.php b/app/code/Magento/SalesRule/Model/Data/Rule.php index 58520831c016b..869822ab917cd 100644 --- a/app/code/Magento/SalesRule/Model/Data/Rule.php +++ b/app/code/Magento/SalesRule/Model/Data/Rule.php @@ -5,10 +5,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\SalesRule\Model\Data; +use Magento\Framework\Api\AbstractExtensibleObject; use Magento\SalesRule\Api\Data\ConditionInterface; +use Magento\SalesRule\Api\Data\RuleExtensionInterface; use Magento\SalesRule\Api\Data\RuleInterface; +use Magento\SalesRule\Api\Data\RuleLabelInterface; /** * Class Rule @@ -16,7 +21,7 @@ * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @codeCoverageIgnore */ -class Rule extends \Magento\Framework\Api\AbstractExtensibleObject implements RuleInterface +class Rule extends AbstractExtensibleObject implements RuleInterface { const KEY_RULE_ID = 'rule_id'; const KEY_NAME = 'name'; @@ -187,7 +192,7 @@ public function getIsActive() * Set whether the coupon is active * * @param bool $isActive - * @return bool + * @return $this */ public function setIsActive($isActive) { @@ -197,7 +202,7 @@ public function setIsActive($isActive) /** * Get condition for the rule * - * @return \Magento\SalesRule\Api\Data\ConditionInterface|null + * @return ConditionInterface|null */ public function getCondition() { @@ -207,7 +212,7 @@ public function getCondition() /** * Set condition for the rule * - * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $condition + * @param ConditionInterface|null $condition * @return $this */ public function setCondition(ConditionInterface $condition = null) @@ -218,7 +223,7 @@ public function setCondition(ConditionInterface $condition = null) /** * Get action condition * - * @return \Magento\SalesRule\Api\Data\ConditionInterface|null + * @return ConditionInterface|null */ public function getActionCondition() { @@ -228,7 +233,7 @@ public function getActionCondition() /** * Set action condition * - * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $actionCondition + * @param ConditionInterface|null $actionCondition * @return $this */ public function setActionCondition(ConditionInterface $actionCondition = null) @@ -283,7 +288,7 @@ public function setIsAdvanced($isAdvanced) /** * Get display label * - * @return \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null + * @return RuleLabelInterface[]|null */ public function getStoreLabels() { @@ -293,7 +298,7 @@ public function getStoreLabels() /** * Set display label * - * @param \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null $storeLabels + * @param RuleLabelInterface[]|null $storeLabels * @return $this */ public function setStoreLabels(array $storeLabels = null) @@ -622,7 +627,7 @@ public function setSimpleFreeShipping($simpleFreeShipping) /** * @inheritdoc * - * @return \Magento\SalesRule\Api\Data\RuleExtensionInterface|null + * @return RuleExtensionInterface|null */ public function getExtensionAttributes() { @@ -632,11 +637,11 @@ public function getExtensionAttributes() /** * @inheritdoc * - * @param \Magento\SalesRule\Api\Data\RuleExtensionInterface $extensionAttributes + * @param RuleExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes( - \Magento\SalesRule\Api\Data\RuleExtensionInterface $extensionAttributes + RuleExtensionInterface $extensionAttributes ) { return $this->_setExtensionAttributes($extensionAttributes); } From a500c3a4686fe7707889ea1f9638beb208bd4bef Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 1 Oct 2019 18:43:39 -0500 Subject: [PATCH 0752/2437] MC-18685: Remove custom layout updates from admin --- .../Attribute/LayoutUpdateManager.php | 26 +++++++++++++++---- .../Product/Attribute/LayoutUpdateManager.php | 23 ++++++++++++++-- app/code/Magento/Cms/Helper/Page.php | 12 ++++++++- .../Page/CustomLayout/CustomLayoutManager.php | 16 ++++++++++-- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php index 69c5f961cb96f..6cf8e93f2c3f1 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php @@ -9,13 +9,13 @@ namespace Magento\Catalog\Model\Category\Attribute; use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category; use Magento\Framework\App\Area; use Magento\Framework\DataObject; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; -use Magento\Framework\View\Result\Page as PageLayout; /** * Manage available layout updates for categories. @@ -113,6 +113,24 @@ function (string $handle) use ($category) : ?string { ); } + /** + * Extract custom layout attribute value. + * + * @param CategoryInterface $category + * @return mixed + */ + private function extractAttributeValue(CategoryInterface $category) + { + if ($category instanceof Category && $category->hasData('custom_layout_update_file')) { + return $category->getData('custom_layout_update_file'); + } + if ($attr = $category->getCustomAttribute('custom_layout_update_file')) { + return $attr->getValue(); + } + + return null; + } + /** * Extract selected custom layout settings. * @@ -124,13 +142,11 @@ function (string $handle) use ($category) : ?string { */ public function extractCustomSettings(CategoryInterface $category, DataObject $intoSettings): void { - if ($category->getId() - && $attribute = $category->getCustomAttribute('custom_layout_update_file') - ) { + if ($category->getId() && $value = $this->extractAttributeValue($category)) { $handles = $intoSettings->getPageLayoutHandles() ?? []; $handles = array_merge_recursive( $handles, - ['selectable' => $category->getId() . '_' . $attribute->getValue()] + ['selectable' => $category->getId() . '_' . $value] ); $intoSettings->setPageLayoutHandles($handles); } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php index 12f7118924268..91f84f88fa6d9 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -9,6 +9,7 @@ namespace Magento\Catalog\Model\Product\Attribute; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; use Magento\Framework\App\Area; use Magento\Framework\DataObject; use Magento\Framework\View\Design\Theme\FlyweightFactory; @@ -124,6 +125,24 @@ function (string $handle) use ($identifier) : ?string { ); } + /** + * Extract custom layout attribute value. + * + * @param ProductInterface $product + * @return mixed + */ + private function extractAttributeValue(ProductInterface $product) + { + if ($product instanceof Product && $product->hasData('custom_layout_update_file')) { + return $product->getData('custom_layout_update_file'); + } + if ($attr = $product->getCustomAttribute('custom_layout_update_file')) { + return $attr->getValue(); + } + + return null; + } + /** * Extract selected custom layout settings. * @@ -135,11 +154,11 @@ function (string $handle) use ($identifier) : ?string { */ public function extractCustomSettings(ProductInterface $product, DataObject $intoSettings): void { - if ($product->getSku() && $attribute = $product->getCustomAttribute('custom_layout_update_file')) { + if ($product->getSku() && $value = $this->extractAttributeValue($product)) { $handles = $intoSettings->getPageLayoutHandles() ?? []; $handles = array_merge_recursive( $handles, - ['selectable' => $this->sanitizeSku($product) . '_' . $attribute->getValue()] + ['selectable' => $this->sanitizeSku($product) . '_' . $value] ); $intoSettings->setPageLayoutHandles($handles); } diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index bbef6fc059c36..39b292bf07239 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -7,6 +7,7 @@ use Magento\Cms\Model\Page\CustomLayoutManagerInterface; use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\App\Action\Action; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; @@ -90,6 +91,11 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper */ private $customLayoutRepo; + /** + * @var IdentityMap + */ + private $identityMap; + /** * Constructor * @@ -104,6 +110,7 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param CustomLayoutManagerInterface|null $customLayoutManager * @param CustomLayoutRepositoryInterface|null $customLayoutRepo + * @param IdentityMap|null $identityMap * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -117,7 +124,8 @@ public function __construct( \Magento\Framework\Escaper $escaper, \Magento\Framework\View\Result\PageFactory $resultPageFactory, ?CustomLayoutManagerInterface $customLayoutManager = null, - ?CustomLayoutRepositoryInterface $customLayoutRepo = null + ?CustomLayoutRepositoryInterface $customLayoutRepo = null, + ?IdentityMap $identityMap = null ) { $this->messageManager = $messageManager; $this->_page = $page; @@ -131,6 +139,7 @@ public function __construct( ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); $this->customLayoutRepo = $customLayoutRepo ?? ObjectManager::getInstance()->get(CustomLayoutRepositoryInterface::class); + $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); parent::__construct($context); } @@ -158,6 +167,7 @@ public function prepareResultPage(Action $action, $pageId = null) if (!$this->_page->getId()) { return false; } + $this->identityMap->add($this->_page); $inRange = $this->_localeDate->isScopeDateInInterval( null, diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php index d11d86433152d..988bd5b4ac136 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -12,6 +12,7 @@ use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; use Magento\Cms\Model\Page\CustomLayoutManagerInterface; +use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\App\Area; use Magento\Framework\View\Design\Theme\FlyweightFactory; use Magento\Framework\View\DesignInterface; @@ -49,22 +50,30 @@ class CustomLayoutManager implements CustomLayoutManagerInterface */ private $layoutProcessor; + /** + * @var IdentityMap + */ + private $identityMap; + /** * @param FlyweightFactory $themeFactory * @param DesignInterface $design * @param PageRepositoryInterface $pageRepository * @param LayoutProcessorFactory $layoutProcessorFactory + * @param IdentityMap $identityMap */ public function __construct( FlyweightFactory $themeFactory, DesignInterface $design, PageRepositoryInterface $pageRepository, - LayoutProcessorFactory $layoutProcessorFactory + LayoutProcessorFactory $layoutProcessorFactory, + IdentityMap $identityMap ) { $this->themeFactory = $themeFactory; $this->design = $design; $this->pageRepository = $pageRepository; $this->layoutProcessorFactory = $layoutProcessorFactory; + $this->identityMap = $identityMap; } /** @@ -132,7 +141,10 @@ function (string $handle) use ($identifier) : ?string { */ public function applyUpdate(PageLayout $layout, CustomLayoutSelectedInterface $layoutSelected): void { - $page = $this->pageRepository->getById($layoutSelected->getPageId()); + $page = $this->identityMap->get($layoutSelected->getPageId()); + if (!$page) { + $page = $this->pageRepository->getById($layoutSelected->getPageId()); + } $layout->addPageLayoutHandles( ['selectable' => $this->sanitizeIdentifier($page) .'_' .$layoutSelected->getLayoutFileId()] From 298e4048b512542c44d05901e25dadbd1e1afb5b Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 2 Oct 2019 10:02:27 +0300 Subject: [PATCH 0753/2437] GraphQl-903: use_for_shipping backward compatibility --- .../Model/Cart/SetBillingAddressOnCart.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 08aeb56e4cd09..0f211a63726b4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -56,9 +56,14 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; - $sameAsshipping = isset($billingAddressInput['same_as_shipping']) + $sameAsShipping = isset($billingAddressInput['same_as_shipping']) ? (bool)$billingAddressInput['same_as_shipping'] : false; + if (!isset($billingAddressInput['same_as_shipping'])) { + $sameAsShipping = isset($billingAddressInput['use_for_shipping']) + ? (bool)$billingAddressInput['use_for_shipping'] : false; + } + if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( __('The billing address must contain either "customer_address_id" or "address".') @@ -72,7 +77,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b } $addresses = $cart->getAllShippingAddresses(); - if ($sameAsshipping && count($addresses) > 1) { + if ($sameAsShipping && count($addresses) > 1) { throw new GraphQlInputException( __('Using the "same_as_shipping" option with multishipping is not possible.') ); @@ -80,7 +85,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); - $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsshipping); + $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } /** From 033efa82980fe4ece72e272e7c6266c9f54f55c4 Mon Sep 17 00:00:00 2001 From: Max Romanov <maxromanov4669@gmail.com> Date: Wed, 2 Oct 2019 11:08:08 +0300 Subject: [PATCH 0754/2437] fix https://github.com/magento/magento2/issues/24452 --- .../view/adminhtml/templates/catalog/category/tree.phtml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml index 6b63a20134df1..d6340330df8ea 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml @@ -507,8 +507,13 @@ })(jQuery); this.closeModal(); } - }] - + }], + keyEventHandlers: { + enterKey: function (event) { + this.buttons[1].click(); + event.preventDefault(); + } + } }).trigger('openModal'); } From 9c2cd28efe3dc67348ca6bedb0f592683ba93ffd Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 2 Oct 2019 11:12:44 +0300 Subject: [PATCH 0755/2437] Adding the tracking number client validation --- .../Block/Adminhtml/Order/Tracking/View.php | 2 +- .../templates/order/tracking/view.phtml | 132 ++++++++++-------- 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php index 356483c9a5dd7..9772ddcaeb9ab 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php @@ -43,7 +43,7 @@ public function __construct( */ protected function _prepareLayout() { - $onclick = "submitAndReloadArea($('shipment_tracking_info').parentNode, '" . $this->getSubmitUrl() . "')"; + $onclick = "saveTrackingInfo($('shipment_tracking_info').parentNode, '" . $this->getSubmitUrl() . "')"; $this->addChild( 'save_button', \Magento\Backend\Block\Widget\Button::class, diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml index 67587f19774c4..afeecf5cdcb8f 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml @@ -9,76 +9,85 @@ ?> <?php /** @var $block Magento\Shipping\Block\Adminhtml\Order\Tracking\View */ ?> <div class="admin__control-table-wrapper"> - <table class="data-table admin__control-table" id="shipment_tracking_info"> - <thead> - <tr class="headings"> - <th class="col-carrier"><?= $block->escapeHtml(__('Carrier')) ?></th> - <th class="col-title"><?= $block->escapeHtml(__('Title')) ?></th> - <th class="col-number"><?= $block->escapeHtml(__('Number')) ?></th> - <th class="col-delete last"><?= $block->escapeHtml(__('Action')) ?></th> - </tr> - </thead> - <tfoot> - <tr> - <td class="col-carrier"> - <select name="carrier" - class="select admin__control-select" - onchange="selectCarrier(this)"> - <?php foreach ($block->getCarriers() as $_code => $_name) : ?> - <option value="<?= $block->escapeHtmlAttr($_code) ?>"><?= $block->escapeHtml($_name) ?></option> - <?php endforeach; ?> - </select> - </td> - <td class="col-title"> - <input class="input-text admin__control-text" - type="text" - id="tracking_title" - name="title" - value="" /> - </td> - <td class="col-number"> - <input class="input-text admin__control-text" - type="text" - id="tracking_number" - name="number" - value="" /> - </td> - <td class="col-delete last"><?= $block->getSaveButtonHtml() ?></td> - </tr> - </tfoot> - <?php if ($_tracks = $block->getShipment()->getAllTracks()) : ?> - <tbody> - <?php $i = 0; foreach ($_tracks as $_track) :$i++ ?> - <tr class="<?= /* @noEscape */ ($i%2 == 0) ? 'even' : 'odd' ?>"> - <td class="col-carrier"> - <?= $block->escapeHtml($block->getCarrierTitle($_track->getCarrierCode())) ?> - </td> - <td class="col-title"><?= $block->escapeHtml($_track->getTitle()) ?></td> - <td class="col-number"> - <?php if ($_track->isCustom()) : ?> - <?= $block->escapeHtml($_track->getNumber()) ?> - <?php else : ?> - <a href="#" onclick="popWin('<?= $block->escapeJs($block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($_track))) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')"><?= $block->escapeHtml($_track->getNumber()) ?></a> - <div id="shipment_tracking_info_response_<?= (int) $_track->getId() ?>"></div> - <?php endif; ?> - </td> - <td class="col-delete last"><button class="action-delete" type="button" onclick="deleteTrackingNumber('<?= $block->escapeJs($block->escapeUrl($block->getRemoveUrl($_track))) ?>'); return false;"><span><?= $block->escapeHtml(__('Delete')) ?></span></button></td> - </tr> - <?php endforeach; ?> - </tbody> - <?php endif; ?> - </table> + <form id="tracking-shipping-form" data-mage-init='{"validation": {}}'> + <table class="data-table admin__control-table" id="shipment_tracking_info"> + <thead> + <tr class="headings"> + <th class="col-carrier"><?= $block->escapeHtml(__('Carrier')) ?></th> + <th class="col-title"><?= $block->escapeHtml(__('Title')) ?></th> + <th class="col-number"><?= $block->escapeHtml(__('Number')) ?></th> + <th class="col-delete last"><?= $block->escapeHtml(__('Action')) ?></th> + </tr> + </thead> + <tfoot> + <tr> + <td class="col-carrier"> + <select name="carrier" + class="select admin__control-select" + onchange="selectCarrier(this)"> + <?php foreach ($block->getCarriers() as $_code => $_name) : ?> + <option value="<?= $block->escapeHtmlAttr($_code) ?>"><?= $block->escapeHtml($_name) ?></option> + <?php endforeach; ?> + </select> + </td> + <td class="col-title"> + <input class="input-text admin__control-text" + type="text" + id="tracking_title" + name="title" + value="" /> + </td> + <td class="col-number"> + <input class="input-text admin__control-text required-entry" + type="text" + id="tracking_number" + name="number" + value="" /> + </td> + <td class="col-delete last"><?= $block->getSaveButtonHtml() ?></td> + </tr> + </tfoot> + <?php if ($_tracks = $block->getShipment()->getAllTracks()) : ?> + <tbody> + <?php $i = 0; foreach ($_tracks as $_track) :$i++ ?> + <tr class="<?= /* @noEscape */ ($i%2 == 0) ? 'even' : 'odd' ?>"> + <td class="col-carrier"> + <?= $block->escapeHtml($block->getCarrierTitle($_track->getCarrierCode())) ?> + </td> + <td class="col-title"><?= $block->escapeHtml($_track->getTitle()) ?></td> + <td class="col-number"> + <?php if ($_track->isCustom()) : ?> + <?= $block->escapeHtml($_track->getNumber()) ?> + <?php else : ?> + <a href="#" onclick="popWin('<?= $block->escapeJs($block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($_track))) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')"><?= $block->escapeHtml($_track->getNumber()) ?></a> + <div id="shipment_tracking_info_response_<?= (int) $_track->getId() ?>"></div> + <?php endif; ?> + </td> + <td class="col-delete last"><button class="action-delete" type="button" onclick="deleteTrackingNumber('<?= $block->escapeJs($block->escapeUrl($block->getRemoveUrl($_track))) ?>'); return false;"><span><?= $block->escapeHtml(__('Delete')) ?></span></button></td> + </tr> + <?php endforeach; ?> + </tbody> + <?php endif; ?> + </table> + </form> </div> <script> -require(['prototype'], function(){ - +require(['prototype', 'jquery'], function(prototype, $j) { //<![CDATA[ function selectCarrier(elem) { var option = elem.options[elem.selectedIndex]; $('tracking_title').value = option.value && option.value != 'custom' ? option.text : ''; } +function saveTrackingInfo(node, url) { + var form = $j('#tracking-shipping-form'); + + if (form.validation() && form.validation('isValid')) { + submitAndReloadArea(node, url); + } +} + function deleteTrackingNumber(url) { if (confirm('<?= $block->escapeJs($block->escapeHtml(__('Are you sure?'))) ?>')) { submitAndReloadArea($('shipment_tracking_info').parentNode, url) @@ -87,6 +96,7 @@ function deleteTrackingNumber(url) { window.selectCarrier = selectCarrier; window.deleteTrackingNumber = deleteTrackingNumber; +window.saveTrackingInfo = saveTrackingInfo; //]]> }); From 35f77dbf8452da84312d174397c0e3fa80ceca24 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 2 Oct 2019 12:22:53 +0400 Subject: [PATCH 0756/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- ...efrontElasticsearch6SearchInvalidValueTest.xml} | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) rename app/code/Magento/Elasticsearch6/Test/Mftf/Test/{StorefrontElasticsearchSearchInvalidValueTest.xml => StorefrontElasticsearch6SearchInvalidValueTest.xml} (87%) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml similarity index 87% rename from app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml rename to app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 932bef3a1452b..5f6949dcafef5 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearchSearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontElasticsearchSearchInvalidValueTest"> + <test name="StorefrontElasticsearch6SearchInvalidValueTest"> <annotations> <features value="Search"/> <stories value="Search Product on Storefront"/> @@ -96,11 +96,11 @@ </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbols"> - <argument name="phrase" value="?SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^"/> + <argument name="phrase" value="?{{ProductWithSpecialSymbols.name}};"/> </actionGroup> <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbols"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbolsSecondTime"> - <argument name="phrase" value="? SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^ ;"/> + <argument name="phrase" value="? {{ProductWithSpecialSymbols.name}} ;"/> </actionGroup> <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbolsSecondTime"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForThirdSearchTerm"> @@ -111,13 +111,5 @@ <argument name="phrase" value="? anything at all ;"/> </actionGroup> <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForForthSearchTerm"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSearchableString"> - <argument name="phrase" value="?searchable string;"/> - </actionGroup> - <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSearchableString"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSearchableStringSecondTime"> - <argument name="phrase" value="? searchable string ;"/> - </actionGroup> - <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSearchableStringSecondTime"/> </test> </tests> From f7cc155c64c4416bf0b59410415e5bc297879dc1 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 2 Oct 2019 12:00:09 +0300 Subject: [PATCH 0757/2437] MC-20624: Automate MC-11459 --- .../Model/Export/CustomerTest.php | 123 ++++++++++++++++-- 1 file changed, 113 insertions(+), 10 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index 7cde07675ae27..bf1888d6b420c 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Registry; use Magento\Customer\Model\Attribute; use Magento\ImportExport\Model\Export; +use Magento\ImportExport\Model\Import; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\ObjectManagerInterface; use Magento\Store\Model\StoreManagerInterface; @@ -20,6 +21,8 @@ /** * Tests for customer export model. + * + * @magentoAppArea adminhtml */ class CustomerTest extends \PHPUnit\Framework\TestCase { @@ -33,6 +36,16 @@ class CustomerTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @var array + */ + private $attributeValues; + + /** + * @var array + */ + private $attributeTypes; + /** * @inheritdoc */ @@ -49,10 +62,13 @@ protected function setUp() */ public function testExport() { - $expectedAttributes = []; - /** @var $collection Collection */ + /** @var Collection $collection */ $collection = $this->objectManager->create(Collection::class); - /** @var $attribute Attribute */ + $this->initAttributeValues($collection); + $this->initAttributeTypes($collection); + + $expectedAttributes = []; + /** @var Attribute $attribute */ foreach ($collection as $attribute) { $expectedAttributes[] = $attribute->getAttributeCode(); } @@ -72,10 +88,10 @@ public function testExport() $this->assertNotEmpty($lines['data'], 'No data was exported.'); - /** @var $customers CustomerModel[] */ + /** @var CustomerModel[] $customers */ $customers = $this->objectManager->create(CustomerCollection::class)->getItems(); foreach ($customers as $customer) { - $data = $customer->getData(); + $data = $this->processCustomerData($customer, $expectedAttributes); $exportData = $lines['data'][$data['email']]; $exportData = $this->unsetDuplicateData($exportData); array_walk( @@ -91,6 +107,96 @@ function (&$value) { } } + /** + * Initialize attribute option values. + * + * @param Collection $attributeCollection + * @return $this + */ + private function initAttributeValues(Collection $attributeCollection): CustomerTest + { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $this->attributeValues[$attribute->getAttributeCode()] = $this->_model->getAttributeOptions($attribute); + } + + return $this; + } + + /** + * Initialize attribute types. + * + * @param \Magento\Customer\Model\ResourceModel\Attribute\Collection $attributeCollection + * @return $this + */ + private function initAttributeTypes(Collection $attributeCollection): CustomerTest + { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $this->attributeTypes[$attribute->getAttributeCode()] = $attribute->getFrontendInput(); + } + + return $this; + } + + /** + * Format Customer data as same as export data. + * + * @param CustomerModel $item + * @param array $expectedAttributes + * @return array + */ + private function processCustomerData(CustomerModel $item, array $expectedAttributes): array + { + $data = []; + foreach ($expectedAttributes as $attributeCode) { + $attributeValue = $item->getData($attributeCode); + + if ($this->isMultiselect($attributeCode)) { + $values = []; + $attributeValue = explode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $attributeValue); + foreach ($attributeValue as $value) { + $values[] = $this->getAttributeValueById($attributeCode, $value); + } + $data[$attributeCode] = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $values); + } else { + $data[$attributeCode] = $this->getAttributeValueById($attributeCode, $attributeValue); + } + } + + return $data; + } + + /** + * Check that attribute is multiselect type by attribute code. + * + * @param string $attributeCode + * @return bool + */ + private function isMultiselect(string $attributeCode): bool + { + return isset($this->attributeTypes[$attributeCode]) + && $this->attributeTypes[$attributeCode] === 'multiselect'; + } + + /** + * Return attribute value by id. + * + * @param string $attributeCode + * @param int|string $valueId + * @return mixed + */ + private function getAttributeValueById(string $attributeCode, $valueId) + { + if (isset($this->attributeValues[$attributeCode]) + && isset($this->attributeValues[$attributeCode][$valueId]) + ) { + return $this->attributeValues[$attributeCode][$valueId]; + } + + return $valueId; + } + /** * Unset non-useful or duplicate data from exported file data. * @@ -172,14 +278,10 @@ public function testFilterAttributeCollection() public function testFilterEntityCollection() { $createdAtDate = '2038-01-01'; - - /** @var $objectManager ObjectManagerInterface */ - $objectManager = $this->objectManager; - /** * Change created_at date of first customer for future filter test. */ - $customers = $objectManager->get(Registry::class) + $customers = $this->objectManager->get(Registry::class) ->registry('_fixture/Magento_ImportExport_Customer_Collection'); $customers[0]->setCreatedAt($createdAtDate); $customers[0]->save(); @@ -239,6 +341,7 @@ protected function _csvToArray($content, $entityId = null) } } } + return $data; } } From 9d96481c5d690fc4fd399b5a63007e5d91d440d6 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 2 Oct 2019 12:04:18 +0300 Subject: [PATCH 0758/2437] GraphQl-903: fixed static issue --- .../QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 0f211a63726b4..b90752ae7358b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -56,13 +56,10 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; + $useForShipping = isset($billingAddressInput['use_for_shipping']) + ? (bool)$billingAddressInput['use_for_shipping'] : false; $sameAsShipping = isset($billingAddressInput['same_as_shipping']) - ? (bool)$billingAddressInput['same_as_shipping'] : false; - - if (!isset($billingAddressInput['same_as_shipping'])) { - $sameAsShipping = isset($billingAddressInput['use_for_shipping']) - ? (bool)$billingAddressInput['use_for_shipping'] : false; - } + ? (bool)$billingAddressInput['same_as_shipping'] : $useForShipping; if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( From a6984616d135603f6f6c8cb52f6082630450f115 Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 2 Oct 2019 13:33:49 +0400 Subject: [PATCH 0759/2437] MAGETWO-67450: The rate in order is duplicated - Updated automated test script --- .../Test/Mftf/Data/CurrencyRatesConfigData.xml | 12 ++++++------ ...teDisplayWhenChooseThreeAllowedCurrenciesTest.xml | 2 +- .../Test/AdminOrderRateDisplayedInOneLineTest.xml | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml index b7b6bc8690bd0..6194287dd058b 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Data/CurrencyRatesConfigData.xml @@ -20,21 +20,21 @@ <data key="scope">websites</data> <data key="scope_code">base</data> </entity> - <entity name="SetAllowedCurrenciesConfigForThree"> + <entity name="SetAllowedCurrenciesConfigForUSD"> <data key="path">currency/options/allow</data> - <data key="value">EUR,USD,RUB</data> + <data key="value">USD</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> - <entity name="SetAllowedCurrenciesConfigForTwo"> + <entity name="SetAllowedCurrenciesConfigForEUR"> <data key="path">currency/options/allow</data> - <data key="value">EUR,USD</data> + <data key="value">EUR</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> - <entity name="SetAllowedCurrenciesConfigForOne"> + <entity name="SetAllowedCurrenciesConfigForRUB"> <data key="path">currency/options/allow</data> - <data key="value">USD</data> + <data key="value">RUB</data> <data key="scope">websites</data> <data key="scope_code">base</data> </entity> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index fb71777b086dc..26fbfd394be68 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -25,7 +25,7 @@ <createData entity="SimpleProduct2" stepKey="createNewProduct"/> <!--Set Currency options for Website--> <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSDWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForThree.scope}} --scope-code={{SetAllowedCurrenciesConfigForThree.scope_code}} {{SetAllowedCurrenciesConfigForThree.path}} {{SetAllowedCurrenciesConfigForThree.value}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}},{{SetAllowedCurrenciesConfigForRUB.value}}" stepKey="setAllowedCurrencyWebsitesEURandRUBandUSD"/> <magentoCLI command="config:set --scope={{SetDefaultCurrencyEURConfig.scope}} --scope-code={{SetDefaultCurrencyEURConfig.scope_code}} {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index 514d4c5d54f89..dc6bdf3db542e 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -27,11 +27,11 @@ <magentoCLI command="config:set {{CatalogPriceScopeWebsiteConfigData.path}} {{CatalogPriceScopeWebsiteConfigData.value}}" stepKey="setCatalogPriceScopeWebsite"/> <!--Set Currency options for Default Config--> <magentoCLI command="config:set {{SetCurrencyEURBaseConfig.path}} {{SetCurrencyEURBaseConfig.value}}" stepKey="setCurrencyBaseEUR"/> - <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForTwo.path}} {{SetAllowedCurrenciesConfigForTwo.value}}" stepKey="setAllowedCurrencyEUR"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}}" stepKey="setAllowedCurrencyEURandUSD"/> <magentoCLI command="config:set {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEUR"/> <!--Set Currency options for Website--> <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseEURWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForTwo.scope}} --scope-code={{SetAllowedCurrenciesConfigForTwo.scope_code}} {{SetAllowedCurrenciesConfigForTwo.path}} {{SetAllowedCurrenciesConfigForTwo.value}}" stepKey="setAllowedCurrencyWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}},{{SetAllowedCurrenciesConfigForEUR.value}}" stepKey="setAllowedCurrencyWebsitesForEURandUSD"/> <magentoCLI command="config:set --scope={{SetDefaultCurrencyEURConfig.scope}} --scope-code={{SetDefaultCurrencyEURConfig.scope_code}} {{SetDefaultCurrencyEURConfig.path}} {{SetDefaultCurrencyEURConfig.value}}" stepKey="setCurrencyDefaultEURWebsites"/> </before> <after> @@ -41,11 +41,11 @@ <magentoCLI command="config:set {{CatalogPriceScopeGlobalConfigData.path}} {{CatalogPriceScopeGlobalConfigData.value}}" stepKey="setCatalogPriceScopeGlobal"/> <magentoCLI command="config:set {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSD"/> <magentoCLI command="config:set {{SetDefaultCurrencyUSDConfig.path}} {{SetDefaultCurrencyUSDConfig.value}}" stepKey="setCurrencyDefaultUSD"/> - <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForOne.path}} {{SetAllowedCurrenciesConfigForOne.value}}" stepKey="setAllowedCurrencyUSD"/> + <magentoCLI command="config:set {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}}" stepKey="setAllowedCurrencyUSD"/> <!--Set Currency options for Website--> <magentoCLI command="config:set --scope={{SetCurrencyUSDBaseConfig.scope}} --scope-code={{SetCurrencyUSDBaseConfig.scope_code}} {{SetCurrencyUSDBaseConfig.path}} {{SetCurrencyUSDBaseConfig.value}}" stepKey="setCurrencyBaseUSDWebsites"/> <magentoCLI command="config:set --scope={{SetDefaultCurrencyUSDConfig.scope}} --scope-code={{SetDefaultCurrencyUSDConfig.scope_code}} {{SetDefaultCurrencyUSDConfig.path}} {{SetDefaultCurrencyUSDConfig.value}}" stepKey="setCurrencyDefaultUSDWebsites"/> - <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForOne.scope}} --scope-code={{SetAllowedCurrenciesConfigForOne.scope_code}} {{SetAllowedCurrenciesConfigForOne.path}} {{SetAllowedCurrenciesConfigForOne.value}}" stepKey="setAllowedCurrencyUSDWebsites"/> + <magentoCLI command="config:set --scope={{SetAllowedCurrenciesConfigForUSD.scope}} --scope-code={{SetAllowedCurrenciesConfigForUSD.scope_code}} {{SetAllowedCurrenciesConfigForUSD.path}} {{SetAllowedCurrenciesConfigForUSD.value}}" stepKey="setAllowedCurrencyUSDWebsites"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Open created product on Storefront and place for order--> From adcbfdd9b8c88e27465046e5111115e74c54e766 Mon Sep 17 00:00:00 2001 From: Mahesh Singh <mahesh721@webkul.com> Date: Wed, 2 Oct 2019 16:15:03 +0530 Subject: [PATCH 0760/2437] Fixed issue with full tax summary --- app/code/Magento/Tax/Block/Sales/Order/Tax.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php index 0adaec9311ee6..c05bbb0244c1b 100644 --- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php @@ -107,7 +107,7 @@ protected function _addTax($after = 'discount') $taxTotal = new \Magento\Framework\DataObject(['code' => 'tax', 'block_name' => $this->getNameInLayout()]); $totals = $this->getParentBlock()->getTotals(); if ($totals['grand_total']) { - $this->getParentBlock()->addTotalBefore($taxTotal, 'grand_total'); + $this->getParentBlock()->addTotal($taxTotal, 'grand_total'); } $this->getParentBlock()->addTotal($taxTotal, $after); return $this; @@ -320,7 +320,7 @@ protected function _initGrandTotal() ] ); $parent->addTotal($totalExcl, 'grand_total'); - $parent->addTotal($totalIncl, 'tax'); + $parent->addTotal($totalIncl, 'grand_total'); $this->_addTax('grand_total'); } return $this; From cb53e902a28e00f1f78fb443ac5cd4b220699878 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 2 Oct 2019 14:40:37 +0300 Subject: [PATCH 0761/2437] Fixing static tests --- .../Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php index 9772ddcaeb9ab..55eecfa00d6da 100644 --- a/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php +++ b/app/code/Magento/Shipping/Block/Adminhtml/Order/Tracking/View.php @@ -86,7 +86,10 @@ public function getRemoveUrl($track) } /** + * Get carrier title + * * @param string $code + * * @return \Magento\Framework\Phrase|string|bool */ public function getCarrierTitle($code) From f3bba70b003decd6e9ec5d635e47d81554c5c627 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Wed, 2 Oct 2019 16:13:50 +0300 Subject: [PATCH 0762/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Fix annotation for unit test that was created for the ticket MC-6387. --- .../Authorizenet/Test/Unit/Model/DirectpostTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php index 50cf7e86e8fce..a1547a0563461 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Model/DirectpostTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Authorizenet\Test\Unit\Model; @@ -306,7 +307,9 @@ public function dataProviderCaptureWithInvalidAmount() } /** - * @@expectedException \Magento\Framework\Exception\LocalizedException + * Test capture has parent transaction id. + * + * @expectedException \Magento\Framework\Exception\LocalizedException */ public function testCaptureHasParentTransactionId() { @@ -550,13 +553,10 @@ public function checkResponseCodeSuccessDataProvider() /** * Checks response failures behaviour. * - * @param int $responseCode - * @param int $failuresHandlerCalls * @return void - * * @expectedException \Magento\Framework\Exception\LocalizedException */ - public function testCheckResponseCodeFailureDefault(): void + public function testCheckResponseCodeFailureDefault() { $responseCode = 999999; $this->responseMock->expects($this->once())->method('getXResponseCode')->willReturn($responseCode); From d3be7818f589193d5350d4c9d4e03453ffbdf4ab Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Wed, 2 Oct 2019 17:35:06 +0400 Subject: [PATCH 0763/2437] MC-18215: Error message while creating shipping label - Updated automated test script --- .../Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml | 0 .../Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml | 2 +- .../Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/{Backend => Customer}/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml (100%) diff --git a/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml similarity index 100% rename from app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml rename to app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index e50d484df27c4..91a76383babd4 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -114,7 +114,7 @@ <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipmentIntoOrder"/> <checkOption selector="{{AdminShipmentTotalSection.createShippingLabel}}" stepKey="checkCreateShippingLabel"/> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> - <actionGroup ref="AdminShipmentCreatePackageActionGroup" stepKey="createPackage"> + <actionGroup ref="AdminShipmentCreateShippingLabelActionGroup" stepKey="createPackage"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <actionGroup ref="AdminGoToShipmentTabActionGroup" stepKey="goToShipmentTab"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml index d160f95e2a9cc..631db885ab3d9 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminShipmentActionGroup.xml @@ -62,7 +62,7 @@ <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPageShipping"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The shipment has been created." stepKey="seeShipmentCreateSuccess"/> </actionGroup> - <actionGroup name="AdminShipmentCreatePackageActionGroup"> + <actionGroup name="AdminShipmentCreateShippingLabelActionGroup"> <arguments> <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> </arguments> From 0c0dc6df18ed32673a359421091765f9c61e7cc4 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Wed, 2 Oct 2019 17:02:59 +0300 Subject: [PATCH 0764/2437] MC-20624: Automate MC-11459 --- .../import_export/customers_rollback.php | 1 + .../Model/Export/CustomerTest.php | 72 +++++++++++++++---- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php index 4630236c17b06..f8eeb8edd15da 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php @@ -34,3 +34,4 @@ } $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); +$registry->unregister('_fixture/Magento_ImportExport_Customer_Collection'); diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index bf1888d6b420c..74a21af111fbe 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -46,6 +46,11 @@ class CustomerTest extends \PHPUnit\Framework\TestCase */ private $attributeTypes; + /** + * @var Collection + */ + private $attributeCollection; + /** * @inheritdoc */ @@ -53,33 +58,64 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->_model = $this->objectManager->create(Customer::class); + $this->attributeCollection = $this->objectManager->create(Collection::class); } /** * Export "Customer Main File". * * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @return void */ public function testExport() { - /** @var Collection $collection */ - $collection = $this->objectManager->create(Collection::class); - $this->initAttributeValues($collection); - $this->initAttributeTypes($collection); + $this->processCustomerAttribute(); + $expectedAttributes = $this->getExpectedAttributes(); + $lines = $this->export($expectedAttributes); + $this->checkExportData($lines, $expectedAttributes); + } + /** + * Return attributes which should be exported. + * + * @return array + */ + private function getExpectedAttributes(): array + { $expectedAttributes = []; /** @var Attribute $attribute */ - foreach ($collection as $attribute) { + foreach ($this->attributeCollection as $attribute) { $expectedAttributes[] = $attribute->getAttributeCode(); } - $expectedAttributes = array_diff($expectedAttributes, $this->_model->getDisabledAttributes()); - $this->_model->setWriter($this->objectManager->get(Csv::class)); + return array_diff($expectedAttributes, $this->_model->getDisabledAttributes()); + } + + /** + * Prepare Customer attribute. + * + * @return void + */ + private function processCustomerAttribute(): void + { + $this->initAttributeValues($this->attributeCollection); + $this->initAttributeTypes($this->attributeCollection); + } + + /** + * Export customer. + * + * @param array $expectedAttributes + * @return array + */ + private function export(array $expectedAttributes): array + { + $this->_model->setWriter($this->objectManager->create(Csv::class)); $data = $this->_model->export(); + $this->assertNotEmpty($data); $lines = $this->_csvToArray($data, 'email'); - $this->assertEquals( count($expectedAttributes), count(array_intersect($expectedAttributes, $lines['header'])), @@ -88,8 +124,20 @@ public function testExport() $this->assertNotEmpty($lines['data'], 'No data was exported.'); + return $lines; + } + + /** + * Check that exported data is correct. + * + * @param array $lines + * @param array $expectedAttributes + * @return void + */ + private function checkExportData(array $lines, array $expectedAttributes): void + { /** @var CustomerModel[] $customers */ - $customers = $this->objectManager->create(CustomerCollection::class)->getItems(); + $customers = $this->objectManager->create(CustomerCollection::class); foreach ($customers as $customer) { $data = $this->processCustomerData($customer, $expectedAttributes); $exportData = $lines['data'][$data['email']]; @@ -111,7 +159,7 @@ function (&$value) { * Initialize attribute option values. * * @param Collection $attributeCollection - * @return $this + * @return CustomerTest */ private function initAttributeValues(Collection $attributeCollection): CustomerTest { @@ -127,7 +175,7 @@ private function initAttributeValues(Collection $attributeCollection): CustomerT * Initialize attribute types. * * @param \Magento\Customer\Model\ResourceModel\Attribute\Collection $attributeCollection - * @return $this + * @return CustomerTest */ private function initAttributeTypes(Collection $attributeCollection): CustomerTest { @@ -184,7 +232,7 @@ private function isMultiselect(string $attributeCode): bool * * @param string $attributeCode * @param int|string $valueId - * @return mixed + * @return int|string|array */ private function getAttributeValueById(string $attributeCode, $valueId) { From a7f87cedecb81ea4901266aadd669fd8f3bea347 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 2 Oct 2019 17:11:19 +0300 Subject: [PATCH 0765/2437] MC-18457: Free Shipping Minimum Order Amount Excluding/Including Tax options --- .../Model/Carrier/Freeshipping.php | 23 +- ...eeShippingDisplayWithInclTaxOptionTest.xml | 68 ++++++ .../Unit/Model/Carrier/FreeshippingTest.php | 200 ++++++++++++++++++ .../OfflineShipping/etc/adminhtml/system.xml | 4 + .../Magento/Quote/Model/Quote/Address.php | 21 +- .../Test/Mftf/Data/FreeShippingMethodData.xml | 15 ++ .../Mftf/Metadata/shipping_methods-meta.xml | 3 + .../Sales/Total/Quote/CommonTaxCollector.php | 4 + 8 files changed, 324 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml create mode 100644 app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php index 674e6b8089787..a1fca2b155f11 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php @@ -63,6 +63,24 @@ public function __construct( parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } + /** + * Check subtotal for allowed free shipping + * + * @param RateRequest $request + * + * @return bool + */ + private function isFreeShippingRequired(RateRequest $request): bool + { + $minSubtotal = $request->getPackageValueWithDiscount(); + if ($request->getBaseSubtotalWithDiscountInclTax() + && $this->getConfigFlag('tax_including')) { + $minSubtotal = $request->getBaseSubtotalWithDiscountInclTax(); + } + + return $minSubtotal >= $this->getConfigData('free_shipping_subtotal'); + } + /** * FreeShipping Rates Collector * @@ -80,10 +98,7 @@ public function collectRates(RateRequest $request) $this->_updateFreeMethodQuote($request); - if ($request->getFreeShipping() || $request->getPackageValueWithDiscount() >= $this->getConfigData( - 'free_shipping_subtotal' - ) - ) { + if ($request->getFreeShipping() || $this->isFreeShippingRequired($request)) { /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); diff --git a/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml new file mode 100644 index 0000000000000..db44a06b54a88 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontFreeShippingDisplayWithInclTaxOptionTest"> + <annotations> + <features value="Shipping"/> + <stories value="Offline Shipping Methods"/> + <title value="Free Shipping Minimum Order Amount Excluding/Including Tax options"/> + <description value="Free Shipping Minimum Order Amount Excluding/Including Tax options"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20613"/> + <useCaseId value="MC-18457"/> + <group value="shipping"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + <field key="price">100.00</field> + </createData> + <!-- Enable free shipping method --> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> + <createData entity="SetTaxIncluding" stepKey="setTaxIncluding"/> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="configureTaxForCA"/> + <createData entity="defaultTaxRule" stepKey="createTaxRule"/> + </before> + <after> + <!-- Disable free shipping method --> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> + <createData entity="SetTaxIncludingToDefault" stepKey="setTaxIncludingToDefault"/> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <createData entity="DefaultTaxConfig" stepKey="resetTaxConfiguration"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + </after> + <!-- Add simple product to cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.tax}}" stepKey="waitForOverviewVisible"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.estimateShippingAndTaxForm}}" stepKey="waitForEstimateShippingAndTaxForm"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.shippingMethodForm}}" stepKey="waitForShippingMethodForm"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <waitForPageLoad stepKey="waitForSelectCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <waitForPageLoad stepKey="waitForSelectRegion"/> + <see selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25" stepKey="seeTaxForCA"/> + <!-- See available Free Shipping option --> + <actionGroup ref="StorefrontAssertShippingMethodPresentInCartActionGroup" stepKey="assertShippingMethodLabel"> + <argument name="shippingMethod" value="{{freeTitleDefault.value}}"/> + </actionGroup> + <!-- Change State to New York --> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_NY.state}}" stepKey="selectAnotherState"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + <dontSee selector="{{CheckoutCartSummarySection.shippingMethodLabel}}" userInput="{{freeTitleDefault.value}}" stepKey="assertShippingMethodIsNotPresentInCart"/> + </test> +</tests> diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php new file mode 100644 index 0000000000000..7f8959e610d42 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php @@ -0,0 +1,200 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\OfflineShipping\Test\Unit\Model\Carrier; + +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\OfflineShipping\Model\Carrier\Freeshipping; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory; +use Magento\Shipping\Model\Rate\ResultFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; + +/** + * Class for test free shipping + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FreeshippingTest extends TestCase +{ + /** + * @var Freeshipping + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var ResultFactory|MockObject + */ + private $resultFactoryMock; + + /** + * @var MethodFactory|MockObject + */ + private $methodFactoryMock; + + /** + * @var ObjectManager + */ + private $helper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->methodFactoryMock = $this + ->getMockBuilder(MethodFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->helper = new ObjectManager($this); + $this->model = $this->helper->getObject( + Freeshipping::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + '_rateResultFactory' => $this->resultFactoryMock, + '_rateMethodFactory' => $this->methodFactoryMock, + ] + ); + } + + /** + * Test for collect rate free shipping with tax options + * + * @param int $subtotalInclTax + * @param int $minOrderAmount + * @param int $packageValueWithDiscount + * @param int $baseSubtotalWithDiscountInclTax + * @param InvokedCount $expectedCallAppend + * + * @return void + * @dataProvider freeShippingWithSubtotalTaxDataProvider + */ + public function testCollectRatesFreeShippingWithTaxOptions( + int $subtotalInclTax, + int $minOrderAmount, + int $packageValueWithDiscount, + int $baseSubtotalWithDiscountInclTax, + InvokedCount $expectedCallAppend + ): void { + /** @var RateRequest|MockObject $request */ + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getAllItems', + 'getPackageQty', + 'getFreeShipping', + 'getBaseSubtotalWithDiscountInclTax', + 'getPackageValueWithDiscount', + ] + ) + ->getMock(); + $item = $this->getMockBuilder(QuoteItem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock->expects($this->at(0)) + ->method('isSetFlag') + ->willReturn(true); + $this->scopeConfigMock->expects($this->at(1)) + ->method('isSetFlag') + ->with( + 'carriers/freeshipping/tax_including', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($subtotalInclTax); + $this->scopeConfigMock->expects($this->at(2)) + ->method('getValue') + ->with( + 'carriers/freeshipping/free_shipping_subtotal', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($minOrderAmount); + $method = $this->getMockBuilder(Method::class) + ->disableOriginalConstructor() + ->setMethods(['setCarrier', 'setCarrierTitle', 'setMethod', 'setMethodTitle', 'setPrice', 'setCost']) + ->getMock(); + $resultModel = $this->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->setMethods(['append']) + ->getMock(); + $this->resultFactoryMock->method('create') + ->willReturn($resultModel); + $request->method('getPackageValueWithDiscount') + ->willReturn($packageValueWithDiscount); + $request->method('getAllItems') + ->willReturn([$item]); + $request->method('getFreeShipping') + ->willReturn(false); + $request->method('getBaseSubtotalWithDiscountInclTax') + ->willReturn($baseSubtotalWithDiscountInclTax); + $this->methodFactoryMock->method('create')->willReturn($method); + + $resultModel->expects($expectedCallAppend) + ->method('append') + ->with($method); + + $this->model->collectRates($request); + } + + /** + * @return array + */ + public function freeShippingWithSubtotalTaxDataProvider(): array + { + return [ + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->once(), + + ], + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 20, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + [ + 'subtotalInclTax' => 0, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + ]; + } +} diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index 2b29d2211b9d1..cb75bddf4d7bd 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -127,6 +127,10 @@ <label>Minimum Order Amount</label> <validate>validate-number validate-zero-or-greater</validate> </field> + <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <label>Include Tax to Amount</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 3ecbc69b80785..ecc51a00e8fb5 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -471,7 +471,7 @@ protected function _isDefaultShippingNullOrSameAsBillingAddress() /** * Declare address quote model object * - * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote $quote * @return $this */ public function setQuote(\Magento\Quote\Model\Quote $quote) @@ -691,7 +691,7 @@ public function getItemQty($itemId = 0) */ public function hasItems() { - return sizeof($this->getAllItems()) > 0; + return count($this->getAllItems()) > 0; } /** @@ -1020,6 +1020,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $request->setLimitCarrier($this->getLimitCarrier()); $baseSubtotalInclTax = $this->getBaseSubtotalTotalInclTax(); $request->setBaseSubtotalInclTax($baseSubtotalInclTax); + $request->setBaseSubtotalWithDiscountInclTax($this->getBaseSubtotalWithDiscount() + $this->getBaseTaxAmount()); $result = $this->_rateCollector->create()->collectRates($request)->getResult(); @@ -1225,8 +1226,8 @@ public function setBaseShippingAmount($value, $alreadyExclTax = false) /** * Set total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setTotalAmount($code, $amount) @@ -1243,8 +1244,8 @@ public function setTotalAmount($code, $amount) /** * Set total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setBaseTotalAmount($code, $amount) @@ -1261,8 +1262,8 @@ public function setBaseTotalAmount($code, $amount) /** * Add amount total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addTotalAmount($code, $amount) @@ -1276,8 +1277,8 @@ public function addTotalAmount($code, $amount) /** * Add amount total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addBaseTotalAmount($code, $amount) diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml index d700aa622c177..3d3667e59903f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml @@ -26,6 +26,7 @@ <requiredEntity type="title">freeTitleDefault</requiredEntity> <requiredEntity type="name">freeNameDefault</requiredEntity> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> <requiredEntity type="specificerrmsg">freeSpecificerrmsgDefault</requiredEntity> <requiredEntity type="sallowspecific">freeSallowspecificDefault</requiredEntity> <requiredEntity type="specificcountry">freeSpecificcountryDefault</requiredEntity> @@ -44,6 +45,9 @@ <entity name="freeShippingSubtotalDefault" type="free_shipping_subtotal"> <data key="value" /> </entity> + <entity name="TaxIncludingDefault" type="tax_including"> + <data key="value">0</data> + </entity> <entity name="freeSpecificerrmsgDefault" type="specificerrmsg"> <data key="value">This shipping method is not available. To use this shipping method, please contact us.</data> </entity> @@ -66,6 +70,17 @@ <entity name="freeShippingSubtotal" type="free_shipping_subtotal"> <data key="value">101</data> </entity> + <!--Set Free Shipping "Include Tax to Amount" to "Yes"--> + <entity name="SetTaxIncluding" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncluding</requiredEntity> + </entity> + <entity name="TaxIncluding" type="tax_including"> + <data key="value">1</data> + </entity> + <!--Set to default Free Shipping "Include Tax to Amount"--> + <entity name="SetTaxIncludingToDefault" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> + </entity> <!--Set to default Free Shipping Subtotal--> <entity name="setFreeShippingSubtotalToDefault" type="free_shipping_method"> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml index 5781b886386f6..14c0d6d5af725 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml @@ -66,6 +66,9 @@ <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> <field key="value">string</field> </object> + <object key="tax_including" dataType="tax_including"> + <field key="value">boolean</field> + </object> <object key="specificerrmsg" dataType="specificerrmsg"> <field key="value">string</field> </object> diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 77b3cfa3a08bb..c70c715d32c1b 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -383,6 +383,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } else { @@ -394,6 +395,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } @@ -592,6 +594,7 @@ protected function processProductItems( $total->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); $total->setBaseSubtotalInclTax($baseSubtotalInclTax); $shippingAssignment->getShipping()->getAddress()->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); + $shippingAssignment->getShipping()->getAddress()->setBaseTaxAmount($baseTax); return $this; } @@ -799,6 +802,7 @@ public function convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo 'rates' => $rates, ]; if (!empty($extraInfo)) { + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $appliedTaxArray = array_merge($appliedTaxArray, $extraInfo); } From 56adedddf6fa8680c298d3967f67171783582725 Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Wed, 2 Oct 2019 11:53:26 -0300 Subject: [PATCH 0766/2437] Unit tests --- .../Address/CollectTotalsObserverTest.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index 4ea067c9be8f6..a590c8aa891a5 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -314,6 +314,43 @@ public function testDispatchWithCustomerCountryInEU() $this->model->execute($this->observerMock); } + public function testDispatchWithAddressCustomerVatIdAndCountryId() + { + $customerCountryCode = "BE"; + $customerVat = "123123123"; + $defaultShipping = 1; + + $customerAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $customerAddress->expects($this->any()) + ->method("getVatId") + ->willReturn($customerVat); + + $customerAddress->expects($this->any()) + ->method("getCountryId") + ->willReturn($customerCountryCode); + + $this->addressRepository->expects($this->once()) + ->method("getById") + ->with($defaultShipping) + ->willReturn($customerAddress); + + $this->customerMock->expects($this->atLeastOnce()) + ->method("getDefaultShipping") + ->willReturn($defaultShipping); + + $this->vatValidatorMock->expects($this->once()) + ->method('isEnabled') + ->with($this->quoteAddressMock, $this->storeId) + ->will($this->returnValue(true)); + + $this->customerVatMock->expects($this->once()) + ->method('isCountryInEU') + ->with($customerCountryCode) + ->willReturn(true); + + $this->model->execute($this->observerMock); + } + public function testDispatchWithEmptyShippingAddress() { $customerCountryCode = "DE"; From 8d0b4c436e654a1f24a91640e6827fa882441619 Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 2 Oct 2019 10:18:38 -0500 Subject: [PATCH 0767/2437] MC-20358: Create New MFTF Suites for MySQL/Elasticsearch Specific Functionality * Removing after block from Elasticsearch suite --- .../Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml index 81debdb0d448e..d612f5bd17a2f 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Suite/SearchEngineElasticsearchSuite.xml @@ -12,11 +12,7 @@ <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> - <after> - <magentoCLI stepKey="setSearchEngineToMysql" command="config:set {{SearchEngineMysqlConfigData.path}} {{SearchEngineMysqlConfigData.value}}"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </after> + <after></after> <include> <group name="SearchEngineElasticsearch" /> </include> From b64a485793d81b0734539e27a0c049b765186991 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 2 Oct 2019 18:40:52 +0300 Subject: [PATCH 0768/2437] GraphQl-972: static fix --- .../Magento/TestFramework/App/ApiMutableScopeConfig.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php b/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php index 63a0ec0f7f48d..fa0cebece9a96 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/App/ApiMutableScopeConfig.php @@ -89,13 +89,12 @@ private function persistConfig($path, $value, $scopeType, $scopeCode): void { $pathParts = explode('/', $path); $store = 0; - if ($scopeType === \Magento\Store\Model\ScopeInterface::SCOPE_STORE) { - if ($scopeCode !== null) { - $store = ObjectManager::getInstance() + if ($scopeType === \Magento\Store\Model\ScopeInterface::SCOPE_STORE + && $scopeCode !== null) { + $store = ObjectManager::getInstance() ->get(\Magento\Store\Api\StoreRepositoryInterface::class) ->get($scopeCode) ->getId(); - } } $configData = [ 'section' => $pathParts[0], From 0b6bd0c5a77e26e9d22cbd8a45d91af76a2514d4 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 2 Oct 2019 11:18:45 -0500 Subject: [PATCH 0769/2437] MC-18685: Remove custom layout updates from admin --- .../Adminhtml/Page/PostDataProcessor.php | 3 +- .../Cms/Controller/Adminhtml/Page/Save.php | 27 +----- app/code/Magento/Cms/Model/Page.php | 61 ++++++++++--- .../CustomLayout/CustomLayoutRepository.php | 29 ++++++- .../Magento/Cms/Model/Page/DataProvider.php | 11 ++- .../Model/Page/CustomLayoutRepositoryTest.php | 86 +++++++++++++++---- 6 files changed, 158 insertions(+), 59 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index c46bcb8f247aa..5172752fb29bd 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -63,7 +63,6 @@ public function __construct( */ public function filter($data) { - unset($data['layout_update_selected']); $filterRules = []; foreach (['custom_theme_from', 'custom_theme_to'] as $dateField) { @@ -140,7 +139,7 @@ private function validateData($data, $layoutXmlValidator) if (!empty($data['layout_update_xml']) && !$layoutXmlValidator->isValid($data['layout_update_xml'])) { return false; } - + if (!empty($data['custom_layout_update_xml']) && !$layoutXmlValidator->isValid($data['custom_layout_update_xml']) ) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 38901c6c00dbe..8ad33f90b7c88 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -48,26 +48,19 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac */ private $pageRepository; - /** - * @var CustomLayoutRepositoryInterface - */ - private $customLayoutRepository; - /** * @param Action\Context $context * @param PostDataProcessor $dataProcessor * @param DataPersistorInterface $dataPersistor * @param \Magento\Cms\Model\PageFactory|null $pageFactory * @param \Magento\Cms\Api\PageRepositoryInterface|null $pageRepository - * @param CustomLayoutRepositoryInterface|null $customLayoutRepository */ public function __construct( Action\Context $context, PostDataProcessor $dataProcessor, DataPersistorInterface $dataPersistor, \Magento\Cms\Model\PageFactory $pageFactory = null, - \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null, - ?CustomLayoutRepositoryInterface $customLayoutRepository = null + \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null ) { $this->dataProcessor = $dataProcessor; $this->dataPersistor = $dataPersistor; @@ -98,14 +91,6 @@ public function execute() if (empty($data['page_id'])) { $data['page_id'] = null; } - //Either use existing custom layout XML or use a file. - $customLayoutFile = (string)$this->getRequest()->getParam('layout_update_selected'); - if ($customLayoutFile !== '_existing_') { - $data['custom_layout_update_xml'] = null; - $data['layout_update_xml'] = null; - } else { - $customLayoutFile = null; - } /** @var \Magento\Cms\Model\Page $model */ $model = $this->pageFactory->create(); @@ -128,7 +113,7 @@ public function execute() ['page' => $model, 'request' => $this->getRequest()] ); - $this->savePage($model, $customLayoutFile); + $this->savePage($model); $this->messageManager->addSuccessMessage(__('You saved the page.')); return $this->processResultRedirect($model, $resultRedirect, $data); } catch (LocalizedException $e) { @@ -147,21 +132,15 @@ public function execute() * Save the page. * * @param Page $page - * @param string|null $customLayoutFile * @return void * @throws \Throwable */ - private function savePage(Page $page, ?string $customLayoutFile): void + private function savePage(Page $page): void { if (!$this->dataProcessor->validate($page->getData())) { throw new \InvalidArgumentException('Page is invalid'); } $this->pageRepository->save($page); - if ($customLayoutFile) { - $this->customLayoutRepository->save(new CustomLayoutSelected($page->getId(), $customLayoutFile)); - } else { - $this->customLayoutRepository->deleteFor($page->getId()); - } } /** diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index 8eefe26236ba5..66aef2a6371b7 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -7,7 +7,9 @@ use Magento\Cms\Api\Data\PageInterface; use Magento\Cms\Helper\Page as PageHelper; +use Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractModel; @@ -57,6 +59,29 @@ class Page extends AbstractModel implements PageInterface, IdentityInterface */ private $scopeConfig; + /** + * @var CustomLayoutRepository + */ + private $customLayoutRepository; + + /** + * @inheritDoc + * + * @param CustomLayoutRepository|null $customLayoutRepository + */ + public function __construct( + \Magento\Framework\Model\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [], + ?CustomLayoutRepository $customLayoutRepository = null + ) { + parent::__construct($context, $registry, $resource, $resourceCollection, $data); + $this->customLayoutRepository = $customLayoutRepository + ?? ObjectManager::getInstance()->get(CustomLayoutRepository::class); + } + /** * Initialize resource model * @@ -548,22 +573,32 @@ public function beforeSave() $this->setUpdateTime(null); } - if (!$this->getId() || $originalIdentifier === $currentIdentifier) { - return parent::beforeSave(); + if ($this->getId() && $originalIdentifier !== $currentIdentifier) { + switch ($originalIdentifier) { + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_ROUTE_PAGE): + throw new LocalizedException( + __('This identifier is reserved for "CMS No Route Page" in configuration.') + ); + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_HOME_PAGE): + throw new LocalizedException(__('This identifier is reserved for "CMS Home Page" in configuration.')); + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_COOKIES_PAGE): + throw new LocalizedException( + __('This identifier is reserved for "CMS No Cookies Page" in configuration.') + ); + } } - switch ($originalIdentifier) { - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_ROUTE_PAGE): - throw new LocalizedException( - __('This identifier is reserved for "CMS No Route Page" in configuration.') - ); - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_HOME_PAGE): - throw new LocalizedException(__('This identifier is reserved for "CMS Home Page" in configuration.')); - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_COOKIES_PAGE): - throw new LocalizedException( - __('This identifier is reserved for "CMS No Cookies Page" in configuration.') - ); + //Removing deprecated custom layout update if a new value is provided + $layoutUpdate = $this->getData('layout_update_selected'); + if ($layoutUpdate === '_no_update_' || ($layoutUpdate && $layoutUpdate !== '_existing_')) { + $this->setCustomLayoutUpdateXml(null); + $this->setLayoutUpdateXml(null); + } + if ($layoutUpdate === '_no_update_' || $layoutUpdate === '_existing_') { + $layoutUpdate = null; } + $this->setData('layout_update_selected', $layoutUpdate); + $this->customLayoutRepository->validateLayoutSelectedFor($this); return parent::beforeSave(); } diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php index 9365bb31e970a..ce50bbe7c7476 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php @@ -82,6 +82,18 @@ private function findPage(int $id): PageModel return $page; } + /** + * Check whether the page can use this layout. + * + * @param PageModel $page + * @param string $layoutFile + * @return bool + */ + private function isLayoutValidFor(PageModel $page, string $layoutFile): bool + { + return in_array($layoutFile, $this->manager->fetchAvailableFiles($page), true); + } + /** * Save new custom layout file value for a page. * @@ -94,7 +106,7 @@ private function findPage(int $id): PageModel private function saveLayout(int $pageId, ?string $layoutFile): void { $page = $this->findPage($pageId); - if ($layoutFile !== null && !in_array($layoutFile, $this->manager->fetchAvailableFiles($page), true)) { + if ($layoutFile !== null && !$this->isLayoutValidFor($page, $layoutFile)) { throw new \InvalidArgumentException( $layoutFile .' is not available for page #' .$pageId ); @@ -114,6 +126,21 @@ public function save(CustomLayoutSelectedInterface $layout): void $this->saveLayout($layout->getPageId(), $layout->getLayoutFileId()); } + /** + * Validate layout update of given page model. + * + * @param PageModel $page + * @return void + * @throws LocalizedException + */ + public function validateLayoutSelectedFor(PageModel $page): void + { + $layoutFile = $page->getData('layout_update_selected'); + if ($layoutFile && !$this->isLayoutValidFor($page, $layoutFile)) { + throw new LocalizedException(__('Invalid Custom Layout Update selected')); + } + } + /** * @inheritDoc */ diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index e75097ca163e4..41010575a1f27 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -48,6 +48,11 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ private $customLayoutManager; + /** + * @var CollectionFactory + */ + private $collectionFactory; + /** * @param string $name * @param string $primaryFieldName @@ -76,6 +81,7 @@ public function __construct( ?CustomLayoutManagerInterface $customLayoutManager = null ) { $this->collection = $pageCollectionFactory->create(); + $this->collectionFactory = $pageCollectionFactory; $this->dataPersistor = $dataPersistor; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); @@ -93,6 +99,8 @@ public function __construct( private function findCurrentPage(): ?Page { if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { + //Loading data for the collection. + $this->getData(); return $this->collection->getItemById($pageId); } @@ -120,6 +128,7 @@ public function getData() if (isset($this->loadedData)) { return $this->loadedData; } + $this->collection = $this->collectionFactory->create(); $items = $this->collection->getItems(); /** @var $page \Magento\Cms\Model\Page */ foreach ($items as $page) { @@ -176,7 +185,7 @@ public function getMeta() } //List of custom layout files available for current page. - $options = [['label' => 'No update', 'value' => '']]; + $options = [['label' => 'No update', 'value' => '_no_update_']]; if ($page = $this->findCurrentPage()) { //We must have a specific page selected. //If custom layout XML is set then displaying this special option. diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php index 12b436fd32411..e3422cd81638b 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php @@ -11,9 +11,11 @@ use Magento\Cms\Model\Page; use Magento\Cms\Model\PageFactory; use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\View\Model\Layout\Merge; use Magento\Framework\View\Model\Layout\MergeFactory; +use Magento\TestFramework\Cms\Model\CustomLayoutManager; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -32,29 +34,41 @@ class CustomLayoutRepositoryTest extends TestCase */ private $pageFactory; + /** + * @var CustomLayoutManager + */ + private $fakeManager; + + /** + * @var IdentityMap + */ + private $identityMap; + /** * @inheritDoc */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); + $this->fakeManager = $objectManager->get(CustomLayoutManager::class); + $this->repo = $objectManager->create(CustomLayoutRepositoryInterface::class, ['manager' => $this->fakeManager]); + $this->pageFactory = $objectManager->get(PageFactory::class); + $this->identityMap = $objectManager->get(IdentityMap::class); + } - //Mocking available list of files for the page. - $handles = [ - 'cms_page_view_selectable_page100_select1', - 'cms_page_view_selectable_page100_select2' - ]; - $processor = $this->getMockBuilder(Merge::class)->disableOriginalConstructor()->getMock(); - $processor->method('getAvailableHandles')->willReturn($handles); - $processorFactory = $this->getMockBuilder(MergeFactory::class)->disableOriginalConstructor()->getMock(); - $processorFactory->method('create')->willReturn($processor); - $manager = $objectManager->create( - CustomLayoutManagerInterface::class, - ['layoutProcessorFactory' => $processorFactory] - ); - $this->repo = $objectManager->create(CustomLayoutRepositoryInterface::class, ['manager' => $manager]); + /** + * Create page instance. + * + * @param string $id + * @return Page + */ + private function createPage(string $id): Page + { + $page = $this->pageFactory->create(['customLayoutRepository' => $this->repo]); + $page->load($id, 'identifier'); + $this->identityMap->add($page); - $this->pageFactory = $objectManager->get(PageFactory::class); + return $page; } /** @@ -65,10 +79,9 @@ protected function setUp() */ public function testCustomLayout(): void { - /** @var Page $page */ - $page = $this->pageFactory->create(); - $page->load('page100', 'identifier'); + $page = $this->createPage('page100'); $pageId = (int)$page->getId(); + $this->fakeManager->fakeAvailableFiles($pageId, ['select1', 'select2']); //Invalid file ID $exceptionRaised = null; @@ -99,4 +112,41 @@ public function testCustomLayout(): void } $this->assertTrue($notFound); } + + /** + * Test that layout updates are saved with save method. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @return void + */ + public function testSaved(): void + { + $page = $this->createPage('page100'); + $this->fakeManager->fakeAvailableFiles((int)$page->getId(), ['select1', 'select2']); + + //Special no-update instruction + $page->setData('layout_update_selected', '_no_update_'); + $page->save(); + $this->assertNull($page->getData('layout_update_selected')); + + //Existing file update + $page->setData('layout_update_selected', 'select1'); + $page->save(); + /** @var Page $page */ + $page = $this->pageFactory->create(); + $page->load('page100', 'identifier'); + $this->assertEquals('select1', $page->getData('layout_update_selected')); + $this->assertEquals('select1', $this->repo->getFor((int)$page->getId())->getLayoutFileId()); + + //Invalid file + $caught = null; + $page->setData('layout_update_selected', 'nonExisting'); + try { + $page->save(); + } catch (\Throwable $exception) { + $caught = $exception; + } + $this->assertInstanceOf(LocalizedException::class, $caught); + $this->assertEquals($caught->getMessage(), 'Invalid Custom Layout Update selected'); + } } From 41e73b35294e7f60f855888363dc9918b02027ab Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Wed, 2 Oct 2019 15:49:17 -0300 Subject: [PATCH 0770/2437] Added cdata to comment --- app/code/Magento/Backend/etc/adminhtml/system.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 343ecc0ee3d58..261229d2db638 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -481,7 +481,7 @@ <field id="base_url" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>Specify URL or {{base_url}} placeholder.</comment> + <comment><![CDATA[Specify URL or {{base_url}} placeholder.]]></comment> </field> <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Base Link URL</label> @@ -505,7 +505,7 @@ <field id="base_url" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Secure Base URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>Specify URL or {{base_url}}, or {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[Specify URL or {{base_url}}, or {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Secure Base Link URL</label> From fa63035141c04b706fe0570cfc9bb5b83fca62fb Mon Sep 17 00:00:00 2001 From: Andrew Molina <amolina@adobe.com> Date: Wed, 2 Oct 2019 14:27:22 -0500 Subject: [PATCH 0771/2437] MC-21435: Migrate failed QuickSearch related MFTF tests to SearchEngineMysqlSuite --- .../Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml | 1 + .../CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml | 3 +++ .../Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml | 1 + .../LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml | 1 + 4 files changed, 6 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml index b6417e12a6db7..89269a1ad0d9e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-6325"/> <useCaseId value="MAGETWO-58764"/> <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="ApiCategory" stepKey="createCategory"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 19db201e91f40..b283c5bfe70dd 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -124,6 +124,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-15034"/> <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> <group value="mtf_migrated"/> </annotations> <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,3); return ret;" stepKey="getFirstThreeLetters" before="searchStorefront"/> @@ -160,6 +161,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-14796"/> <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> <group value="mtf_migrated"/> </annotations> <before> @@ -242,6 +244,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-14797"/> <group value="CatalogSearch"/> + <group value="SearchEngineMysql"/> <group value="mtf_migrated"/> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index 1075f79aef187..d5b76888fccc7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -17,6 +17,7 @@ <severity value="MAJOR"/> <testCaseId value="MC-249"/> <group value="ConfigurableProduct"/> + <group value="SearchEngineMysql"/> </annotations> <before> <!-- TODO: This should be converted to an actionGroup once MQE-993 is fixed. --> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml index 721942f58f7cc..6d182d0b7a5e2 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-6092"/> <group value="LayeredNavigation"/> + <group value="SearchEngineMysql"/> </annotations> <before> <createData entity="productDropDownAttribute" stepKey="attribute"/> From 745ac409ea121d9d602ebf2d471531c97cf4746a Mon Sep 17 00:00:00 2001 From: Bruno Roeder <brunoadrielr@gmail.com> Date: Wed, 2 Oct 2019 17:13:20 -0300 Subject: [PATCH 0772/2437] Added cdata to all comments with curly brackets --- app/code/Magento/Backend/etc/adminhtml/system.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 261229d2db638..d80acc8850169 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -486,17 +486,17 @@ <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Base Link URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May start with {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May start with {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="base_static_url" translate="label comment" type="text" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URL for Static View Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May be empty or start with {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May be empty or start with {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="base_media_url" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Base URL for User Media Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May be empty or start with {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May be empty or start with {{unsecure_base_url}} placeholder.]]></comment> </field> </group> <group id="secure" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> @@ -510,17 +510,17 @@ <field id="base_link_url" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Secure Base Link URL</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May start with {{secure_base_url}} or {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May start with {{secure_base_url}} or {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="base_static_url" translate="label comment" type="text" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Secure Base URL for Static View Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="base_media_url" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Secure Base URL for User Media Files</label> <backend_model>Magento\Config\Model\Config\Backend\Baseurl</backend_model> - <comment>May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.</comment> + <comment><![CDATA[May be empty or start with {{secure_base_url}}, or {{unsecure_base_url}} placeholder.]]></comment> </field> <field id="use_in_frontend" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Secure URLs on Storefront</label> From 6509f403abb24f45daad958c978141343b215a71 Mon Sep 17 00:00:00 2001 From: Veronika Kurochkina <veronika_kurochkina@epam.com> Date: Thu, 3 Oct 2019 13:10:39 +0300 Subject: [PATCH 0773/2437] MC-17653: Cannot schedule update for catalog price rule for date attribute - Fix merge conflict --- .../Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index c364dca59223b..614d464130baa 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -75,6 +75,10 @@ <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> </actionGroup> + <actionGroup name="AdminCreateMultipleWebsiteCatalogPriceRule" extends="createCatalogPriceRule"> + <remove keyForRemoval="selectSite"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="['FirstWebsite', 'SecondWebsite']" stepKey="selectWebsite"/> + </actionGroup> <actionGroup name="CreateCatalogPriceRuleViaTheUi"> <arguments> <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> From 271e0b9ba64007095384c35c9416f187c6619bb8 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Thu, 3 Oct 2019 13:28:31 +0300 Subject: [PATCH 0774/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Model/Cart/GetShippingAddress.php | 23 ++++++++++++++++--- .../Model/Cart/SetShippingAddressesOnCart.php | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index 6e35091aecd7b..badfe8ca48108 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -12,6 +12,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote\Address; +use Magento\QuoteGraphQl\Model\Cart\Address\SaveQuoteAddressToCustomerAddressBook; /** * Get shipping address @@ -23,12 +24,21 @@ class GetShippingAddress */ private $quoteAddressFactory; + /** + * @var SaveQuoteAddressToCustomerAddressBook + */ + private $saveQuoteAddressToCustomerAddressBook; + /** * @param QuoteAddressFactory $quoteAddressFactory + * @param SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook */ - public function __construct(QuoteAddressFactory $quoteAddressFactory) - { + public function __construct( + QuoteAddressFactory $quoteAddressFactory, + SaveQuoteAddressToCustomerAddressBook $saveQuoteAddressToCustomerAddressBook + ) { $this->quoteAddressFactory = $quoteAddressFactory; + $this->saveQuoteAddressToCustomerAddressBook = $saveQuoteAddressToCustomerAddressBook; } /** @@ -62,8 +72,15 @@ public function execute(ContextInterface $context, array $shippingAddressInput): ); } + $customerId = $context->getUserId(); + if (null === $customerAddressId) { $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); + + // need to save address only for registered user and if save_in_address_book = true + if (0 !== $customerId && !empty($addressInput['save_in_address_book'])) { + $this->saveQuoteAddressToCustomerAddressBook->execute($shippingAddress, $customerId); + } } else { if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); @@ -71,7 +88,7 @@ public function execute(ContextInterface $context, array $shippingAddressInput): $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress( (int)$customerAddressId, - $context->getUserId() + $customerId ); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index d4f2853467b39..6b1296eaf3752 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -7,7 +7,6 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; @@ -21,6 +20,7 @@ class SetShippingAddressesOnCart implements SetShippingAddressesOnCartInterface * @var AssignShippingAddressToCart */ private $assignShippingAddressToCart; + /** * @var GetShippingAddress */ From 179c5a2f3a3748beeec43807738674438f793b85 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Thu, 3 Oct 2019 06:26:10 -0500 Subject: [PATCH 0775/2437] MC-15986: Category Filtering - added description changes from review --- app/code/Magento/CatalogGraphQl/etc/schema.graphqls | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 5e8f5590bbd35..5712db9f8bc77 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -16,7 +16,7 @@ type Query { @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @deprecated(reason: "Use 'categoryList' query instead of 'category' query") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") categoryList( filters: CategoryFilterInput @doc(description: "Identifies which Category filter inputs to search for and return.") - ): [CategoryTree] @doc(description: "Categories filter.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") + ): [CategoryTree] @doc(description: "Returns an array of categories based on the specified filters.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryList") } type Price @doc(description: "The Price object defines the price of a product as well as any tax-related adjustments.") { @@ -113,7 +113,7 @@ type CustomizableAreaValue @doc(description: "CustomizableAreaValue defines the max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option.") } -type CategoryTree implements CategoryInterface @doc(description: "Category Tree implementation.") { +type CategoryTree implements CategoryInterface @doc(description: "An array containing the filtered categories and their subcategories") { children: [CategoryTree] @doc(description: "Child categories tree.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } @@ -282,7 +282,7 @@ input ProductAttributeFilterInput @doc(description: "ProductAttributeFilterInput input CategoryFilterInput @doc(description: "CategoryFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { - ids: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies and filters the category.") + ids: FilterEqualTypeInput @doc(description: "Filter by category ID that uniquely identifies the category.") url_key: FilterEqualTypeInput @doc(description: "Filter by the part of the URL that identifies the category") name: FilterMatchTypeInput @doc(description: "Filter by the display name of the category.") } From fa4567a98747918f0e8554443897b55584c16e1f Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Thu, 3 Oct 2019 06:35:24 -0500 Subject: [PATCH 0776/2437] MC-15986: Category Filtering - added resolver for category tree --- .../Model/Category/CategoryFilter.php | 66 +++++++++++ .../Model/Resolver/CategoryList.php | 105 ++++++++++++++++++ .../Products/Query/FieldSelection.php | 6 +- 3 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php new file mode 100644 index 0000000000000..574f5dd2715ce --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Category;; + +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Catalog\Api\CategoryRepositoryInterface; + +/** + * Category filter allows to filter collection using 'id, url_key, name' from search criteria. + */ +class CategoryFilter +{ + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @param CategoryRepositoryInterface $categoryRepository; + * @param CollectionFactory $collectionFactory + */ + public function __construct( + CategoryRepositoryInterface $categoryRepository, + CollectionFactory $collectionFactory + ) { + $this->categoryRepository = $categoryRepository; + $this->collectionFactory = $collectionFactory; + } + + /** + * Filter for filtering the requested categories id's based on url_key, ids, name in the result. + * + * @param array $args + * + * @return array|int + */ + public function applyFilters(array $args) + { + $categoryCollection = $this->collectionFactory->create(); + foreach($args['filters'] as $field => $cond){ + foreach($cond as $condType => $value){ + if($field === 'ids'){ + $categoryCollection->addIdFilter($value); + } else { + $categoryCollection->addAttributeToFilter($field, [$condType => $value]); + } + } + } + $categoryIds = []; + $categoriesData = $categoryCollection->getData(); + foreach ($categoriesData as $categoryData){ + $categoryIds[] = (int)$categoryData['entity_id']; + } + return $categoryIds; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php new file mode 100644 index 0000000000000..932b88c68f1f5 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver; + +use Magento\Catalog\Model\Category; +use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; +use Magento\CatalogGraphQl\Model\Category\CategoryFilter; + +/** + * Category tree resolver, used for GraphQL category data request processing. + */ +class CategoryList implements ResolverInterface +{ + /** + * Name of type in GraphQL + */ + const CATEGORY_INTERFACE = 'CategoryInterface'; + + /** + * @var CategoryTree + */ + private $categoryTree; + + /** + * @var CategoryFilter + */ + private $categoryFilter; + + /** + * @var ExtractDataFromCategoryTree + */ + private $extractDataFromCategoryTree; + + /** + * @var CheckCategoryIsActive + */ + private $checkCategoryIsActive; + + /** + * @param CategoryTree $categoryTree + * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree + * @param CheckCategoryIsActive $checkCategoryIsActive + * @param CategoryFilter $categoryFilter + */ + public function __construct( + CategoryTree $categoryTree, + ExtractDataFromCategoryTree $extractDataFromCategoryTree, + CheckCategoryIsActive $checkCategoryIsActive, + CategoryFilter $categoryFilter + ) { + $this->categoryTree = $categoryTree; + $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; + $this->checkCategoryIsActive = $checkCategoryIsActive; + $this->categoryFilter = $categoryFilter; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (isset($value[$field->getName()])) { + return $value[$field->getName()]; + } + + if (!isset($args['filters'])) { + throw new GraphQlInputException( + __( "'filters' input argument is required.") + ); + } + + $rootCategoryIds = $this->categoryFilter->applyFilters($args); + $result = []; + $categoriesTreeData = []; + + foreach ($rootCategoryIds as $rootCategoryId) { + if ($rootCategoryId !== Category::TREE_ROOT_ID) { + $this->checkCategoryIsActive->execute($rootCategoryId); + } + $categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId); + + if (empty($categoriesTree) || ($categoriesTree->count() == 0)) { + throw new GraphQlNoSuchEntityException(__('Category doesn\'t exist')); + } + $categoriesTreeData[] = $categoriesTree; + } + + foreach ($categoriesTreeData as $treeData ) { + $result[] = $this->extractDataFromCategoryTree->execute($treeData); + } + return current($result); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php index 3912bab05ebbe..ae4f2e911a5b0 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php @@ -60,9 +60,9 @@ private function getProductFields(ResolveInfo $info): array $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames); } } - - $fieldNames = array_merge(...$fieldNames); - + if (!empty($fieldNames)) { + $fieldNames = array_merge(...$fieldNames); + } return $fieldNames; } From fd7522ff7c584a123be556c3b8fbb89d05a95c15 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Thu, 3 Oct 2019 14:40:50 +0300 Subject: [PATCH 0777/2437] graphQl-914: [Customer] Improve consistency of country field in customer address --- .../Address/CreateCustomerAddress.php | 1 + .../Address/ExtractCustomerAddressData.php | 4 +++ .../CustomerGraphQl/etc/schema.graphqls | 5 ++-- .../Customer/CreateCustomerAddressTest.php | 21 ++++++++------- .../Customer/UpdateCustomerAddressTest.php | 27 ++++++++++--------- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php index 474bd99a8f136..9637b3e555b8b 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php @@ -67,6 +67,7 @@ public function __construct( */ public function execute(int $customerId, array $data): AddressInterface { + // It is needed because AddressInterface has country_id field. if (isset($data['country_code'])) { $data['country_id'] = $data['country_code']; } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php index 8741bff7aa88d..7992ca8342921 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php @@ -127,6 +127,10 @@ public function execute(AddressInterface $address): array $addressData['customer_id'] = null; + if (isset($addressData['country_id'])) { + $addressData['country_code'] = $addressData['country_id']; + } + return $addressData; } } diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index fea55ec385675..9ce2d61aa458d 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -28,7 +28,7 @@ input CustomerAddressInput { city: String @doc(description: "The city or town") region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID") postcode: String @doc(description: "The customer's ZIP or postal code") - country_id: CountryCodeEnum @doc(description: "The customer's country") @deprecated(reason: "Use country_code instead.") + country_id: CountryCodeEnum @doc(description: "Deprecated, use country_code instead.") country_code: CountryCodeEnum @doc(description: "The customer's country") default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") @@ -103,7 +103,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform customer_id: Int @doc(description: "The customer ID") @deprecated(reason: "customer_id is not needed as part of CustomerAddress, address ID (id) is unique identifier for the addresses.") region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID") region_id: Int @deprecated(reason: "Region ID is excessive on storefront and region code should suffice for all scenarios") - country_id: String @doc(description: "The customer's country") + country_id: String @doc(description: "The customer's country") @deprecated(reason: "Use country_code instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") street: [String] @doc(description: "An array of strings that define the street number and name") company: String @doc(description: "The customer's company") telephone: String @doc(description: "The telephone number") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 8860965d07f05..9ccd3b0d46c7a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -139,7 +139,6 @@ public function testCreateCustomerAddress() */ public function testCreateCustomerAddressWithCountryCode() { - $customerId = 1; $newAddress = [ 'region' => [ 'region' => 'Arizona', @@ -195,7 +194,7 @@ public function testCreateCustomerAddressWithCountryCode() region_id region_code } - country_id + country_code street company telephone @@ -220,16 +219,14 @@ public function testCreateCustomerAddressWithCountryCode() $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); $this->assertArrayHasKey('createCustomerAddress', $response); $this->assertArrayHasKey('customer_id', $response['createCustomerAddress']); - $this->assertEquals($customerId, $response['createCustomerAddress']['customer_id']); + $this->assertEquals(null, $response['createCustomerAddress']['customer_id']); $this->assertArrayHasKey('id', $response['createCustomerAddress']); $address = $this->addressRepository->getById($response['createCustomerAddress']['id']); $this->assertEquals($address->getId(), $response['createCustomerAddress']['id']); - $newAddress['country_id'] = $newAddress['country_code']; - unset($newAddress['country_code']); - $this->assertCustomerAddressesFields($address, $response['createCustomerAddress']); - $this->assertCustomerAddressesFields($address, $newAddress); + $this->assertCustomerAddressesFields($address, $response['createCustomerAddress'], 'country_code'); + $this->assertCustomerAddressesFields($address, $newAddress, 'country_code'); } /** @@ -412,12 +409,16 @@ public function invalidInputDataProvider() * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, array $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + array $actualResponse, + string $countryFieldName = 'country_id' + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => $countryFieldName, 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php index 84525a55f8a9f..d92c003c080ef 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php @@ -85,7 +85,6 @@ public function testUpdateCustomerAddressWithCountryCode() { $userName = 'customer@example.com'; $password = 'password'; - $customerId = 1; $addressId = 1; $mutation = $this->getMutationWithCountryCode($addressId); @@ -93,15 +92,15 @@ public function testUpdateCustomerAddressWithCountryCode() $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); $this->assertArrayHasKey('updateCustomerAddress', $response); $this->assertArrayHasKey('customer_id', $response['updateCustomerAddress']); - $this->assertEquals($customerId, $response['updateCustomerAddress']['customer_id']); + $this->assertEquals(null, $response['updateCustomerAddress']['customer_id']); $this->assertArrayHasKey('id', $response['updateCustomerAddress']); $address = $this->addressRepository->getById($addressId); $this->assertEquals($address->getId(), $response['updateCustomerAddress']['id']); - $this->assertCustomerAddressesFields($address, $response['updateCustomerAddress']); + $this->assertCustomerAddressesFields($address, $response['updateCustomerAddress'], 'country_code'); $updateAddress = $this->getAddressDataCanadaCountry(); - $this->assertCustomerAddressesFields($address, $updateAddress); + $this->assertCustomerAddressesFields($address, $updateAddress, 'country_code'); } /** @@ -159,12 +158,16 @@ public function testUpdateCustomerAddressWithMissingAttribute() * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + $actualResponse, + string $countryFieldName = 'country_id' + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => $countryFieldName, 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], @@ -443,7 +446,7 @@ private function getAddressDataCanadaCountry(): array 'region_id' => 66, 'region_code' => 'AB' ], - 'country_id' => 'CA', + 'country_code' => 'CA', 'street' => ['Line 1 Street', 'Line 2'], 'company' => 'Company Name', 'telephone' => '123456789', @@ -531,8 +534,8 @@ private function getMutation(int $addressId): string private function getMutationWithCountryCode(int $addressId): string { $updateAddress = $this->getAddressDataCanadaCountry(); - $defaultShippingText = $updateAddress['default_shipping'] ? "true" : "false"; - $defaultBillingText = $updateAddress['default_billing'] ? "true" : "false"; + $defaultShippingText = $updateAddress['default_shipping'] ? 'true' : 'false'; + $defaultBillingText = $updateAddress['default_billing'] ? 'true' : 'false'; $mutation = <<<MUTATION @@ -543,7 +546,7 @@ private function getMutationWithCountryCode(int $addressId): string region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_code: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -566,7 +569,7 @@ private function getMutationWithCountryCode(int $addressId): string region_id region_code } - country_id + country_code street company telephone From 5ad96052adf4a83e1d84533e37e0c801edb9a872 Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Thu, 3 Oct 2019 14:45:58 +0300 Subject: [PATCH 0778/2437] graphQl-509: `save_in_address_book` has no impact on Address Book --- .../Model/Cart/GetShippingAddress.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php index badfe8ca48108..b25a8aed6ca8d 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetShippingAddress.php @@ -72,6 +72,26 @@ public function execute(ContextInterface $context, array $shippingAddressInput): ); } + $shippingAddress = $this->createShippingAddress($context, $customerAddressId, $addressInput); + + return $shippingAddress; + } + + /** + * Create shipping address. + * + * @param ContextInterface $context + * @param int|null $customerAddressId + * @param array|null $addressInput + * + * @return \Magento\Quote\Model\Quote\Address + * @throws GraphQlAuthorizationException + */ + private function createShippingAddress( + ContextInterface $context, + ?int $customerAddressId, + ?array $addressInput + ) { $customerId = $context->getUserId(); if (null === $customerAddressId) { From f5a7ac98dadb79e5c994fee06a5e03b8346e1671 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 3 Oct 2019 16:02:55 +0300 Subject: [PATCH 0779/2437] MC-5233: DateTime product attributes support --- .../Product/Attribute/Edit/Tab/Advanced.php | 50 +++++- .../Product/Attribute/Edit/Tab/Main.php | 112 +++++++++----- .../Attribute/Edit/Tab/AdvancedTest.php | 104 ++++++++++--- .../Unit/Ui/Component/ColumnFactoryTest.php | 143 ++++++++++++++++-- .../Product/Form/Modifier/EavTest.php | 100 +++++++----- .../Catalog/Ui/Component/ColumnFactory.php | 72 +++++++-- .../Catalog/Ui/Component/Listing/Columns.php | 5 +- .../Product/Form/Modifier/Eav.php | 16 ++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 17 +++ app/code/Magento/Catalog/etc/config.xml | 7 + .../catalog/product/attribute/js.phtml | 1 + .../product_attribute_add_form.xml | 26 ++++ .../Magento/Eav/Model/Entity/Attribute.php | 37 ++++- .../Entity/Attribute/Frontend/Datetime.php | 6 +- .../Attribute/Frontend/DatetimeTest.php | 62 ++++++-- .../Test/Unit/Model/Entity/AttributeTest.php | 2 + .../adminhtml/web/js/product-attributes.js | 8 + app/code/Magento/Ui/Component/Filters.php | 3 + .../Ui/Component/Filters/Type/Date.php | 47 ++++-- .../Component/Form/Element/DataType/Date.php | 24 ++- .../Unit/Component/Filters/Type/DateTest.php | 113 +++++++++----- .../Ui/Test/Unit/Component/FiltersTest.php | 47 ++++-- .../Form/Element/DataType/DateTest.php | 69 ++++++++- .../Ui/view/base/web/js/form/element/date.js | 7 + .../Ui/view/base/web/js/grid/columns/date.js | 17 ++- .../view/base/web/js/grid/filters/filters.js | 4 + .../Ui/view/base/web/js/grid/filters/range.js | 8 + 27 files changed, 872 insertions(+), 235 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php index 1b6756968662f..89239a2e3e608 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php @@ -7,16 +7,20 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; use Magento\Backend\Block\Widget\Form\Generic; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; use Magento\Eav\Helper\Data; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime; /** - * Product attribute add/edit form main tab + * Product attribute add/edit advanced form tab * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Advanced extends Generic { @@ -70,7 +74,7 @@ public function __construct( * Adding product form elements for editing attribute * * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD) */ protected function _prepareForm() @@ -139,7 +143,21 @@ protected function _prepareForm() 'label' => __('Default Value'), 'title' => __('Default Value'), 'value' => $attributeObject->getDefaultValue(), - 'date_format' => $dateFormat + 'date_format' => $dateFormat, + ] + ); + + $timeFormat = $this->_localeDate->getTimeFormat(\IntlDateFormatter::SHORT); + $fieldset->addField( + 'default_value_datetime', + 'date', + [ + 'name' => 'default_value_datetime', + 'label' => __('Default Value'), + 'title' => __('Default Value'), + 'value' => $this->getLocalizedDateDefaultValue(), + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, ] ); @@ -266,7 +284,7 @@ protected function _initFormValues() /** * Retrieve attribute object from registry * - * @return mixed + * @return Attribute */ private function getAttributeObject() { @@ -285,4 +303,28 @@ private function getPropertyLocker() } return $this->propertyLocker; } + + /** + * Get localized date default value + * + * @return string + * @throws LocalizedException + */ + private function getLocalizedDateDefaultValue(): string + { + $attributeObject = $this->getAttributeObject(); + if (empty($attributeObject->getDefaultValue()) || $attributeObject->getFrontendInput() !== 'datetime') { + return (string)$attributeObject->getDefaultValue(); + } + + try { + $localizedDate = $this->_localeDate->date($attributeObject->getDefaultValue(), null, false); + $localizedDate->setTimezone(new \DateTimeZone($this->_localeDate->getConfigTimezone())); + $localizedDate = $localizedDate->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid.')); + } + + return $localizedDate; + } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php index 85cf37a1214b5..ddc7273432cb3 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php @@ -7,60 +7,60 @@ /** * Product attribute add/edit form main tab * - * @author Magento Core Team <core@magentocommerce.com> + * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply as HelperApply; use Magento\Eav\Block\Adminhtml\Attribute\Edit\Main\AbstractMain; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\DataObject; /** + * Product attribute add/edit form main tab + * * @api - * @SuppressWarnings(PHPMD.DepthOfInheritance) * @since 100.0.2 */ class Main extends AbstractMain { /** - * Adding product form elements for editing attribute - * - * @return $this - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @inheritdoc */ protected function _prepareForm() { parent::_prepareForm(); - /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attributeObject */ - $attributeObject = $this->getAttributeObject(); - /* @var $form \Magento\Framework\Data\Form */ - $form = $this->getForm(); - /* @var $fieldset \Magento\Framework\Data\Form\Element\Fieldset */ - $fieldset = $form->getElement('base_fieldset'); - $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; - foreach ($fieldset->getElements() as $element) { - /** @var \Magento\Framework\Data\Form\AbstractForm $element */ - if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { - $fieldsToRemove[] = $element->getId(); - } - } - foreach ($fieldsToRemove as $id) { - $fieldset->removeField($id); - } + $this->removeUnusedFields(); + $this->processFrontendInputTypes(); + + $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $this->getForm()]); + + return $this; + } + /** + * @inheritdoc + */ + protected function _getAdditionalElementTypes() + { + return ['apply' => HelperApply::class]; + } + + /** + * Process frontend input types for product attributes + * + * @return void + */ + private function processFrontendInputTypes(): void + { + $form = $this->getForm(); + /** @var AbstractElement $frontendInputElm */ $frontendInputElm = $form->getElement('frontend_input'); - $additionalTypes = [ - ['value' => 'price', 'label' => __('Price')], - ['value' => 'media_image', 'label' => __('Media Image')], - ]; - $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; - if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { - $additionalTypes[] = [ - 'value' => $attributeObject->getFrontendInput(), - 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], - ]; - } + $additionalTypes = $this->getAdditionalFrontendInputTypes(); - $response = new \Magento\Framework\DataObject(); + $response = new DataObject(); $response->setTypes([]); $this->_eventManager->dispatch('adminhtml_product_attribute_types', ['response' => $response]); $_hiddenFields = []; @@ -74,19 +74,51 @@ protected function _prepareForm() $frontendInputValues = array_merge($frontendInputElm->getValues(), $additionalTypes); $frontendInputElm->setValues($frontendInputValues); + } - $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $form]); + /** + * Get additional Frontend Input Types for product attributes + * + * @return array + */ + private function getAdditionalFrontendInputTypes(): array + { + $additionalTypes = [ + ['value' => 'price', 'label' => __('Price')], + ['value' => 'media_image', 'label' => __('Media Image')], + ]; - return $this; + $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; + $attributeObject = $this->getAttributeObject(); + if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { + $additionalTypes[] = [ + 'value' => $attributeObject->getFrontendInput(), + 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], + ]; + } + + return $additionalTypes; } /** - * Retrieve additional element types for product attributes + * Remove unused form fields * - * @return array + * @return void */ - protected function _getAdditionalElementTypes() + private function removeUnusedFields(): void { - return ['apply' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply::class]; + $form = $this->getForm(); + /* @var $fieldset Fieldset */ + $fieldset = $form->getElement('base_fieldset'); + $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; + foreach ($fieldset->getElements() as $element) { + /** @var AbstractElement $element */ + if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { + $fieldsToRemove[] = $element->getId(); + } + } + foreach ($fieldsToRemove as $id) { + $fieldset->removeField($id); + } } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php index 0352bc83cafb7..4d9345d0b3f22 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php @@ -5,66 +5,87 @@ */ namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; +use Magento\Eav\Helper\Data as EavHelper; +use Magento\Eav\Model\Entity\Type as EntityType; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\Form\Element\Text; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test product attribute add/edit advanced form tab + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AdvancedTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Adminhtml\Product\Attribute\Grid + * @var Advanced */ protected $block; /** - * @var \Magento\Framework\Data\FormFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ protected $formFactory; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registry; /** - * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ protected $localeDate; /** - * @var \Magento\Config\Model\Config\Source\Yesno|\PHPUnit_Framework_MockObject_MockObject + * @var Yesno|MockObject */ protected $yesNo; /** - * @var \Magento\Eav\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var EavHelper|MockObject */ protected $eavData; /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ protected $filesystem; /** - * @var PropertyLocker|\PHPUnit_Framework_MockObject_MockObject + * @var PropertyLocker|MockObject */ protected $propertyLocker; + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->registry = $this->createMock(\Magento\Framework\Registry::class); - $this->formFactory = $this->createMock(\Magento\Framework\Data\FormFactory::class); - $this->yesNo = $this->createMock(\Magento\Config\Model\Config\Source\Yesno::class); - $this->localeDate = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); - $this->eavData = $this->createMock(\Magento\Eav\Helper\Data::class); - $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $objectManager = new ObjectManager($this); + $this->registry = $this->createMock(Registry::class); + $this->formFactory = $this->createMock(FormFactory::class); + $this->yesNo = $this->createMock(Yesno::class); + $this->localeDate = $this->createMock(TimezoneInterface::class); + $this->eavData = $this->createMock(EavHelper::class); + $this->filesystem = $this->createMock(Filesystem::class); $this->propertyLocker = $this->createMock(PropertyLocker::class); $this->block = $objectManager->getObject( - \Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced::class, + Advanced::class, [ 'registry' => $this->registry, 'formFactory' => $this->formFactory, @@ -77,17 +98,35 @@ protected function setUp() ); } + /** + * Test the block's html output + */ public function testToHtml() { - $fieldSet = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $form = $this->createMock(\Magento\Framework\Data\Form::class); + $defaultValue = 'default_value'; + $localizedDefaultValue = 'localized_default_value'; + $frontendInput = 'datetime'; + $dateFormat = 'mm/dd/yy'; + $timeFormat = 'H:i:s:'; + $timeZone = 'America/Chicago'; + + $fieldSet = $this->createMock(Fieldset::class); + $form = $this->createMock(Form::class); $attributeModel = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['getDefaultValue', 'setDisabled', 'getId', 'getEntityType', 'getIsUserDefined', 'getAttributeCode'] + Attribute::class, + [ + 'getDefaultValue', + 'setDisabled', + 'getId', + 'getEntityType', + 'getIsUserDefined', + 'getAttributeCode', + 'getFrontendInput' + ] ); - $entityType = $this->createMock(\Magento\Eav\Model\Entity\Type::class); - $formElement = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Text::class, ['setDisabled']); - $directoryReadInterface = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + $entityType = $this->createMock(EntityType::class); + $formElement = $this->createPartialMock(Text::class, ['setDisabled']); + $directoryReadInterface = $this->createMock(ReadInterface::class); $this->registry->expects($this->any())->method('registry')->with('entity_attribute') ->willReturn($attributeModel); @@ -95,13 +134,28 @@ public function testToHtml() $form->expects($this->any())->method('addFieldset')->willReturn($fieldSet); $form->expects($this->any())->method('getElement')->willReturn($formElement); $fieldSet->expects($this->any())->method('addField')->willReturnSelf(); - $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn('default_value'); + $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn($defaultValue); $attributeModel->expects($this->any())->method('setDisabled')->willReturnSelf(); $attributeModel->expects($this->any())->method('getId')->willReturn(1); $attributeModel->expects($this->any())->method('getEntityType')->willReturn($entityType); $attributeModel->expects($this->any())->method('getIsUserDefined')->willReturn(false); $attributeModel->expects($this->any())->method('getAttributeCode')->willReturn('attribute_code'); - $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn('mm/dd/yy'); + $attributeModel->expects($this->any())->method('getFrontendInput')->willReturn($frontendInput); + + $dateTimeMock = $this->createMock(\DateTime::class); + $dateTimeMock->expects($this->once())->method('setTimezone')->with(new \DateTimeZone($timeZone)); + $dateTimeMock->expects($this->once()) + ->method('format') + ->with(DateTime::DATETIME_PHP_FORMAT) + ->willReturn($localizedDefaultValue); + $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn($dateFormat); + $this->localeDate->expects($this->any())->method('getTimeFormat')->willReturn($timeFormat); + $this->localeDate->expects($this->once())->method('getConfigTimezone')->willReturn($timeZone); + $this->localeDate->expects($this->once()) + ->method('date') + ->with($defaultValue, null, false) + ->willReturn($dateTimeMock); + $entityType->expects($this->any())->method('getEntityTypeCode')->willReturn('entity_type_code'); $this->eavData->expects($this->any())->method('getFrontendClasses')->willReturn([]); $formElement->expects($this->exactly(2))->method('setDisabled')->willReturnSelf(); diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 774edcfeb6b64..f002173de7996 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -7,14 +7,16 @@ namespace Magento\Catalog\Test\Unit\Ui\Component; -use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Catalog\Ui\Component\ColumnFactory; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\ColumnInterface; use Magento\Ui\Component\Filters\FilterModifier; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * ColumnFactory test. @@ -32,25 +34,30 @@ class ColumnFactoryTest extends TestCase private $objectManager; /** - * @var ProductAttributeInterface|\PHPUnit\Framework\MockObject\MockObject + * @var Attribute|MockObject */ private $attribute; /** - * @var ContextInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ContextInterface|MockObject */ private $context; /** - * @var UiComponentFactory|\PHPUnit\Framework\MockObject\MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var ColumnInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ColumnInterface|MockObject */ private $column; + /** + * @var TimezoneInterface|MockObject + */ + private $timeZone; + /** * @inheritdoc */ @@ -58,18 +65,30 @@ protected function setUp(): void { $this->objectManager = new ObjectManager($this); - $this->attribute = $this->getMockBuilder(ProductAttributeInterface::class) - ->setMethods(['usesSource']) - ->getMockForAbstractClass(); + $this->attribute = $this->createPartialMock( + Attribute::class, + [ + 'getAttributeCode', + 'getIsFilterableInGrid', + 'getFrontendInput', + 'getDefaultFrontendLabel', + 'getIsVisibleInGrid', + ] + ); $this->context = $this->createMock(ContextInterface::class); $this->uiComponentFactory = $this->createMock(UiComponentFactory::class); $this->column = $this->getMockForAbstractClass(ColumnInterface::class); $this->uiComponentFactory->method('create') ->willReturn($this->column); + $this->timeZone = $this->createMock(TimezoneInterface::class); - $this->columnFactory = $this->objectManager->getObject(ColumnFactory::class, [ - 'componentFactory' => $this->uiComponentFactory - ]); + $this->columnFactory = $this->objectManager->getObject( + ColumnFactory::class, + [ + 'componentFactory' => $this->uiComponentFactory, + 'timezone' => $this->timeZone, + ] + ); } /** @@ -96,7 +115,6 @@ public function testCreatedObject(): void * * @param array $filterModifiers * @param null|string $filter - * * @return void * @dataProvider filterModifiersProvider */ @@ -132,7 +150,7 @@ public function testCreateWithNotFilterableInGridAttribute(array $filterModifier } /** - * Filter modifiers data provider. + * Filter modifiers data provider * * @return array */ @@ -153,4 +171,101 @@ public function filterModifiersProvider(): array ], ]; } + + /** + * Test to create date column + * + * @param string $frontendInput + * @param bool $showsTime + * @param string $expectedDateFormat + * @param string $expectedTimezone + * @dataProvider createDateColumnDataProvider + */ + public function testCreateDateColumn( + string $frontendInput, + bool $showsTime, + string $expectedDateFormat, + string $expectedTimezone + ) { + $attributeCode = 'attribute_code'; + $dateFormat = 'date_format'; + $dateTimeFormat = 'datetime_format'; + $defaultTimezone = 'default_timezone'; + $configTimezone = 'config_timezone'; + $label = 'Date label'; + + $expectedConfig = [ + 'data' => [ + 'config' => [ + 'label' => __($label), + 'dataType' => 'date', + 'add_field' => true, + 'visible' => true, + 'filter' => 'dateRange', + 'component' => 'Magento_Ui/js/grid/columns/date', + 'timeZone' => $expectedTimezone, + 'dateFormat' => $expectedDateFormat, + 'options' => [ + 'showsTime' => $showsTime + ] + ], + ], + 'context' => $this->context, + ]; + + $this->attribute->method('getAttributeCode') + ->willReturn($attributeCode); + $this->attribute->method('getDefaultFrontendLabel') + ->willReturn($label); + $this->attribute->method('getIsFilterableInGrid') + ->willReturn(true); + $this->attribute->method('getIsVisibleInGrid') + ->willReturn(true); + $this->attribute->method('getFrontendInput') + ->willReturn($frontendInput); + + $this->timeZone->method('getDateFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateFormat); + $this->timeZone->method('getDateTimeFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateTimeFormat); + $this->timeZone->method('getDefaultTimezone') + ->willReturn($defaultTimezone); + $this->timeZone->method('getConfigTimezone') + ->willReturn($configTimezone); + + $this->uiComponentFactory->expects($this->once()) + ->method('create') + ->with($attributeCode, 'column', $expectedConfig) + ->willReturn($this->column); + + $this->assertEquals( + $this->column, + $this->columnFactory->create($this->attribute, $this->context) + ); + } + + /** + * Data provider to create date column test + * + * @return array + */ + public function createDateColumnDataProvider(): array + { + return [ + [ + 'frontendInput' => 'date', + 'showsTime' => false, + 'dateFormat' => 'date_format', + 'expectedTimezone' => 'default_timezone', + ], + [ + 'frontendInput' => 'datetime', + 'showsTime' => true, + 'expectedDateFormat' => 'datetime_format', + 'expectedTimezone' => 'config_timezone', + ], + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 88075b13f1430..834cb505e0903 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -38,6 +38,7 @@ use Magento\Framework\Stdlib\ArrayManager; use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory as EavAttributeFactory; use Magento\Framework\Event\ManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; /** * Class EavTest @@ -49,142 +50,142 @@ class EavTest extends AbstractModifierTest { /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfigMock; /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + * @var EavValidationRules|MockObject */ private $eavValidationRulesMock; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $requestMock; /** - * @var GroupCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollectionFactory|MockObject */ private $groupCollectionFactoryMock; /** - * @var GroupCollection|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollection|MockObject */ private $groupCollectionMock; /** - * @var Group|\PHPUnit_Framework_MockObject_MockObject + * @var Group|MockObject */ private $groupMock; /** - * @var EavAttribute|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttribute|MockObject */ private $attributeMock; /** - * @var EntityType|\PHPUnit_Framework_MockObject_MockObject + * @var EntityType|MockObject */ private $entityTypeMock; /** - * @var AttributeCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollectionFactory|MockObject */ private $attributeCollectionFactoryMock; /** - * @var AttributeCollection|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollection|MockObject */ private $attributeCollectionMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var FormElementMapper|\PHPUnit_Framework_MockObject_MockObject + * @var FormElementMapper|MockObject */ private $formElementMapperMock; /** - * @var MetaPropertiesMapper|\PHPUnit_Framework_MockObject_MockObject + * @var MetaPropertiesMapper|MockObject */ private $metaPropertiesMapperMock; /** - * @var SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteriaBuilder|MockObject */ private $searchCriteriaBuilderMock; /** - * @var ProductAttributeGroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeGroupRepositoryInterface|MockObject */ private $attributeGroupRepositoryMock; /** - * @var SearchCriteria|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteria|MockObject */ private $searchCriteriaMock; /** - * @var SortOrderBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SortOrderBuilder|MockObject */ private $sortOrderBuilderMock; /** - * @var ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeRepositoryInterface|MockObject */ private $attributeRepositoryMock; /** - * @var AttributeGroupInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeGroupInterface|MockObject */ private $attributeGroupMock; /** - * @var SearchResultsInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SearchResultsInterface|MockObject */ private $searchResultsMock; /** - * @var Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ private $eavAttributeMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ protected $storeMock; /** - * @var Currency|\PHPUnit_Framework_MockObject_MockObject + * @var Currency|MockObject */ protected $currencyMock; /** - * @var CurrencyLocale|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencyLocale|MockObject */ protected $currencyLocaleMock; /** - * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeInterface|MockObject */ protected $productAttributeMock; /** - * @var ArrayManager|\PHPUnit_Framework_MockObject_MockObject + * @var ArrayManager|MockObject */ protected $arrayManagerMock; /** - * @var EavAttributeFactory|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttributeFactory|MockObject */ protected $eavAttributeFactoryMock; /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $eventManagerMock; @@ -457,8 +458,10 @@ public function testModifyData() * @param string|null $attrValue * @param array $expected * @param bool $locked - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta + * @param string|null $frontendInput + * @param array $expectedCustomize + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta * @dataProvider setupAttributeMetaDataProvider */ public function testSetupAttributeMetaDefaultAttribute( @@ -466,7 +469,9 @@ public function testSetupAttributeMetaDefaultAttribute( bool $productRequired, $attrValue, array $expected, - $locked = false + bool $locked = false, + string $frontendInput = null, + array $expectedCustomize = [] ) : void { $configPath = 'arguments/data/config'; $groupCode = 'product-details'; @@ -492,6 +497,7 @@ public function testSetupAttributeMetaDefaultAttribute( $this->productAttributeMock->method('getDefaultValue')->willReturn('required_value'); $this->productAttributeMock->method('getAttributeCode')->willReturn('code'); $this->productAttributeMock->method('getValue')->willReturn('value'); + $this->productAttributeMock->method('getFrontendInput')->willReturn($frontendInput); $attributeMock = $this->getMockBuilder(AttributeInterface::class) ->setMethods(['getValue']) @@ -527,14 +533,16 @@ function ($value) use ($attributeOptionsExpected) { } ) ) - ->willReturn($expected); + ->willReturn($expected + $expectedCustomize); $this->arrayManagerMock->method('get')->willReturn([]); $this->arrayManagerMock->method('exists')->willReturn(true); + $actual = $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder); + $this->assertEquals( - $expected, - $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder) + $expected + $expectedCustomize, + $actual ); } @@ -660,7 +668,29 @@ public function setupAttributeMetaDataProvider() 'globalScope' => false, 'sortOrder' => 0, ], - ] + ], + 'datetime_null_prod_not_new_and_required' => [ + 'productId' => 1, + 'productRequired' => true, + 'attrValue' => 'val', + 'expected' => [ + 'dataType' => 'datetime', + 'formElement' => 'datetime', + 'visible' => null, + 'required' => true, + 'notice' => null, + 'default' => null, + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0, + ], + 'locked' => false, + 'frontendInput' => 'datetime', + 'expectedCustomize' => ['arguments' => ['data' => ['config' => ['options' => ['showsTime' => 1]]]]], + ], ]; } } diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 9a6a22fcb0985..c538273476b1f 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -5,7 +5,14 @@ */ namespace Magento\Catalog\Ui\Component; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Filters\FilterModifier; +use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** * Column Factory @@ -16,7 +23,7 @@ class ColumnFactory { /** - * @var \Magento\Framework\View\Element\UiComponentFactory + * @var UiComponentFactory */ protected $componentFactory; @@ -40,25 +47,36 @@ class ColumnFactory 'select' => 'select', 'multiselect' => 'multiselect', 'date' => 'date', + 'datetime' => 'date', ]; /** - * @param \Magento\Framework\View\Element\UiComponentFactory $componentFactory + * @var TimezoneInterface */ - public function __construct(\Magento\Framework\View\Element\UiComponentFactory $componentFactory) - { + private $timezone; + + /** + * @param UiComponentFactory $componentFactory + * @param TimezoneInterface|null $timezone + */ + public function __construct( + UiComponentFactory $componentFactory, + TimezoneInterface $timezone = null + ) { $this->componentFactory = $componentFactory; + $this->timezone = $timezone + ?? ObjectManager::getInstance()->get(TimezoneInterface::class); } /** * Create Factory * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute - * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context + * @param ProductAttributeInterface $attribute + * @param ContextInterface $context * @param array $config * - * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface - * @throws \Magento\Framework\Exception\LocalizedException + * @return ColumnInterface + * @throws LocalizedException */ public function create($attribute, $context, array $config = []) { @@ -84,19 +102,46 @@ public function create($attribute, $context, array $config = []) $optionData['__disableTmpl'] = true; } } - + $config['component'] = $this->getJsComponent($config['dataType']); - + + if ($config['dataType'] === 'date') { + $config += $this->getDateConfig($attribute); + } + $arguments = [ 'data' => [ 'config' => $config, ], 'context' => $context, ]; - + return $this->componentFactory->create($columnName, 'column', $arguments); } + /** + * Get config for Date columns + * + * @param ProductAttributeInterface $attribute + * @return array + */ + private function getDateConfig(ProductAttributeInterface $attribute): array + { + $isDatetime = $attribute->getFrontendInput() === 'datetime'; + $dateFormat = $isDatetime + ? $this->timezone->getDateTimeFormat(\IntlDateFormatter::MEDIUM) + : $this->timezone->getDateFormat(\IntlDateFormatter::MEDIUM); + $timeZone = $isDatetime + ? $this->timezone->getConfigTimezone() + : $this->timezone->getDefaultTimezone(); + + return [ + 'timeZone' => $timeZone, + 'dateFormat' => $dateFormat, + 'options' => [ 'showsTime' => $isDatetime], + ]; + } + /** * Get Js Component * @@ -112,7 +157,7 @@ protected function getJsComponent($dataType) /** * Get Data Type * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param ProductAttributeInterface $attribute * * @return string */ @@ -129,8 +174,9 @@ protected function getDataType($attribute) */ protected function getFilterType($frontendInput) { - $filtersMap = ['date' => 'dateRange']; + $filtersMap = ['date' => 'dateRange', 'datetime' => 'dateRange']; $result = array_replace_recursive($this->dataTypeMap, $filtersMap); + return $result[$frontendInput] ?? $result['default']; } } diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php index 8ea6d8b9e5a06..d7b9bc3846f49 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Ui\Component\Listing; /** + * Column IU component + * * @api * @since 100.0.2 */ @@ -30,6 +32,7 @@ class Columns extends \Magento\Ui\Component\Listing\Columns 'boolean' => 'select', 'multiselect' => 'select', 'date' => 'dateRange', + 'datetime' => 'datetimeRange', ]; /** @@ -52,7 +55,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function prepare() { diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 4039ff862f6fe..e41b2390930f0 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -746,6 +746,9 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // Gallery attribute is being handled by "Images And Videos" section $meta = []; break; + case 'datetime': + $meta = $this->customizeDatetimeAttribute($meta); + break; } //Checking access to design config. @@ -948,6 +951,19 @@ private function customizeWysiwyg(ProductAttributeInterface $attribute, array $m return $meta; } + /** + * Customize datetime attribute + * + * @param array $meta + * @return array + */ + private function customizeDatetimeAttribute(array $meta) + { + $meta['arguments']['data']['config']['options']['showsTime'] = 1; + + return $meta; + } + /** * Retrieve form element * diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c04cfb2dce00a..9e02d3da9f5d8 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -232,4 +232,21 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype"> + <arguments> + <argument name="optionsArray" xsi:type="array"> + <item name="450" xsi:type="array"> + <item name="value" xsi:type="string">datetime</item> + <item name="label" xsi:type="string" translate="true">Date and Time</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Ui\DataProvider\Mapper\FormElement"> + <arguments> + <argument name="mappings" xsi:type="array"> + <item name="datetime" xsi:type="string">date</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 20511f4ff2295..8506d2ae03032 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -78,5 +78,12 @@ <thumbnail_position>stretch</thumbnail_position> </watermark> </design> + <general> + <validator_data> + <input_types> + <datetime>datetime</datetime> + </input_types> + </validator_data> + </general> </default> </config> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index f020eddc35dbd..212a345f4bcbc 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -172,6 +172,7 @@ function switchDefaultValueField() break; case 'date': + case 'datetime': defaultValueDateVisibility = true; break; diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 6c5d37a92ea4a..3a6621137ed5a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -101,6 +101,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -287,6 +288,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -376,6 +378,29 @@ <dataScope>default_value_date</dataScope> </settings> </field> + <field name="default_value_datetime" component="Magento_Catalog/js/components/visible-on-option/date" sortOrder="35" formElement="date"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="valuesForOptions" xsi:type="array"> + <item name="datetime" xsi:type="string">datetime</item> + </item> + </item> + </argument> + <settings> + <dataType>text</dataType> + <label translate="true">Default Value</label> + <dataScope>default_value_datetime</dataScope> + </settings> + <formElements> + <date> + <settings> + <options> + <option name="showsTime" xsi:type="boolean">true</option> + </options> + </settings> + </date> + </formElements> + </field> <field name="is_unique" component="Magento_Catalog/js/components/visible-on-option/yesno" sortOrder="40" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -412,6 +437,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">date</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 8bd9ca2cc03c8..82eafa6174bb2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -285,13 +285,8 @@ public function beforeSave() // save default date value as timestamp if ($hasDefaultValue) { - try { - $locale = $this->_localeResolver->getLocale(); - $defaultValue = $this->_localeDate->date($defaultValue, $locale, false, false); - $this->setDefaultValue($defaultValue->format(DateTime::DATETIME_PHP_FORMAT)); - } catch (\Exception $e) { - throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); - } + $defaultValue = $this->getUtcDateDefaultValue($defaultValue); + $this->setDefaultValue($defaultValue); } } @@ -310,6 +305,29 @@ public function beforeSave() return parent::beforeSave(); } + /** + * Convert localized date default value to UTC + * + * @param string $defaultValue + * @return string + * @throws LocalizedException + */ + private function getUtcDateDefaultValue(string $defaultValue): string + { + $hasTime = $this->getFrontendInput() === 'datetime'; + try { + $defaultValue = $this->_localeDate->date($defaultValue, null, $hasTime, $hasTime); + if ($hasTime) { + $defaultValue->setTimezone(new \DateTimeZone($this->_localeDate->getDefaultTimezone())); + } + $utcValue = $defaultValue->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); + } + + return $utcValue; + } + /** * @inheritdoc * @@ -346,6 +364,7 @@ public function getBackendTypeByInput($type) break; case 'date': + case 'datetime': $field = 'datetime'; break; @@ -401,6 +420,10 @@ public function getDefaultValueByInput($type) $field = 'default_value_date'; break; + case 'datetime': + $field = 'default_value_datetime'; + break; + case 'boolean': $field = 'default_value_yesno'; break; diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php index a9cd3be246bb1..8effd73d34af2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php @@ -7,6 +7,8 @@ namespace Magento\Eav\Model\Entity\Attribute\Frontend; /** + * Entity datetime frontend attribute + * * @api * @since 100.0.2 */ @@ -42,10 +44,12 @@ public function getValue(\Magento\Framework\DataObject $object) $value = parent::getValue($object); if ($value) { + $timeType = $this->getAttribute()->getFrontendInput() === 'datetime' + ? \IntlDateFormatter::MEDIUM : \IntlDateFormatter::NONE; $data = $this->_localeDate->formatDateTime( new \DateTime($value), \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::NONE + $timeType ); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php index 66549f2e00415..c775548fc8c75 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php @@ -5,22 +5,31 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Frontend; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Attribute\Frontend\Datetime; +use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DatetimeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Entity datetime frontend attribute + */ +class DatetimeTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ private $localeDateMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var BooleanFactory|MockObject */ private $booleanFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AbstractAttribute|MockObject */ private $attributeMock; @@ -29,40 +38,63 @@ class DatetimeTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @inheritdoc + */ protected function setUp() { - $this->booleanFactoryMock = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory::class); - $this->localeDateMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + $this->booleanFactoryMock = $this->createMock(BooleanFactory::class); + $this->localeDateMock = $this->createMock(TimezoneInterface::class); $this->attributeMock = $this->createPartialMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getAttributeCode', 'getFrontendLabel', 'getData'] + AbstractAttribute::class, + ['getAttributeCode', 'getFrontendLabel', 'getFrontendInput'] ); $this->model = new Datetime($this->booleanFactoryMock, $this->localeDateMock); $this->model->setAttribute($this->attributeMock); } - public function testGetValue() + /** + * Test to retrieve attribute value + * + * @param string $frontendInput + * @param int $timeType + * @dataProvider getValueDataProvider + */ + public function testGetValue(string $frontendInput, int $timeType) { $attributeValue = '11-11-2011'; + $attributeCode = 'datetime'; $date = new \DateTime($attributeValue); - $object = new \Magento\Framework\DataObject(['datetime' => $attributeValue]); + $object = new DataObject([$attributeCode => $attributeValue]); $this->attributeMock->expects($this->any()) ->method('getAttributeCode') - ->willReturn('datetime'); + ->willReturn($attributeCode); $this->attributeMock->expects($this->any()) - ->method('getData') - ->with('frontend_input') - ->willReturn('text'); + ->method('getFrontendInput') + ->willReturn($frontendInput); $this->localeDateMock->expects($this->once()) ->method('formatDateTime') - ->with($date, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, null, null) + ->with($date, \IntlDateFormatter::MEDIUM, $timeType) ->willReturn($attributeValue); $this->assertEquals($attributeValue, $this->model->getValue($object)); } + /** + * Retrieve attribute value data provider + * + * @return array + */ + public function getValueDataProvider(): array + { + return [ + ['frontendInput' => 'date', 'timeType' => \IntlDateFormatter::NONE], + ['frontendInput' => 'datetime', 'timeType' => \IntlDateFormatter::MEDIUM], + ]; + } + /** * @param mixed $labelText * @param string $attributeCode diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 402497a1379c0..7aa5bca00f0b6 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -57,6 +57,7 @@ public static function dataGetBackendTypeByInput() ['image', 'text'], ['textarea', 'text'], ['date', 'datetime'], + ['datetime', 'datetime'], ['select', 'int'], ['boolean', 'int'], ['price', 'decimal'], @@ -91,6 +92,7 @@ public static function dataGetDefaultValueByInput() ['weight', 'default_value_text'], ['textarea', 'default_value_textarea'], ['date', 'default_value_date'], + ['datetime', 'default_value_datetime'], ['boolean', 'default_value_yesno'] ]; } diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index f795f99e8112d..9e48af20ee945 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -31,6 +31,7 @@ define([ defaultValueText: $('#default_value_text'), defaultValueTextarea: $('#default_value_textarea'), defaultValueDate: $('#default_value_date'), + defaultValueDatetime: $('#default_value_datetime'), defaultValueYesno: $('#default_value_yesno'), isGlobal: $('#is_global'), useProductImageForSwatch: $('#use_product_image_for_swatch'), @@ -178,6 +179,7 @@ define([ defaultValueTextVisibility = false, defaultValueTextareaVisibility = false, defaultValueDateVisibility = false, + defaultValueDatetimeVisibility = false, defaultValueYesnoVisibility = false, scopeVisibility = true, useProductImageForSwatch = false, @@ -203,6 +205,10 @@ define([ defaultValueDateVisibility = true; break; + case 'datetime': + defaultValueDatetimeVisibility = true; + break; + case 'boolean': defaultValueYesnoVisibility = true; break; @@ -256,6 +262,7 @@ define([ defaultValueTextVisibility = false; defaultValueTextareaVisibility = false; defaultValueDateVisibility = false; + defaultValueDatetimeVisibility = false; defaultValueYesnoVisibility = false; break; @@ -279,6 +286,7 @@ define([ this.setRowVisibility(this.defaultValueText, defaultValueTextVisibility); this.setRowVisibility(this.defaultValueTextarea, defaultValueTextareaVisibility); this.setRowVisibility(this.defaultValueDate, defaultValueDateVisibility); + this.setRowVisibility(this.defaultValueDatetime, defaultValueDatetimeVisibility); this.setRowVisibility(this.defaultValueYesno, defaultValueYesnoVisibility); this.setRowVisibility(this.isGlobal, scopeVisibility); diff --git a/app/code/Magento/Ui/Component/Filters.php b/app/code/Magento/Ui/Component/Filters.php index fe02c23af9c8a..5bf89ae7936e9 100644 --- a/app/code/Magento/Ui/Component/Filters.php +++ b/app/code/Magento/Ui/Component/Filters.php @@ -12,6 +12,8 @@ use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** + * Grid filters UI component + * * @api * @since 100.0.2 */ @@ -36,6 +38,7 @@ class Filters extends AbstractComponent implements ObserverInterface 'textRange' => 'filterRange', 'select' => 'filterSelect', 'dateRange' => 'filterDate', + 'datetimeRange' => 'filterDate', ]; /** diff --git a/app/code/Magento/Ui/Component/Filters/Type/Date.php b/app/code/Magento/Ui/Component/Filters/Type/Date.php index e854b888c45e6..28ad8568ebe31 100644 --- a/app/code/Magento/Ui/Component/Filters/Type/Date.php +++ b/app/code/Magento/Ui/Component/Filters/Type/Date.php @@ -9,6 +9,8 @@ use Magento\Ui\Component\Form\Element\DataType\Date as DataTypeDate; /** + * Date grid filter UI Component + * * @api * @since 100.0.2 */ @@ -84,30 +86,18 @@ protected function applyFilter() if (isset($value['from'])) { $this->applyFilterByType( 'gteq', - $this->wrappedComponent->convertDate( - $value['from'], - 0, - 0, - 0, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['from']) ); } if (isset($value['to'])) { $this->applyFilterByType( 'lteq', - $this->wrappedComponent->convertDate( - $value['to'], - 23, - 59, - 59, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['to'], 23, 59, 59) ); } } else { - $this->applyFilterByType('eq', $this->wrappedComponent->convertDate($value)); + $this->applyFilterByType('eq', $this->convertDatetime((string)$value)); } } } @@ -130,4 +120,31 @@ protected function applyFilterByType($type, $value) $this->getContext()->getDataProvider()->addFilter($filter); } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $value + * @param int $hour + * @param int $minute + * @param int $second + * @return \DateTime + */ + private function convertDatetime(string $value, int $hour = 0, int $minute = 0, int $second = 0): ?\DateTime + { + $value = $this->getData('config/options/showsTime') + ? $this->wrappedComponent->convertDatetime( + $value, + !$this->getData('config/skipTimeZoneConversion') + ) + : $this->wrappedComponent->convertDate( + $value, + $hour, + $minute, + $second, + !$this->getData('config/skipTimeZoneConversion') + ); + + return $value; + } } diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 31d2fe786cfd8..8ea6236e8e2e2 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -111,7 +111,7 @@ public function getComponentName() public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcTimeZone = true) { try { - $dateObj = $this->localeDate->date($date, $this->getLocale(), true); + $dateObj = $this->localeDate->date($date, $this->getLocale(), false); $dateObj->setTime($hour, $minute, $second); //convert store date to default date in UTC timezone without DST if ($setUtcTimeZone) { @@ -122,4 +122,26 @@ public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcT return null; } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $date + * @param bool $setUtcTimeZone + * @return \DateTime|null + */ + public function convertDatetime(string $date, bool $setUtcTimeZone = true): ?\DateTime + { + try { + $date = rtrim($date, 'Z'); + $dateObj = new \DateTime($date, new \DateTimeZone($this->localeDate->getConfigTimezone())); + //convert store date to default date in UTC timezone without DST + if ($setUtcTimeZone) { + $dateObj->setTimezone(new \DateTimeZone('UTC')); + } + return $dateObj; + } catch (\Exception $e) { + return null; + } + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 78456968cbef1..7038a587be0b0 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -13,6 +13,7 @@ use Magento\Ui\Component\Filters\Type\Date; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Form\Element\DataType\Date as FormDate; +use PHPUnit\Framework\MockObject\MockObject; /** * Class DateTest @@ -20,27 +21,27 @@ class DateTest extends \PHPUnit\Framework\TestCase { /** - * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ContextInterface|MockObject */ private $contextMock; /** - * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var FilterBuilder|MockObject */ private $filterBuilderMock; /** - * @var FilterModifier|\PHPUnit_Framework_MockObject_MockObject + * @var FilterModifier|MockObject */ private $filterModifierMock; /** - * @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var DataProviderInterface|MockObject */ private $dataProviderMock; @@ -89,18 +90,19 @@ public function testGetComponentName() * Run test prepare method * * @param string $name + * @param bool $showsTime * @param array $filterData * @param array|null $expectedCondition * @dataProvider getPrepareDataProvider * @return void */ - public function testPrepare($name, $filterData, $expectedCondition) + public function testPrepare(string $name, bool $showsTime, array $filterData, ?array $expectedCondition) { $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) ->disableOriginalConstructor() ->getMock(); $this->contextMock->expects(static::atLeastOnce())->method('getProcessor')->willReturn($processor); - /** @var FormDate $uiComponent */ + /** @var FormDate|MockObject $uiComponent */ $uiComponent = $this->getMockBuilder(FormDate::class) ->disableOriginalConstructor() ->getMock(); @@ -125,7 +127,7 @@ public function testPrepare($name, $filterData, $expectedCondition) ->willReturn($this->dataProviderMock); if ($expectedCondition !== null) { - $this->processFilters($name, $filterData, $expectedCondition, $uiComponent); + $this->processFilters($name, $showsTime, $filterData, $expectedCondition, $uiComponent); } $this->uiComponentFactory->expects($this->any()) @@ -139,7 +141,10 @@ public function testPrepare($name, $filterData, $expectedCondition) $this->filterBuilderMock, $this->filterModifierMock, [], - ['name' => $name] + [ + 'name' => $name, + 'config' => ['options' => ['showsTime' => $showsTime]], + ] ); $date->prepare(); } @@ -152,7 +157,7 @@ public function testPrepare($name, $filterData, $expectedCondition) * @param string $expectedDate * @param int $i * - * @return Filter|\PHPUnit_Framework_MockObject_MockObject + * @return Filter|MockObject */ private function getFilterMock($name, $expectedType, $expectedDate, &$i) { @@ -184,57 +189,87 @@ public function getPrepareDataProvider() { return [ [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => null]], - ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => null]], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], ], [ - 'test_date', - ['test_date' => ['from' => null, 'to' => '11-05-2015']], - ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => null, 'to' => '11-05-2015']], + 'expectedCondition' => ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], ], [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], - [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], + 'expectedCondition' => [ 'date_from' => '2015-05-11 00:00:00', 'type_from' => 'gteq', 'date_to' => '2015-05-11 23:59:59', 'type_to' => 'lteq' ], ], [ - 'test_date', - ['test_date' => '11-05-2015'], - ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => '11-05-2015'], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + ], + [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '', 'to' => '']], + 'expectedCondition' => null, ], [ - 'test_date', - ['test_date' => ['from' => '', 'to' => '']], - null, + 'name' => 'test_date', + 'showsTime' => true, + 'filterData' => ['test_date' => ['from' => '11-05-2015 10:20:00', 'to' => '11-05-2015 18:25:00']], + 'expectedCondition' => [ + 'date_from' => '2015-05-11 10:20:00', 'type_from' => 'gteq', + 'date_to' => '2015-05-11 18:25:00', 'type_to' => 'lteq' + ], ], ]; } /** - * @param $name - * @param $filterData - * @param $expectedCondition - * @param $uiComponent + * @param string $name + * @param bool $showsTime + * @param array $filterData + * @param array $expectedCondition + * @param MockObject $uiComponent */ - private function processFilters($name, $filterData, $expectedCondition, $uiComponent) - { + private function processFilters( + string $name, + bool $showsTime, + array $filterData, + array $expectedCondition, + FormDate $uiComponent + ) { if (is_string($filterData[$name])) { $uiComponent->expects(static::once()) - ->method('convertDate') + ->method($showsTime ? 'convertDatetime' : 'convertDate') ->with($filterData[$name]) ->willReturn(new \DateTime($filterData[$name])); } else { - $from = new \DateTime($filterData[$name]['from']); - $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); - $uiComponent->method('convertDate') - ->willReturnMap([ - [$filterData[$name]['from'], 0, 0, 0, true, $from], - [$filterData[$name]['to'], 23, 59, 59, true, $to], - ]); + if ($showsTime) { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to']); + $uiComponent->method('convertDatetime') + ->willReturnMap([ + [$filterData[$name]['from'], true, $from], + [$filterData[$name]['to'], true, $to], + ]); + } else { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); + $uiComponent->method('convertDate') + ->willReturnMap([ + [$filterData[$name]['from'], 0, 0, 0, true, $from], + [$filterData[$name]['to'], 23, 59, 59, true, $to], + ]); + } } $i = 0; diff --git a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php index 402fd30bf4d5b..19a1be69ca1d7 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php @@ -8,23 +8,27 @@ namespace Magento\Ui\Test\Unit\Component; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Magento\Ui\Component\Filters; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Filters; +use PHPUnit\Framework\MockObject\MockObject; /** * Unit tests for \Magento\Ui\Component\Filters class */ class FiltersTest extends \PHPUnit\Framework\TestCase { - /** @var Filters|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Filters|MockObject */ private $filters; - /** @var \Magento\Framework\View\Element\UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentInterface|MockObject */ private $uiComponentInterface; - /** @var \Magento\Framework\View\Element\UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentFactory|MockObject */ private $uiComponentFactory; - /** @var \Magento\Framework\View\Element\UiComponent\ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ContextInterface|MockObject */ private $context; /** @@ -33,13 +37,13 @@ class FiltersTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $this->uiComponentInterface = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) + $this->uiComponentInterface = $this->getMockBuilder(UiComponentInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->uiComponentFactory = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentFactory::class) + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + $this->context = $this->getMockBuilder(ContextInterface::class) ->disableOriginalConstructor() ->getMock(); $this->filters = $objectManager->getObject( @@ -52,7 +56,14 @@ protected function setUp() ); } - public function testUpdate() + /** + * Test to Update filter component according to $component + * + * @param string $filterType + * @param string $filterName + * @dataProvider updateDataProvider + */ + public function testUpdate(string $filterType, string $filterName) { $componentName = 'component_name'; $componentConfig = [0, 1, 2]; @@ -60,7 +71,10 @@ public function testUpdate() ->disableOriginalConstructor() ->setMethods(['getData', 'getName', 'getConfiguration']) ->getMockForAbstractClass(); - $columnInterface->expects($this->atLeastOnce())->method('getData')->with('config/filter')->willReturn('text'); + $columnInterface->expects($this->atLeastOnce()) + ->method('getData') + ->with('config/filter') + ->willReturn($filterType); $columnInterface->expects($this->atLeastOnce())->method('getName')->willReturn($componentName); $columnInterface->expects($this->once())->method('getConfiguration')->willReturn($componentConfig); $filterComponent = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) @@ -71,11 +85,22 @@ public function testUpdate() ->willReturnSelf(); $filterComponent->expects($this->once())->method('prepare')->willReturnSelf(); $this->uiComponentFactory->expects($this->once())->method('create') - ->with($componentName, 'filterInput', ['context' => $this->context]) + ->with($componentName, $filterName, ['context' => $this->context]) ->willReturn($filterComponent); $this->filters->update($columnInterface); /** Verify that filter is already set and it wouldn't be set again */ $this->filters->update($columnInterface); } + + /** + * @return array + */ + public function updateDataProvider(): array + { + return [ + ['text', 'filterInput'], + ['datetimeRange', 'filterDate'], + ]; + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php index cdb11f05daa8c..015c025e7c102 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php @@ -11,27 +11,35 @@ use Magento\Framework\View\Element\UiComponent\Context; use Magento\Framework\View\Element\UiComponent\Processor; use Magento\Ui\Component\Form\Element\DataType\Date; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DateTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Date form element + */ +class DateTest extends TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Context|MockObject */ private $contextMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var TimezoneInterface|MockObject */ private $localeDateMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var ResolverInterface|MockObject */ private $localeResolverMock; - /** @var \Magento\Ui\Component\Form\Element\DataType\Date */ + /** @var Date */ private $date; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Processor|MockObject */ private $processorMock; - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + /** @var ObjectManager */ private $objectManagerHelper; + /** + * @inheritdoc + */ public function setUp() { $this->contextMock = $this->createMock(Context::class); @@ -39,9 +47,12 @@ public function setUp() $this->localeResolverMock = $this->createMock(ResolverInterface::class); $this->objectManagerHelper = new ObjectManager($this); $this->processorMock = $this->createMock(Processor::class); - $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($this->processorMock); + $this->contextMock->method('getProcessor')->willReturn($this->processorMock); } + /** + * Test to Prepare component configuration with Time offset + */ public function testPrepareWithTimeOffset() { $this->date = new Date( @@ -72,6 +83,9 @@ public function testPrepareWithTimeOffset() $this->assertEquals($localeDateFormat, $config['options']['dateFormat']); } + /** + * Test to Prepare component configuration without Time offset + */ public function testPrepareWithoutTimeOffset() { $defaultDateFormat = 'MM/dd/y'; @@ -130,4 +144,43 @@ public function testPrepare() $this->assertEquals('America/Chicago', $configArray['storeTimeZone']); $this->assertEquals('de-DE', $configArray['options']['storeLocale']); } + + /** + * Test to Convert given date to default (UTC) timezone + * + * @param string $dateStr + * @param bool $setUtcTimeZone + * @param string $convertedDate + * @dataProvider convertDatetimeDataProvider + */ + public function testConvertDatetime(string $dateStr, bool $setUtcTimeZone, string $convertedDate) + { + $this->localeDateMock->method('getConfigTimezone') + ->willReturn('America/Los_Angeles'); + + $this->date = $this->objectManagerHelper->getObject( + Date::class, + [ + 'localeDate' => $this->localeDateMock, + ] + ); + + $this->assertEquals( + $convertedDate, + $this->date->convertDatetime($dateStr, $setUtcTimeZone)->format('Y-m-d H:i:s'), + "The date value wasn't converted" + ); + } + + /** + * @return array + */ + public function convertDatetimeDataProvider(): array + { + return [ + ['2019-09-30T12:32:00.000Z', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000Z', true, '2019-09-30 19:32:00'], + ]; + } } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/date.js b/app/code/Magento/Ui/view/base/web/js/form/element/date.js index ac28271e90a3b..1432372dd75a9 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/date.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/date.js @@ -107,6 +107,13 @@ define([ return this._super().observe(['shiftedValue']); }, + /** + * @inheritdoc + */ + getPreview: function () { + return this.shiftedValue(); + }, + /** * Prepares and sets date/time value that will be displayed * in the input field. diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js index 3f9c5b20d6215..cc69d990372c1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js @@ -9,8 +9,10 @@ define([ 'mageUtils', 'moment', - './column' -], function (utils, moment, Column) { + './column', + 'underscore', + 'moment-timezone-with-data' +], function (utils, moment, Column, _) { 'use strict'; return Column.extend({ @@ -20,9 +22,9 @@ define([ }, /** - * Overrides base method to normalize date format. + * Overrides base method to normalize date format * - * @returns {DateColumn} Chainable. + * @returns {DateColumn} Chainable */ initConfig: function () { this._super(); @@ -38,12 +40,15 @@ define([ * @returns {String} Formatted date. */ getLabel: function (value, format) { - var date; + var date = moment.utc(this._super()); if (this.storeLocale !== undefined) { moment.locale(this.storeLocale, utils.extend({}, this.calendarConfig)); } - date = moment(this._super()); + + if (!_.isUndefined(this.timeZone)) { + date = date.tz(this.timeZone); + } date = date.isValid() && value[this.index] ? date.format(format || this.dateFormat) : diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 98c3eb1c6f882..a6fae9df50c4d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -83,6 +83,10 @@ define([ component: 'Magento_Ui/js/grid/filters/range', rangeType: 'date' }, + datetimeRange: { + component: 'Magento_Ui/js/grid/filters/range', + rangeType: 'datetime' + }, textRange: { component: 'Magento_Ui/js/grid/filters/range', rangeType: 'text' diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js index ccfba8e98b6f4..1949234c89324 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js @@ -30,6 +30,14 @@ define([ dateFormat: 'MM/dd/YYYY', shiftedValue: 'filter' }, + datetime: { + component: 'Magento_Ui/js/form/element/date', + dateFormat: 'MM/dd/YYYY', + shiftedValue: 'filter', + options: { + showsTime: true + } + }, text: { component: 'Magento_Ui/js/form/element/abstract' }, From 9b6a45e8d0e448a726d06cb56952035acbc9a537 Mon Sep 17 00:00:00 2001 From: Aliaksei_Manenak <Aliaksei_Manenak@epam.com> Date: Thu, 3 Oct 2019 14:49:12 +0300 Subject: [PATCH 0780/2437] MC-18822: Increase test coverage for Content functional area - Skip until failed. https://jira.corp.magento.com/browse/MAGETWO-96420 --- .../testsuite/Magento/Newsletter/Controller/SubscriberTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php index b7b87d3b9e20d..bf19d6ddefc36 100644 --- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php +++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php @@ -83,6 +83,8 @@ public function testNewActionOwnerEmail() */ public function testCreatePosWithSubscribeEmailAction() { + $this->markTestSkipped('Skip until failed. MAGETWO-96420'); + $config = Bootstrap::getObjectManager()->get(MutableScopeConfigInterface::class); $accountConfirmationRequired = $config->getValue( AccountConfirmation::XML_PATH_IS_CONFIRM, From f8b47b3606bc4e852f31271313c57e34a3bda69f Mon Sep 17 00:00:00 2001 From: RomanKis <roman.kis.y@gmail.com> Date: Thu, 3 Oct 2019 17:12:26 +0300 Subject: [PATCH 0781/2437] graphQl-961: ShippingAddressInput.postcode: String, is not required by Schema --- .../Model/Cart/QuoteAddressFactory.php | 8 +++----- .../Model/Cart/SetBillingAddressOnCart.php | 2 ++ .../Model/Cart/SetShippingAddressesOnCart.php | 2 ++ .../Customer/SetShippingAddressOnCartTest.php | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php index 9fe8d34435d5d..0fca8a19aa03f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -16,7 +16,6 @@ use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote\Address as QuoteAddress; use Magento\Quote\Model\Quote\AddressFactory as BaseQuoteAddressFactory; -use Magento\Framework\App\ObjectManager; /** * Create QuoteAddress @@ -47,20 +46,19 @@ class QuoteAddressFactory * @param BaseQuoteAddressFactory $quoteAddressFactory * @param GetCustomerAddress $getCustomerAddress * @param AddressHelper $addressHelper - * @param CountryInformationAcquirerInterface|null $countryInformationAcquirer + * @param CountryInformationAcquirerInterface $countryInformationAcquirer */ public function __construct( BaseQuoteAddressFactory $quoteAddressFactory, GetCustomerAddress $getCustomerAddress, AddressHelper $addressHelper, - CountryInformationAcquirerInterface $countryInformationAcquirer = null + CountryInformationAcquirerInterface $countryInformationAcquirer ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->getCustomerAddress = $getCustomerAddress; $this->addressHelper = $addressHelper; $this->countryInformationAcquirer = $countryInformationAcquirer; - $this->countryInformationAcquirer = $countryInformationAcquirer - ?: ObjectManager::getInstance()->get(CountryInformationAcquirerInterface::class); + $this->countryInformationAcquirer = $countryInformationAcquirer; } /** diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index dd2daa6cb24ff..d3e4b13d2023b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -140,6 +140,8 @@ private function validateAddress(Address $shippingAddress) * * @param array $errors * @return string + * + * @todo change implementation in https://github.com/magento/graphql-ce/issues/970. */ private function getAddressErrors(array $errors): string { diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 499f6a6e9e402..b2db19d4ffe45 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -81,6 +81,8 @@ private function validateAddress(Address $shippingAddress) * * @param array $errors * @return string + * + * @todo change implementation in https://github.com/magento/graphql-ce/issues/970. */ private function getAddressErrors(array $errors): string { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index fd21475f12504..2fcbddfad1a6f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -441,6 +441,22 @@ public function dataProviderUpdateWithMissedRequiredParameters(): array }]', '"regionId" is required. Enter and try again.' ], + 'missed_multiple_fields' => [ + 'cart_id: "cart_id_value" + shipping_addresses: [{ + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + country_code: "US" + telephone: "88776655" + } + }]', + '"postcode" is required. Enter and try again. +"regionId" is required. Enter and try again.' + ], ]; } From 59ff70960eab3d2ad6e3599d4c427fce2bd0be93 Mon Sep 17 00:00:00 2001 From: Prabhu Ram <pganapat@adobe.com> Date: Thu, 3 Oct 2019 09:48:58 -0500 Subject: [PATCH 0782/2437] MC-20648: Implement the changes - added changes for storing discounts in db --- .../Quote/Model/Quote/Address/Total.php | 15 ------ .../Model/Quote/Item/Plugin/Discount.php | 23 --------- app/code/Magento/Quote/etc/db_schema.xml | 7 ++- app/code/Magento/Quote/etc/di.xml | 3 -- .../Model/Cart/DiscountAggregator.php | 48 ------------------- .../Model/Resolver/CartItemPrices.php | 6 +-- .../QuoteGraphQl/Model/Resolver/Discounts.php | 20 ++------ .../Model/Plugin/ResourceModel/Discount.php | 47 ++++++++++++++++++ .../SalesRule/Model/Quote/Discount.php | 44 ++++++++--------- .../Model/Quote/Item/Plugin/Discount.php | 43 +++++++++++++++++ app/code/Magento/SalesRule/etc/di.xml | 7 ++- .../SalesRule/etc/extension_attributes.xml | 3 ++ 12 files changed, 129 insertions(+), 137 deletions(-) delete mode 100644 app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php create mode 100644 app/code/Magento/SalesRule/Model/Plugin/ResourceModel/Discount.php create mode 100644 app/code/Magento/SalesRule/Model/Quote/Item/Plugin/Discount.php diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total.php b/app/code/Magento/Quote/Model/Quote/Address/Total.php index 3ed9f7f984334..d8dd0953407d4 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total.php @@ -200,19 +200,4 @@ public function getFullInfo() } return $fullInfo; } - - public function getDiscountBreakdown() { - $fullInfo = $this->getData('discount_breakdown'); - if (is_string($fullInfo)) { - $fullInfo = $this->serializer->unserialize($fullInfo); - } - return $fullInfo; - } - - public function setDiscountBreakdown($discount) { - if (isset($discount)) { - $this->setData('discount_breakdown', $this->serializer->serialize($discount)); - } - return $this; - } } diff --git a/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php b/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php deleted file mode 100644 index 134258c2e09ab..0000000000000 --- a/app/code/Magento/Quote/Model/Quote/Item/Plugin/Discount.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Created by PhpStorm. - * User: pganapat - * Date: 9/25/19 - * Time: 7:42 PM - */ - -namespace Magento\Quote\Model\Quote\Item\Plugin; - -use Magento\Quote\Model\Quote\Item\CartItemPersister; -use Magento\Quote\Api\Data\CartItemInterface; -use Magento\Quote\Api\Data\CartInterface; - -class Discount -{ - - public function beforeSave(CartItemPersister $subject, CartInterface $quote, CartItemInterface $cartItem) { - $extension = $cartItem->getExtensionAttributes(); - $cartItem->setDiscounts(\GuzzleHttp\json_encode($extension->getDiscounts())); - return [$quote, $cartItem]; - } -} \ No newline at end of file diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index cf3ce416e24c5..9a72658b0af6e 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -173,10 +173,9 @@ default="0" comment="Base Grand Total"/> <column xsi:type="text" name="customer_notes" nullable="true" comment="Customer Notes"/> <column xsi:type="text" name="applied_taxes" nullable="true" comment="Applied Taxes"/> - <column xsi:type="varchar" name="discount_description" nullable="true" length="25500" + <column xsi:type="varchar" name="discount_description" nullable="true" length="255" comment="Discount Description"/> - <column xsi:type="text" name="discount_breakdown" nullable="true" length="255" - comment="Discount Breakdown"/> + <column xsi:type="text" name="discounts" nullable="true" comment="Discount split"/> <column xsi:type="decimal" name="shipping_discount_amount" scale="4" precision="20" unsigned="false" nullable="true" comment="Shipping Discount Amount"/> <column xsi:type="decimal" name="base_shipping_discount_amount" scale="4" precision="20" unsigned="false" @@ -236,7 +235,7 @@ <column xsi:type="varchar" name="name" nullable="true" length="255" comment="Name"/> <column xsi:type="text" name="description" nullable="true" comment="Description"/> <column xsi:type="text" name="applied_rule_ids" nullable="true" comment="Applied Rule Ids"/> - <column xsi:type="text" name="discounts" nullable="true" comment="Discounts"/> + <column xsi:type="text" name="discounts" nullable="true" comment="Discount split"/> <column xsi:type="text" name="additional_data" nullable="true" comment="Additional Data"/> <column xsi:type="smallint" name="is_qty_decimal" padding="5" unsigned="true" nullable="true" identity="false" comment="Is Qty Decimal"/> diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index 6060e3e2845a1..cd5e62307fdca 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -101,9 +101,6 @@ <type name="Magento\Catalog\Model\Product\Action"> <plugin name="quoteProductMassChange" type="Magento\Quote\Model\Product\Plugin\MarkQuotesRecollectMassDisabled"/> </type> - <type name="Magento\Quote\Model\Quote\Item\CartItemPersister"> - <plugin name="discountItemPlugin" type="Magento\Quote\Model\Quote\Item\Plugin\Discount"/> - </type> <type name="Magento\Quote\Model\ValidationRules\QuoteValidationComposite"> <arguments> <argument name="validationRules" xsi:type="array"> diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php b/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php deleted file mode 100644 index a620b4b2610cf..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart; - -use Magento\Quote\Model\Quote; - -/** - * Aggregate cart level discounts - * - * @package Magento\QuoteGraphQl\Model\Cart - */ -class DiscountAggregator -{ - /** - * Aggregate Discount per rule - * - * @param Quote $quote - * @return array - */ - public function aggregateDiscountPerRule( - Quote $quote - ) { - $items = $quote->getItems(); - $discountPerRule = []; - foreach ($items as $item) { - $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); - if ($discountBreakdown) { - foreach ($discountBreakdown as $key => $value) { - /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ - $discount = $value['discount']; - $rule = $value['rule']; - if (isset($discountPerRule[$key])) { - $discountPerRule[$key]['discount'] += $discount->getAmount(); - } else { - $discountPerRule[$key]['discount'] = $discount->getAmount(); - } - $discountPerRule[$key]['rule'] = $rule; - } - } - } - return $discountPerRule; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index b66327ac1dbba..56488bf0eaadf 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -94,11 +94,11 @@ private function getDiscountValues($cartItem, $currencyCode) $discount = []; $amount = []; /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */ - $discountData = $value['discount']; + $discountAmount = $value['discount']; /* @var \Magento\SalesRule\Model\Rule $rule */ $rule = $value['rule']; - $discount['label'] = $rule->getStoreLabel($cartItem->getQuote()->getStore()) ?: __('Discount'); - $amount['value'] = $discountData->getAmount(); + $discount['label'] = $rule ?: __('Discount'); + $amount['value'] = $discountAmount; $amount['currency'] = $currencyCode; $discount['amount'] = $amount; $discountValues[] = $discount; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php index 2c3f6d0e69f4a..b00d75a305868 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php @@ -12,27 +12,12 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Model\Quote; -use Magento\QuoteGraphQl\Model\Cart\DiscountAggregator; /** * @inheritdoc */ class Discounts implements ResolverInterface { - /** - * @var DiscountAggregator - */ - private $discountAggregator; - - /** - * @param DiscountAggregator|null $discountAggregator - */ - public function __construct( - DiscountAggregator $discountAggregator - ) { - $this->discountAggregator = $discountAggregator; - } - /** * @inheritdoc */ @@ -55,14 +40,15 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value private function getDiscountValues(Quote $quote) { $discountValues=[]; - $totalDiscounts = $this->discountAggregator->aggregateDiscountPerRule($quote); + $address = $quote->getShippingAddress(); + $totalDiscounts = $address->getExtensionAttributes()->getDiscounts(); if ($totalDiscounts) { foreach ($totalDiscounts as $value) { $discount = []; $amount = []; /* @var \Magento\SalesRule\Model\Rule $rule*/ $rule = $value['rule']; - $discount['label'] = $rule->getStoreLabel($quote->getStore()) ?: __('Discount'); + $discount['label'] = $rule ?: __('Discount'); $amount['value'] = $value['discount']; $amount['currency'] = $quote->getQuoteCurrencyCode(); $discount['amount'] = $amount; diff --git a/app/code/Magento/SalesRule/Model/Plugin/ResourceModel/Discount.php b/app/code/Magento/SalesRule/Model/Plugin/ResourceModel/Discount.php new file mode 100644 index 0000000000000..e0e88d9534f18 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Plugin/ResourceModel/Discount.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Model\Plugin\ResourceModel; + +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Plugin for persisting discounts along with Quote Address + */ +class Discount +{ + /** + * @var Json + */ + private $json; + + /** + * @param Json $json + */ + public function __construct(Json $json) + { + $this->json = $json; + } + + /** + * Plugin method for persisting data from extension attribute + * + * @param \Magento\Quote\Model\ResourceModel\Quote $subject + * @param \Magento\Framework\Model\AbstractModel $object + * @return array + */ + public function beforeSave( + \Magento\Quote\Model\ResourceModel\Quote $subject, + \Magento\Framework\Model\AbstractModel $object + ) { + foreach ($object->getAllAddresses() as $address) { + $discounts = $address->getExtensionAttributes()->getDiscounts(); + if ($discounts) { + $address->setDiscounts($this->json->serialize($discounts)); + } + } + return [$object]; + } +} diff --git a/app/code/Magento/SalesRule/Model/Quote/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Discount.php index 47aaee2bd8fa7..efd2d5b5a6802 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Discount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Discount.php @@ -127,14 +127,13 @@ public function collect( $this->calculator->process($item); $this->aggregateItemDiscount($item, $total); } + $this->aggregateDiscountPerRule($item, $address); } $this->calculator->prepareDescription($address); $total->setDiscountDescription($address->getDiscountDescription()); - $total->setDiscountBreakdown($this->aggregateDiscountPerRule($quote)); $total->setSubtotalWithDiscount($total->getSubtotal() + $total->getDiscountAmount()); $total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $total->getBaseDiscountAmount()); - $address->setDiscountAmount($total->getDiscountAmount()); $address->setBaseDiscountAmount($total->getBaseDiscountAmount()); @@ -221,32 +220,31 @@ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Qu } /** - * @param \Magento\Quote\Model\Quote $quote - * @return array + * Aggregates discount per rule + * + * @param \Magento\Quote\Api\Data\CartItemInterface $item + * @param \Magento\Quote\Api\Data\AddressInterface $address + * @return void */ private function aggregateDiscountPerRule( - \Magento\Quote\Model\Quote $quote + \Magento\Quote\Api\Data\CartItemInterface $item, + \Magento\Quote\Api\Data\AddressInterface $address ) { - $items = $quote->getItems(); - $discountPerRule = []; - if ($items) { - foreach ($items as $item) { - $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); - if ($discountBreakdown) { - foreach ($discountBreakdown as $key => $value) { - /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ - $discount = $value['discount']; - $ruleLabel = $value['rule']; - if (isset($discountPerRule[$key])) { - $discountPerRule[$key]['discount'] += $discount; - } else { - $discountPerRule[$key]['discount'] = $discount; - } - $discountPerRule[$key]['rule'] = $ruleLabel; - } + $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); + $discountPerRule = $address->getExtensionAttributes()->getDiscounts(); + if ($discountBreakdown) { + foreach ($discountBreakdown as $key => $value) { + /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ + $discount = $value['discount']; + $ruleLabel = $value['rule']; + if (isset($discountPerRule[$key])) { + $discountPerRule[$key]['discount'] += $discount; + } else { + $discountPerRule[$key]['discount'] = $discount; } + $discountPerRule[$key]['rule'] = $ruleLabel; } } - return $discountPerRule; + $address->getExtensionAttributes()->setDiscounts($discountPerRule); } } diff --git a/app/code/Magento/SalesRule/Model/Quote/Item/Plugin/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Item/Plugin/Discount.php new file mode 100644 index 0000000000000..143fa073b37ec --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Quote/Item/Plugin/Discount.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Model\Quote\Item\Plugin; + +use Magento\Quote\Model\Quote\Item\CartItemPersister; +use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Plugin for persisting discounts on Cart Item + */ +class Discount +{ + + private $json; + + /** + * @param Json $json + */ + public function __construct(Json $json) + { + $this->json = $json; + } + + /** + * Plugin method for persisting data from extension attributes + * + * @param CartItemPersister $subject + * @param CartInterface $quote + * @param CartItemInterface $cartItem + * @return array + */ + public function beforeSave(CartItemPersister $subject, CartInterface $quote, CartItemInterface $cartItem) + { + $cartExtension = $cartItem->getExtensionAttributes(); + $cartItem->setDiscounts($this->json->serialize($cartExtension->getDiscounts())); + return [$quote, $cartItem]; + } +} diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml index c1d22a04771ab..a3bc8b7e57e8f 100644 --- a/app/code/Magento/SalesRule/etc/di.xml +++ b/app/code/Magento/SalesRule/etc/di.xml @@ -178,7 +178,12 @@ </argument> </arguments> </type> - + <type name="Magento\Quote\Model\Quote\Item\CartItemPersister"> + <plugin name="discount_item_plugin" type="Magento\SalesRule\Model\Quote\Item\Plugin\Discount"/> + </type> + <type name="Magento\Quote\Model\ResourceModel\Quote"> + <plugin name="discount_cart_plugin" type="Magento\SalesRule\Model\Plugin\ResourceModel\Discount"/> + </type> <type name="Magento\Quote\Model\Cart\CartTotalRepository"> <plugin name="coupon_label_plugin" type="Magento\SalesRule\Plugin\CartTotalRepository" /> </type> diff --git a/app/code/Magento/SalesRule/etc/extension_attributes.xml b/app/code/Magento/SalesRule/etc/extension_attributes.xml index 202ced4204f73..b73afcd09f060 100644 --- a/app/code/Magento/SalesRule/etc/extension_attributes.xml +++ b/app/code/Magento/SalesRule/etc/extension_attributes.xml @@ -9,4 +9,7 @@ <extension_attributes for="Magento\Quote\Api\Data\CartItemInterface"> <attribute code="discounts" type="string" /> </extension_attributes> + <extension_attributes for="Magento\Quote\Api\Data\AddressInterface"> + <attribute code="discounts" type="string" /> + </extension_attributes> </config> \ No newline at end of file From 46e21b2a4a6b3ed7aea2d4d1f4e4e471a7471ed2 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Wed, 2 Oct 2019 19:35:58 -0500 Subject: [PATCH 0783/2437] MC-17518: Cart Rest API not showing all shipping assignments for multi-shipping --- .../DisableMultishippingMode.php} | 4 +- ...n.php => MultishippingQuoteRepository.php} | 4 +- ...tPlugin.php => ResetShippingAssigment.php} | 2 +- .../DisableMultishippingModeTest.php} | 10 +- app/code/Magento/Multishipping/etc/di.xml | 2 +- .../Magento/Multishipping/etc/frontend/di.xml | 8 +- .../Multishipping/Api/CartRepositoryTest.php | 139 ++++++++++++++++++ .../Magento/Quote/Api/CartRepositoryTest.php | 46 ------ 8 files changed, 154 insertions(+), 61 deletions(-) rename app/code/Magento/Multishipping/{Controller/Checkout/Plugin.php => Plugin/DisableMultishippingMode.php} (93%) rename app/code/Magento/Multishipping/Plugin/{MultishippingQuoteRepositoryPlugin.php => MultishippingQuoteRepository.php} (98%) rename app/code/Magento/Multishipping/Plugin/{ResetShippingAssigmentPlugin.php => ResetShippingAssigment.php} (97%) rename app/code/Magento/Multishipping/Test/Unit/{Controller/Checkout/PluginTest.php => Plugin/DisableMultishippingModeTest.php} (91%) create mode 100644 dev/tests/api-functional/testsuite/Magento/Multishipping/Api/CartRepositoryTest.php diff --git a/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php b/app/code/Magento/Multishipping/Plugin/DisableMultishippingMode.php similarity index 93% rename from app/code/Magento/Multishipping/Controller/Checkout/Plugin.php rename to app/code/Magento/Multishipping/Plugin/DisableMultishippingMode.php index f60feb2b834b9..fff2346d76240 100644 --- a/app/code/Magento/Multishipping/Controller/Checkout/Plugin.php +++ b/app/code/Magento/Multishipping/Plugin/DisableMultishippingMode.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Multishipping\Controller\Checkout; +namespace Magento\Multishipping\Plugin; use Magento\Checkout\Model\Cart; use Magento\Framework\App\Action\Action; @@ -13,7 +13,7 @@ /** * Turns Off Multishipping mode for Quote. */ -class Plugin +class DisableMultishippingMode { /** * @var Cart diff --git a/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php b/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepository.php similarity index 98% rename from app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php rename to app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepository.php index db972cf9bd7c7..af19e4bc91f51 100644 --- a/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepositoryPlugin.php +++ b/app/code/Magento/Multishipping/Plugin/MultishippingQuoteRepository.php @@ -15,9 +15,9 @@ use Magento\Quote\Model\ShippingAssignmentFactory; /** - * Plugin for multishipping quote processing in WebAPI. + * Plugin for multishipping quote processing. */ -class MultishippingQuoteRepositoryPlugin +class MultishippingQuoteRepository { /** * @var ShippingAssignmentFactory diff --git a/app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php b/app/code/Magento/Multishipping/Plugin/ResetShippingAssigment.php similarity index 97% rename from app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php rename to app/code/Magento/Multishipping/Plugin/ResetShippingAssigment.php index 376dbf723b88f..deac19e23a23a 100644 --- a/app/code/Magento/Multishipping/Plugin/ResetShippingAssigmentPlugin.php +++ b/app/code/Magento/Multishipping/Plugin/ResetShippingAssigment.php @@ -13,7 +13,7 @@ /** * Resets quote shipping assignments when item is removed from multishipping quote. */ -class ResetShippingAssigmentPlugin +class ResetShippingAssigment { /** * @var ShippingAssignmentProcessor diff --git a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php similarity index 91% rename from app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php rename to app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php index 5397317ca2997..96aa364c38730 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Controller/Checkout/PluginTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php @@ -10,15 +10,15 @@ use Magento\Checkout\Controller\Index\Index; use Magento\Checkout\Model\Cart; -use Magento\Multishipping\Controller\Checkout\Plugin; +use Magento\Multishipping\Plugin\DisableMultishippingMode; use Magento\Quote\Api\Data\CartExtensionInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\Quote\Model\Quote; /** - * Class PluginTest + * Class DisableMultishippingModeTest */ -class PluginTest extends \PHPUnit\Framework\TestCase +class DisableMultishippingModeTest extends \PHPUnit\Framework\TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject @@ -31,7 +31,7 @@ class PluginTest extends \PHPUnit\Framework\TestCase private $quoteMock; /** - * @var Plugin + * @var DisableMultishippingMode */ private $object; @@ -48,7 +48,7 @@ protected function setUp() $this->cartMock->expects($this->once()) ->method('getQuote') ->will($this->returnValue($this->quoteMock)); - $this->object = new \Magento\Multishipping\Controller\Checkout\Plugin($this->cartMock); + $this->object = new DisableMultishippingMode($this->cartMock); } /** diff --git a/app/code/Magento/Multishipping/etc/di.xml b/app/code/Magento/Multishipping/etc/di.xml index 993376da83783..ad0d341d6b2e9 100644 --- a/app/code/Magento/Multishipping/etc/di.xml +++ b/app/code/Magento/Multishipping/etc/di.xml @@ -10,6 +10,6 @@ <plugin name="multishipping_shipping_addresses" type="Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin" /> </type> <type name="Magento\Quote\Model\QuoteRepository"> - <plugin name="multishipping_quote_repository" type="Magento\Multishipping\Plugin\MultishippingQuoteRepositoryPlugin" /> + <plugin name="multishipping_quote_repository" type="Magento\Multishipping\Plugin\MultishippingQuoteRepository" /> </type> </config> diff --git a/app/code/Magento/Multishipping/etc/frontend/di.xml b/app/code/Magento/Multishipping/etc/frontend/di.xml index 3d85f4c8a447e..481b95280a4a4 100644 --- a/app/code/Magento/Multishipping/etc/frontend/di.xml +++ b/app/code/Magento/Multishipping/etc/frontend/di.xml @@ -31,13 +31,13 @@ </arguments> </type> <type name="Magento\Checkout\Controller\Cart\Add"> - <plugin name="multishipping_disabler" type="Magento\Multishipping\Controller\Checkout\Plugin" sortOrder="50" /> + <plugin name="multishipping_disabler" type="Magento\Multishipping\Plugin\DisableMultishippingMode" sortOrder="50" /> </type> <type name="Magento\Checkout\Controller\Cart\UpdatePost"> - <plugin name="multishipping_disabler" type="Magento\Multishipping\Controller\Checkout\Plugin" sortOrder="50" /> + <plugin name="multishipping_disabler" type="Magento\Multishipping\Plugin\DisableMultishippingMode" sortOrder="50" /> </type> <type name="Magento\Checkout\Controller\Index\Index"> - <plugin name="multishipping_disabler" type="Magento\Multishipping\Controller\Checkout\Plugin" sortOrder="50" /> + <plugin name="multishipping_disabler" type="Magento\Multishipping\Plugin\DisableMultishippingMode" sortOrder="50" /> </type> <type name="Magento\Checkout\Model\Cart"> <plugin name="multishipping_session_mapper" type="Magento\Multishipping\Model\Checkout\Type\Multishipping\Plugin" sortOrder="50" /> @@ -46,6 +46,6 @@ <plugin name="multishipping_clear_addresses" type="Magento\Multishipping\Model\Cart\Controller\CartPlugin" sortOrder="50" /> </type> <type name="Magento\Quote\Model\Quote"> - <plugin name="multishipping_reset_shipping_assigment" type="Magento\Multishipping\Plugin\ResetShippingAssigmentPlugin"/> + <plugin name="multishipping_reset_shipping_assigment" type="Magento\Multishipping\Plugin\ResetShippingAssigment"/> </type> </config> diff --git a/dev/tests/api-functional/testsuite/Magento/Multishipping/Api/CartRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Multishipping/Api/CartRepositoryTest.php new file mode 100644 index 0000000000000..46844438fdd97 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Multishipping/Api/CartRepositoryTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Multishipping\Api; + +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SortOrderBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Tests web-api for multishipping quote. + */ +class CartRepositoryTest extends WebapiAbstract +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var SortOrderBuilder + */ + private $sortOrderBuilder; + + /** + * @var FilterBuilder + */ + private $filterBuilder; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->filterBuilder = $this->objectManager->create(FilterBuilder::class); + $this->sortOrderBuilder = $this->objectManager->create(SortOrderBuilder::class); + $this->searchCriteriaBuilder = $this->objectManager->create(SearchCriteriaBuilder::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + try { + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $cart = $this->getCart('multishipping_quote_id'); + $quoteRepository->delete($cart); + } catch (\InvalidArgumentException $e) { + // Do nothing if cart fixture was not used + } + parent::tearDown(); + } + + /** + * Tests that multishipping quote contains all addresses in shipping assignments. + * + * @magentoApiDataFixture Magento/Multishipping/Fixtures/quote_with_split_items.php + */ + public function testGetMultishippingCart() + { + $cart = $this->getCart('multishipping_quote_id'); + $cartId = $cart->getId(); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/carts/' . $cartId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => 'quoteCartRepositoryV1', + 'serviceVersion' => 'V1', + 'operation' => 'quoteCartRepositoryV1Get', + ], + ]; + + $requestData = ['cartId' => $cartId]; + $cartData = $this->_webApiCall($serviceInfo, $requestData); + + $shippingAssignments = $cart->getExtensionAttributes()->getShippingAssignments(); + foreach ($shippingAssignments as $key => $shippingAssignment) { + $address = $shippingAssignment->getShipping()->getAddress(); + $cartItem = $shippingAssignment->getItems()[0]; + $this->assertEquals( + $address->getId(), + $cartData['extension_attributes']['shipping_assignments'][$key]['shipping']['address']['id'] + ); + $this->assertEquals( + $cartItem->getSku(), + $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['sku'] + ); + $this->assertEquals( + $cartItem->getQty(), + $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['qty'] + ); + } + } + + /** + * Retrieve quote by given reserved order ID + * + * @param string $reservedOrderId + * @return Quote + * @throws \InvalidArgumentException + */ + private function getCart(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + if (empty($items)) { + throw new \InvalidArgumentException('There is no quote with provided reserved order ID.'); + } + + return array_pop($items); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php index 7ffc4311e40ed..5a894758dc9ed 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartRepositoryTest.php @@ -66,8 +66,6 @@ protected function tearDown() $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); $cart = $this->getCart('test01'); $quoteRepository->delete($cart); - $cart = $this->getCart('multishipping_quote_id'); - $quoteRepository->delete($cart); } catch (\InvalidArgumentException $e) { // Do nothing if cart fixture was not used } @@ -145,50 +143,6 @@ public function testGetCart() $this->assertEquals($cart->getStoreToQuoteRate(), $cartData['currency']['store_to_quote_rate']); } - /** - * Tests that multishipping quote contains all addresses in shipping assignments. - * - * @magentoApiDataFixture Magento/Multishipping/Fixtures/quote_with_split_items.php - */ - public function testGetMultishippingCart() - { - $cart = $this->getCart('multishipping_quote_id'); - $cartId = $cart->getId(); - - $serviceInfo = [ - 'rest' => [ - 'resourcePath' => '/V1/carts/' . $cartId, - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, - ], - 'soap' => [ - 'service' => 'quoteCartRepositoryV1', - 'serviceVersion' => 'V1', - 'operation' => 'quoteCartRepositoryV1Get', - ], - ]; - - $requestData = ['cartId' => $cartId]; - $cartData = $this->_webApiCall($serviceInfo, $requestData); - - $shippingAssignments = $cart->getExtensionAttributes()->getShippingAssignments(); - foreach ($shippingAssignments as $key => $shippingAssignment) { - $address = $shippingAssignment->getShipping()->getAddress(); - $cartItem = $shippingAssignment->getItems()[0]; - $this->assertEquals( - $address->getId(), - $cartData['extension_attributes']['shipping_assignments'][$key]['shipping']['address']['id'] - ); - $this->assertEquals( - $cartItem->getSku(), - $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['sku'] - ); - $this->assertEquals( - $cartItem->getQty(), - $cartData['extension_attributes']['shipping_assignments'][$key]['items'][0]['qty'] - ); - } - } - /** * Tests exception when cartId is not provided. * From db1771d1cff37f66bb295f0add9419e99d84f009 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 3 Oct 2019 10:29:35 -0500 Subject: [PATCH 0784/2437] MC-20534: Migrate remaining failed MFTF tests to SearchEngineMysqlSuite --- .../Catalog/Test/Mftf/Data/ProductData.xml | 14 +++++ ...CustomOptionsSuiteAndImportOptionsTest.xml | 2 + .../Test/AdminSimpleProductImagesTest.xml | 2 + ...eForSimpleProductWithCustomOptionsTest.xml | 52 +++---------------- 4 files changed, 26 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 1335a2c5046ce..aad43bb7011c1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -559,6 +559,20 @@ <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> </entity> + <entity name="ApiVirtualProductWithDescriptionAndUnderscoredSku" type="product"> + <data key="sku" unique="suffix">api_virtual_product</data> + <data key="type_id">virtual</data> + <data key="attribute_set_id">4</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Api Virtual Product</data> + <data key="price">123.00</data> + <data key="urlKey" unique="suffix">api-virtual-product</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductDescription</requiredEntity> + <requiredEntity type="custom_attribute_array">ApiProductShortDescription</requiredEntity> + </entity> <entity name="SimpleProductWithNewFromDate" type="product"> <data key="sku" unique="suffix">SimpleProduct</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index 58737dd509743..23f772a395a7d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -117,6 +117,8 @@ <!-- Verify we see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSuccessMessage"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Verify customer see created virtual product with custom options suite and import options(from above step) on storefront page and is searchable by sku --> <amOnPage url="{{StorefrontProductPage.url(virtualProductCustomImportOptions.urlKey)}}" stepKey="goToProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index 1cd0e15780c11..4875197380f32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -144,6 +144,8 @@ <!-- Save the second product --> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Go to the admin grid and see the uploaded image --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml index 5b7e722c92a02..a251ee1e235d0 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml @@ -77,63 +77,27 @@ <actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - + <!-- Navigate to category on store front --> <amOnPage url="{{StorefrontProductPage.url($createCategory.name$)}}" stepKey="goToCategoryPage"/> - - <!-- Check product 1 name on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1Name"> - <argument name="productInfo" value="$createProduct1.name$"/> - <argument name="productNumber" value="3"/> - </actionGroup> <!-- Check product 1 price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1Price"> - <argument name="productInfo" value="$51.10"/> - <argument name="productNumber" value="3"/> - </actionGroup> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct1.name$)}}" userInput="$51.10" stepKey="storefrontProduct1Price"/> <!-- Check product 1 regular price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1RegularPrice"> - <argument name="productInfo" value="$56.78"/> - <argument name="productNumber" value="3"/> - </actionGroup> - - <!-- Check product 2 name on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct2Name"> - <argument name="productInfo" value="$createProduct2.name$"/> - <argument name="productNumber" value="2"/> - </actionGroup> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct1.name$)}}" userInput="$56.78" stepKey="storefrontProduct1RegularPrice"/> <!-- Check product 2 price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct2Price"> - <argument name="productInfo" value="$51.10"/> - <argument name="productNumber" value="2"/> - </actionGroup> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct2.name$)}}" userInput="$51.10" stepKey="storefrontProduct2Price"/> - <!-- Check product 2 price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct2RegularPrice"> - <argument name="productInfo" value="$56.78"/> - <argument name="productNumber" value="2"/> - </actionGroup> - - <!-- Check product 3 name on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct3Name"> - <argument name="productInfo" value="$createProduct3.name$"/> - <argument name="productNumber" value="1"/> - </actionGroup> + <!-- Check product 2 regular price on store front category page --> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct2.name$)}}" userInput="$56.78" stepKey="storefrontProduct2RegularPrice"/> <!-- Check product 3 price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct3Price"> - <argument name="productInfo" value="$51.10"/> - <argument name="productNumber" value="1"/> - </actionGroup> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct3.name$)}}" userInput="$51.10" stepKey="storefrontProduct3Price"/> <!-- Check product 3 regular price on store front category page --> - <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct3RegularPrice"> - <argument name="productInfo" value="$56.78"/> - <argument name="productNumber" value="1"/> - </actionGroup> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByName($createProduct3.name$)}}" userInput="$56.78" stepKey="storefrontProduct3RegularPrice"/> <!-- Navigate to product 1 on store front --> <amOnPage url="{{StorefrontProductPage.url($createProduct1.name$)}}" stepKey="goToProductPage1"/> From 287e906eaf3dd0b93f3f39e43a0cd79ef98e17ea Mon Sep 17 00:00:00 2001 From: Tomash Khamlai <tomash.khamlai@gmail.com> Date: Thu, 3 Oct 2019 19:40:51 +0300 Subject: [PATCH 0785/2437] Extend test coverage for BraintreeGraphQ Cover exception: - `Required parameter "is_active_payment_token_enabler" for "braintree" is missing.` Cover: - Case when Magento\Braintree\Gateway\Request\PaymentDataBuilder receives MERCHANT_ACCOUNT_ID (deprecated) Signed-off-by: Tomash Khamlai <tomash.khamlai@gmail.com> --- .../CreateBraintreeClientTokenTest.php | 1 + .../Customer/SetPaymentMethodTest.php | 58 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/CreateBraintreeClientTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/CreateBraintreeClientTokenTest.php index 1564d00fa5996..7d69c49ae6aa3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/CreateBraintreeClientTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/CreateBraintreeClientTokenTest.php @@ -20,6 +20,7 @@ class CreateBraintreeClientTokenTest extends GraphQlAbstract * @magentoConfigFixture default_store payment/braintree/active 1 * @magentoConfigFixture default_store payment/braintree/environment sandbox * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id + * @magentoConfigFixture default_store payment/braintree/merchant_account_id def_merchant_id * @magentoConfigFixture default_store payment/braintree/public_key def_public_key * @magentoConfigFixture default_store payment/braintree/private_key def_private_key */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php index 84a639af30b0e..b48e9064dae72 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Braintree\Customer; use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Framework\Exception\AuthenticationException; use Magento\Framework\Registry; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Integration\Api\CustomerTokenServiceInterface; @@ -261,6 +262,34 @@ public function testSetPaymentMethodInvalidMethodInput(string $methodCode) $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); } + /** + * @magentoConfigFixture default_store carriers/flatrate/active 1 + * @magentoConfigFixture default_store payment/braintree/active 1 + * @magentoConfigFixture default_store payment/braintree_cc_vault/active 1 + * @magentoConfigFixture default_store payment/braintree/environment sandbox + * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id + * @magentoConfigFixture default_store payment/braintree/public_key def_public_key + * @magentoConfigFixture default_store payment/braintree/private_key def_private_key + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + * @dataProvider dataProviderTestSetPaymentMethodInvalidInput + * @expectedException \Exception + */ + public function testSetPaymentMethodWithoutRequiredPaymentMethodInput() + { + $reservedOrderId = 'test_quote'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + + $setPaymentQuery = $this->getSetPaymentBraintreeQueryInvalidPaymentMethodInput($maskedQuoteId); + $this->expectExceptionMessage("for \"braintree\" is missing."); + $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); + } + public function dataProviderTestSetPaymentMethodInvalidInput(): array { return [ @@ -371,6 +400,33 @@ private function getSetPaymentBraintreeQueryInvalidInput(string $maskedQuoteId, QUERY; } + /** + * @param string $maskedQuoteId + * @return string + */ + private function getSetPaymentBraintreeQueryInvalidPaymentMethodInput(string $maskedQuoteId): string + { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input:{ + cart_id:"{$maskedQuoteId}" + payment_method:{ + code:"braintree" + braintree:{ + payment_method_nonce:"fake-valid-nonce" + } + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + } + /** * @param string $maskedQuoteId * @param string $methodCode @@ -437,7 +493,7 @@ private function getPaymentTokenQuery(): string * @param string $username * @param string $password * @return array - * @throws \Magento\Framework\Exception\AuthenticationException + * @throws AuthenticationException */ private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array { From ecd52b945c28bad14267c31f14e1284d55b5ce6b Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Thu, 3 Oct 2019 12:09:09 -0500 Subject: [PATCH 0786/2437] MC-15986: Category Filtering - added resolver changes for category list query --- .../Model/Category/CategoryFilter.php | 29 ++++++------------ .../Model/Resolver/CategoryList.php | 30 +++++-------------- .../CatalogGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 574f5dd2715ce..5058bb2e07c66 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -5,10 +5,9 @@ */ declare(strict_types=1); -namespace Magento\CatalogGraphQl\Model\Category;; +namespace Magento\CatalogGraphQl\Model\Category; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; -use Magento\Catalog\Api\CategoryRepositoryInterface; /** * Category filter allows to filter collection using 'id, url_key, name' from search criteria. @@ -21,19 +20,11 @@ class CategoryFilter private $collectionFactory; /** - * @var CategoryRepositoryInterface - */ - private $categoryRepository; - - /** - * @param CategoryRepositoryInterface $categoryRepository; * @param CollectionFactory $collectionFactory */ public function __construct( - CategoryRepositoryInterface $categoryRepository, CollectionFactory $collectionFactory ) { - $this->categoryRepository = $categoryRepository; $this->collectionFactory = $collectionFactory; } @@ -41,15 +32,14 @@ public function __construct( * Filter for filtering the requested categories id's based on url_key, ids, name in the result. * * @param array $args - * - * @return array|int + * @return array */ - public function applyFilters(array $args) + public function applyFilters(array $args): array { $categoryCollection = $this->collectionFactory->create(); - foreach($args['filters'] as $field => $cond){ - foreach($cond as $condType => $value){ - if($field === 'ids'){ + foreach ($args['filters'] as $field => $cond) { + foreach ($cond as $condType => $value) { + if ($field === 'ids') { $categoryCollection->addIdFilter($value); } else { $categoryCollection->addAttributeToFilter($field, [$condType => $value]); @@ -57,10 +47,9 @@ public function applyFilters(array $args) } } $categoryIds = []; - $categoriesData = $categoryCollection->getData(); - foreach ($categoriesData as $categoryData){ - $categoryIds[] = (int)$categoryData['entity_id']; + foreach ($categoryCollection as $category) { + $categoryIds[] = (int)$category->getId(); } - return $categoryIds; + return $categoryIds; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 932b88c68f1f5..2511e07dc4f89 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -14,20 +14,14 @@ use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; use Magento\CatalogGraphQl\Model\Category\CategoryFilter; /** - * Category tree resolver, used for GraphQL category data request processing. + * Category List resolver, used for GraphQL category data request processing. */ class CategoryList implements ResolverInterface { - /** - * Name of type in GraphQL - */ - const CATEGORY_INTERFACE = 'CategoryInterface'; - /** * @var CategoryTree */ @@ -76,30 +70,22 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } if (!isset($args['filters'])) { - throw new GraphQlInputException( - __( "'filters' input argument is required.") - ); + $rootCategoryIds = [(int)$context->getExtensionAttributes()->getStore()->getRootCategoryId()]; + } else { + $rootCategoryIds = $this->categoryFilter->applyFilters($args); } - $rootCategoryIds = $this->categoryFilter->applyFilters($args); $result = []; - $categoriesTreeData = []; - foreach ($rootCategoryIds as $rootCategoryId) { if ($rootCategoryId !== Category::TREE_ROOT_ID) { $this->checkCategoryIsActive->execute($rootCategoryId); } - $categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId); - - if (empty($categoriesTree) || ($categoriesTree->count() == 0)) { + $categoryTree = $this->categoryTree->getTree($info, $rootCategoryId); + if (empty($categoryTree)) { throw new GraphQlNoSuchEntityException(__('Category doesn\'t exist')); } - $categoriesTreeData[] = $categoriesTree; - } - - foreach ($categoriesTreeData as $treeData ) { - $result[] = $this->extractDataFromCategoryTree->execute($treeData); + $result[] = current($this->extractDataFromCategoryTree->execute($categoryTree)); } - return current($result); + return $result; } } diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 0cc3901a1f076..9c729a175ad06 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -129,7 +129,7 @@ type CustomizableAreaValue @doc(description: "CustomizableAreaValue defines the max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option.") } -type CategoryTree implements CategoryInterface @doc(description: "An array containing the filtered categories and their subcategories") { +type CategoryTree implements CategoryInterface @doc(description: "Category Tree implementation.") { children: [CategoryTree] @doc(description: "Child categories tree.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") } From d015409302077afc9943449b743a398714e62958 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Thu, 3 Oct 2019 13:39:02 -0500 Subject: [PATCH 0787/2437] MC-17518: Cart Rest API not showing all shipping assignments for multi-shipping -Fix namespace --- .../Test/Unit/Plugin/DisableMultishippingModeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php index 96aa364c38730..02ae1a70ce801 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Plugin/DisableMultishippingModeTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Magento\Multishipping\Test\Unit\Controller\Checkout; +namespace Magento\Multishipping\Test\Unit\Plugin; use Magento\Checkout\Controller\Index\Index; use Magento\Checkout\Model\Cart; From be5455a58240b57e4dc0c44a5cea2bb5a2e01c57 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Thu, 3 Oct 2019 13:51:17 -0500 Subject: [PATCH 0788/2437] MC-21481: TransportBuilder doesn't add "to" email-addresses, if given in array --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 4a8d6572faaf8..f2f61cfd7ef1b 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -427,6 +427,8 @@ private function addAddressByType(string $addressType, $email, ?string $name = n $this->messageData[$addressType], $convertedAddressArray ); + } else { + $this->messageData[$addressType] = $convertedAddressArray; } } } From 81f48ac946d0ef0853aed5abbd016f9f0574cacf Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 3 Oct 2019 14:02:02 -0500 Subject: [PATCH 0789/2437] MC-18685: Remove custom layout updates from admin --- .../Cms/Controller/Adminhtml/Page/Save.php | 4 -- app/code/Magento/Cms/Model/Page.php | 37 +++++++++++++------ .../Controller/Adminhtml/Page/SaveTest.php | 2 +- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 8ad33f90b7c88..9d195e5999956 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -11,8 +11,6 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; -use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; /** * Save CMS page action. @@ -67,8 +65,6 @@ public function __construct( $this->pageFactory = $pageFactory ?: ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); $this->pageRepository = $pageRepository ?: ObjectManager::getInstance()->get(\Magento\Cms\Api\PageRepositoryInterface::class); - $this->customLayoutRepository = $customLayoutRepository - ?? ObjectManager::getInstance()->get(CustomLayoutRepositoryInterface::class); parent::__construct($context); } diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index 66aef2a6371b7..28d013f45f1fa 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -65,8 +65,11 @@ class Page extends AbstractModel implements PageInterface, IdentityInterface private $customLayoutRepository; /** - * @inheritDoc - * + * @param \Magento\Framework\Model\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param array $data * @param CustomLayoutRepository|null $customLayoutRepository */ public function __construct( @@ -561,18 +564,15 @@ public function setIsActive($isActive) } /** - * @inheritdoc - * @since 101.0.0 + * Validate identifier before saving the entity. + * + * @return void + * @throws LocalizedException */ - public function beforeSave() + private function validateNewIdentifier(): void { $originalIdentifier = $this->getOrigData('identifier'); $currentIdentifier = $this->getIdentifier(); - - if ($this->hasDataChanges()) { - $this->setUpdateTime(null); - } - if ($this->getId() && $originalIdentifier !== $currentIdentifier) { switch ($originalIdentifier) { case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_ROUTE_PAGE): @@ -580,13 +580,28 @@ public function beforeSave() __('This identifier is reserved for "CMS No Route Page" in configuration.') ); case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_HOME_PAGE): - throw new LocalizedException(__('This identifier is reserved for "CMS Home Page" in configuration.')); + throw new LocalizedException( + __('This identifier is reserved for "CMS Home Page" in configuration.') + ); case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_COOKIES_PAGE): throw new LocalizedException( __('This identifier is reserved for "CMS No Cookies Page" in configuration.') ); } } + } + + /** + * @inheritdoc + * @since 101.0.0 + */ + public function beforeSave() + { + if ($this->hasDataChanges()) { + $this->setUpdateTime(null); + } + + $this->validateNewIdentifier(); //Removing deprecated custom layout update if a new value is provided $layoutUpdate = $this->getData('layout_update_selected'); diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php index 15ba72154643c..b5ae9fb55c2bb 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php @@ -297,7 +297,7 @@ public function testSaveActionThrowsException() ->method('set') ->with( 'cms_page', - ['page_id' => $this->pageId, 'custom_layout_update_xml' => null, 'layout_update_xml' => null] + ['page_id' => $this->pageId] ); $this->resultRedirect->expects($this->atLeastOnce()) From 9948404e92f926ea4948d66104a05f8884acb417 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 3 Oct 2019 15:36:28 -0500 Subject: [PATCH 0790/2437] MC-18685: Remove custom layout updates from admin --- .../CustomLayout/CustomLayoutRepository.php | 2 +- .../Controller/Adminhtml/PageDesignTest.php | 29 +++++++++++++++---- .../Model/Page/CustomLayoutManagerTest.php | 10 ++++++- .../Cms/Model/Page/DataProviderTest.php | 4 +-- .../Cms/_files/pages_with_layout_xml.php | 19 ++++++++---- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php index ce50bbe7c7476..cf0db6eb27cb8 100644 --- a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php @@ -136,7 +136,7 @@ public function save(CustomLayoutSelectedInterface $layout): void public function validateLayoutSelectedFor(PageModel $page): void { $layoutFile = $page->getData('layout_update_selected'); - if ($layoutFile && !$this->isLayoutValidFor($page, $layoutFile)) { + if ($layoutFile && (!$page->getId() || !$this->isLayoutValidFor($page, $layoutFile))) { throw new LocalizedException(__('Invalid Custom Layout Update selected')); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php index ab0a5aa72f35e..8bc7a89280559 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php @@ -56,6 +56,11 @@ class PageDesignTest extends AbstractBackendController */ private $scopeConfig; + /** + * @var string[] + */ + private $pagesToDelete = []; + /** * @inheritDoc */ @@ -68,10 +73,24 @@ protected function setUp() $this->scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class); } + /** + * @inheritDoc + */ + protected function tearDown() + { + parent::tearDown(); + + foreach ($this->pagesToDelete as $identifier) { + $page = $this->pageRetriever->execute($identifier); + $page->delete(); + } + $this->pagesToDelete = []; + } + /** * Check whether additional authorization is required for the design fields. * - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @return void */ public function testSaveDesign(): void @@ -79,7 +98,7 @@ public function testSaveDesign(): void //Expected list of sessions messages collected throughout the controller calls. $sessionMessages = ['You are not allowed to change CMS pages design settings']; //Test page data. - $id = 'test-page'; + $id = 'test-page' .rand(1111, 9999); $requestData = [ PageInterface::IDENTIFIER => $id, PageInterface::TITLE => 'Page title', @@ -130,13 +149,13 @@ public function testSaveDesign(): void /** * Check that default design values are accepted without the permissions. * - * @magentoDbIsolation enabled + * @magentoDbIsolation disabled * @return void */ public function testSaveDesignWithDefaults(): void { //Test page data. - $id = 'test-page'; + $id = 'test-page' .rand(1111, 9999); $defaultLayout = $this->scopeConfig->getValue('web/default_layouts/default_cms_layout'); $requestData = [ PageInterface::IDENTIFIER => $id, @@ -192,7 +211,7 @@ public function testSaveLayoutXml(): void PageInterface::TITLE => 'Page title', PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), PageInterface::LAYOUT_UPDATE_XML => $page->getLayoutUpdateXml(), - 'layout_update_selected' => '' + 'layout_update_selected' => '_no_update_' ]; $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue($requestData); diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php index 966afa0febc1c..e741b95ff4371 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -42,6 +42,11 @@ class CustomLayoutManagerTest extends TestCase */ private $resultFactory; + /** + * @var IdentityMap + */ + private $identityMap; + /** * @inheritDoc */ @@ -68,6 +73,7 @@ protected function setUp() ['manager' => $this->manager] ); $this->pageFactory = $objectManager->get(PageFactory::class); + $this->identityMap = $objectManager->get(IdentityMap::class); } /** @@ -80,15 +86,17 @@ protected function setUp() public function testCustomLayoutUpdate(): void { /** @var Page $page */ - $page = $this->pageFactory->create(); + $page = $this->pageFactory->create(['customLayoutRepository' => $this->repo]); $page->load('page100', 'identifier'); $pageId = (int)$page->getId(); + $this->identityMap->add($page); //Set file ID $this->repo->save(new CustomLayoutSelected($pageId, 'select2')); //Test handles $result = $this->resultFactory->create(); $this->manager->applyUpdate($result, $this->repo->getFor($pageId)); + $this->identityMap->remove((int)$page->getId()); $this->assertContains('___selectable_page100_select2', $result->getLayout()->getUpdate()->getHandles()); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php index d2ca833f3923f..2028f5d8a04b6 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -116,7 +116,7 @@ public function testCustomLayoutMeta(): void $meta['design']['children']['custom_layout_update_select']['arguments']['data'] ); $expectedList = [ - ['label' => 'No update', 'value' => ''], + ['label' => 'No update', 'value' => '_no_update_'], ['label' => 'test1', 'value' => 'test1'], ['label' => 'test2', 'value' => 'test2'] ]; @@ -141,7 +141,7 @@ public function testCustomLayoutMeta(): void $meta['design']['children']['custom_layout_update_select']['arguments']['data'] ); $expectedList = [ - ['label' => 'No update', 'value' => ''], + ['label' => 'No update', 'value' => '_no_update_'], ['label' => 'Use existing layout update XML', 'value' => '_existing_'], ['label' => 'test3', 'value' => 'test3'], ]; diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php index 550b40a1bfec6..9734ed3abaeed 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -8,33 +8,40 @@ use Magento\Cms\Model\Page as PageModel; use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\TestFramework\Cms\Model\CustomLayoutManager; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); $pageFactory = $objectManager->get(PageModelFactory::class); +/** @var CustomLayoutManager $fakeManager */ +$fakeManager = $objectManager->get(CustomLayoutManager::class); +$layoutRepo = $objectManager->create(PageModel\CustomLayoutRepositoryInterface::class, ['manager' => $fakeManager]); + /** @var PageModel $page */ -$page = $pageFactory->create(); +$page = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page->setIdentifier('test_custom_layout_page_1'); $page->setTitle('Test Page'); -$page->setCustomLayoutUpdateXml('tst'); -$page->setLayoutUpdateXml('tst_current'); +$page->setCustomLayoutUpdateXml('<container />'); +$page->setLayoutUpdateXml('<container />'); $page->setIsActive(true); $page->setStoreId(0); $page->save(); /** @var PageModel $page2 */ -$page2 = $pageFactory->create(); +$page2 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page2->setIdentifier('test_custom_layout_page_2'); $page2->setTitle('Test Page 2'); $page->setIsActive(true); $page->setStoreId(0); $page2->save(); /** @var PageModel $page3 */ -$page3 = $pageFactory->create(); +$page3 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page3->setIdentifier('test_custom_layout_page_3'); $page3->setTitle('Test Page 3'); -$page3->setData('layout_update_selected', 'test_selected'); $page3->setStores([0]); $page3->setIsActive(1); $page3->setContent('<h1>Test Page</h1>'); $page3->setPageLayout('1column'); $page3->save(); +$fakeManager->fakeAvailableFiles((int)$page3->getId(), ['test_selected']); +$page3->setData('layout_update_selected', 'test_selected'); +$page3->save(); From f6446259cea26f20c9f2471b86127af136c3352c Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Thu, 3 Oct 2019 17:24:52 -0500 Subject: [PATCH 0791/2437] MC-15986: Category Filtering - added resolver changes on filter --- .../Model/Category/CategoryFilter.php | 18 +++++++-------- .../Model/Resolver/CategoryList.php | 23 ++++++++++++++++--- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 5058bb2e07c66..42f35a8711858 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -32,24 +32,24 @@ public function __construct( * Filter for filtering the requested categories id's based on url_key, ids, name in the result. * * @param array $args - * @return array + * @param \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection + * @return bool */ - public function applyFilters(array $args): array - { - $categoryCollection = $this->collectionFactory->create(); + public function applyFilters( + array $args, + \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection + ): bool { foreach ($args['filters'] as $field => $cond) { foreach ($cond as $condType => $value) { if ($field === 'ids') { $categoryCollection->addIdFilter($value); + } elseif ($condType === 'match') { + $categoryCollection->addAttributeToFilter($field, ['like' => "%{$value}%"]); } else { $categoryCollection->addAttributeToFilter($field, [$condType => $value]); } } } - $categoryIds = []; - foreach ($categoryCollection as $category) { - $categoryIds[] = (int)$category->getId(); - } - return $categoryIds; + return true; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 2511e07dc4f89..140cb68be6799 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -16,6 +16,7 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree; use Magento\CatalogGraphQl\Model\Category\CategoryFilter; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; /** * Category List resolver, used for GraphQL category data request processing. @@ -27,6 +28,11 @@ class CategoryList implements ResolverInterface */ private $categoryTree; + /** + * @var CollectionFactory + */ + private $collectionFactory; + /** * @var CategoryFilter */ @@ -47,17 +53,20 @@ class CategoryList implements ResolverInterface * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree * @param CheckCategoryIsActive $checkCategoryIsActive * @param CategoryFilter $categoryFilter + * @param CollectionFactory $collectionFactory */ public function __construct( CategoryTree $categoryTree, ExtractDataFromCategoryTree $extractDataFromCategoryTree, CheckCategoryIsActive $checkCategoryIsActive, - CategoryFilter $categoryFilter + CategoryFilter $categoryFilter, + CollectionFactory $collectionFactory ) { $this->categoryTree = $categoryTree; $this->extractDataFromCategoryTree = $extractDataFromCategoryTree; $this->checkCategoryIsActive = $checkCategoryIsActive; $this->categoryFilter = $categoryFilter; + $this->collectionFactory = $collectionFactory; } /** @@ -69,10 +78,18 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $value[$field->getName()]; } + $categoryCollection = $this->collectionFactory->create(); + $categoryCollection->addAttributeToFilter('is_active', 1); + $categoryCollection->addAttributeToSelect(['name','url_key', 'ids']); + if (!isset($args['filters'])) { $rootCategoryIds = [(int)$context->getExtensionAttributes()->getStore()->getRootCategoryId()]; } else { - $rootCategoryIds = $this->categoryFilter->applyFilters($args); + $this->categoryFilter->applyFilters($args, $categoryCollection); + $rootCategoryIds = []; + foreach ($categoryCollection as $category) { + $rootCategoryIds[] = (int)$category->getId(); + } } $result = []; @@ -81,7 +98,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->checkCategoryIsActive->execute($rootCategoryId); } $categoryTree = $this->categoryTree->getTree($info, $rootCategoryId); - if (empty($categoryTree)) { + if (empty($categoryTree) || ($categoryTree->count() == 0)) { throw new GraphQlNoSuchEntityException(__('Category doesn\'t exist')); } $result[] = current($this->extractDataFromCategoryTree->execute($categoryTree)); From b66bafe743204efe0c6b56b48d9c602e18e58ad6 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 3 Oct 2019 16:18:36 -0500 Subject: [PATCH 0792/2437] MC-21467: Reorder from admin throws an error when adding products - Fix return key press in products grid filter submits the "create order" form in admin --- .../view/adminhtml/templates/widget/grid.phtml | 3 +++ .../adminhtml/templates/widget/grid/extended.phtml | 3 +++ .../Gateway/Validator/ErrorCodeProvider.php | 13 ++++++++----- .../Block/Adminhtml/Order/Create/Search/Grid.php | 12 ++++++------ .../view/adminhtml/web/order/create/scripts.js | 14 ++++++++++++++ lib/web/mage/adminhtml/grid.js | 5 +++++ 6 files changed, 39 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid.phtml index b712bc6c95315..7f6f2bbd13fa5 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid.phtml @@ -170,6 +170,9 @@ $numColumns = $block->getColumns() !== null ? count($block->getColumns()) : 0; <?php if ($block->getSortableUpdateCallback()) : ?> <?= $block->escapeJs($block->getJsObjectName()) ?>.sortableUpdateCallback = <?= /* @noEscape */ $block->getSortableUpdateCallback() ?>; <?php endif; ?> + <?php if ($block->getFilterKeyPressCallback()) : ?> + <?= $block->escapeJs($block->getJsObjectName()) ?>.filterKeyPressCallback = <?= /* @noEscape */ $block->getFilterKeyPressCallback() ?>; + <?php endif; ?> <?= $block->escapeJs($block->getJsObjectName()) ?>.bindSortable(); <?php if ($block->getRowInitCallback()) : ?> <?= $block->escapeJs($block->getJsObjectName()) ?>.initRowCallback = <?= /* @noEscape */ $block->getRowInitCallback() ?>; diff --git a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml index 0bb453f25d7ca..527ddc436207f 100644 --- a/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml +++ b/app/code/Magento/Backend/view/adminhtml/templates/widget/grid/extended.phtml @@ -272,6 +272,9 @@ $numColumns = count($block->getColumns()); <?php if ($block->getCheckboxCheckCallback()) : ?> <?= $block->escapeJs($block->getJsObjectName()) ?>.checkboxCheckCallback = <?= /* @noEscape */ $block->getCheckboxCheckCallback() ?>; <?php endif; ?> + <?php if ($block->getFilterKeyPressCallback()) : ?> + <?= $block->escapeJs($block->getJsObjectName()) ?>.filterKeyPressCallback = <?= /* @noEscape */ $block->getFilterKeyPressCallback() ?>; + <?php endif; ?> <?php if ($block->getRowInitCallback()) : ?> <?= $block->escapeJs($block->getJsObjectName()) ?>.initRowCallback = <?= /* @noEscape */ $block->getRowInitCallback() ?>; <?= $block->escapeJs($block->getJsObjectName()) ?>.initGridRows(); diff --git a/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeProvider.php b/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeProvider.php index 58ce33305da85..2f73dd8f380dc 100644 --- a/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeProvider.php +++ b/app/code/Magento/Braintree/Gateway/Validator/ErrorCodeProvider.php @@ -11,6 +11,7 @@ use Braintree\Error\Validation; use Braintree\Result\Error; use Braintree\Result\Successful; +use Braintree\Transaction; /** * Processes errors codes from Braintree response. @@ -38,12 +39,14 @@ public function getErrorCodes($response): array $result[] = $error->code; } - if (isset($response->transaction) && $response->transaction->status === 'gateway_rejected') { - $result[] = $response->transaction->gatewayRejectionReason; - } + if (isset($response->transaction) && $response->transaction) { + if ($response->transaction->status === Transaction::GATEWAY_REJECTED) { + $result[] = $response->transaction->gatewayRejectionReason; + } - if (isset($response->transaction) && $response->transaction->status === 'processor_declined') { - $result[] = $response->transaction->processorResponseCode; + if ($response->transaction->status === Transaction::PROCESSOR_DECLINED) { + $result[] = $response->transaction->processorResponseCode; + } } return $result; diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 9a271f741edda..001c581dc0dac 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -5,8 +5,7 @@ */ namespace Magento\Sales\Block\Adminhtml\Order\Create\Search; -use Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider\ProductCollection - as ProductCollectionDataProvider; +use Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider\ProductCollection; use Magento\Framework\App\ObjectManager; /** @@ -48,7 +47,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended protected $_productFactory; /** - * @var ProductCollectionDataProvider $productCollectionProvider + * @var ProductCollection $productCollectionProvider */ private $productCollectionProvider; @@ -60,7 +59,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended * @param \Magento\Backend\Model\Session\Quote $sessionQuote * @param \Magento\Sales\Model\Config $salesConfig * @param array $data - * @param ProductCollectionDataProvider|null $productCollectionProvider + * @param ProductCollection|null $productCollectionProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -70,14 +69,14 @@ public function __construct( \Magento\Backend\Model\Session\Quote $sessionQuote, \Magento\Sales\Model\Config $salesConfig, array $data = [], - ProductCollectionDataProvider $productCollectionProvider = null + ProductCollection $productCollectionProvider = null ) { $this->_productFactory = $productFactory; $this->_catalogConfig = $catalogConfig; $this->_sessionQuote = $sessionQuote; $this->_salesConfig = $salesConfig; $this->productCollectionProvider = $productCollectionProvider - ?: ObjectManager::getInstance()->get(ProductCollectionDataProvider::class); + ?: ObjectManager::getInstance()->get(ProductCollection::class); parent::__construct($context, $backendHelper, $data); } @@ -94,6 +93,7 @@ protected function _construct() $this->setCheckboxCheckCallback('order.productGridCheckboxCheck.bind(order)'); $this->setRowInitCallback('order.productGridRowInit.bind(order)'); $this->setDefaultSort('entity_id'); + $this->setFilterKeyPressCallback('order.productGridFilterKeyPress'); $this->setUseAjax(true); if ($this->getRequest()->getParam('collapse')) { $this->setIsCollapsed(true); diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 3fe9d08782880..4e07414510748 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -795,6 +795,20 @@ define([ grid.reloadParams = {'products[]':this.gridProducts.keys()}; }, + productGridFilterKeyPress: function (grid, event) { + var returnKey = parseInt(Event.KEY_RETURN || 13, 10); + + if (event.keyCode === returnKey) { + if (typeof event.stopPropagation === 'function') { + event.stopPropagation(); + } + + if (typeof event.preventDefault === 'function') { + event.preventDefault(); + } + } + }, + /** * Submit configured products to quote */ diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index 1c9319f95a647..28bdb96e5cdb7 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -63,6 +63,7 @@ define([ this.initRowCallback = false; this.doFilterCallback = false; this.sortableUpdateCallback = false; + this.filterKeyPressCallback = false; this.reloadParams = false; @@ -511,6 +512,10 @@ define([ if (event.keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq this.doFilter(); } + + if (this.filterKeyPressCallback) { + this.filterKeyPressCallback(this, event); + } }, /** From e9c704b9277e2567278992dabd14946e65c42f42 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 4 Oct 2019 10:12:06 +0300 Subject: [PATCH 0793/2437] MC-5233: DateTime product attributes support --- .../Attribute/Frontend/DatetimeTest.php | 2 +- .../Unit/Component/Filters/Type/DateTest.php | 21 ++++--- .../Ui/Test/Unit/Component/FiltersTest.php | 2 +- .../Eav/Model/Entity/AttributeTest.php | 55 ++++++++++++------- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php index c775548fc8c75..163f3d208e724 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php @@ -56,7 +56,7 @@ protected function setUp() /** * Test to retrieve attribute value - * + * * @param string $frontendInput * @param int $timeType * @dataProvider getValueDataProvider diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 7038a587be0b0..31d7ca92c5985 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -239,6 +239,7 @@ public function getPrepareDataProvider() * @param array $filterData * @param array $expectedCondition * @param MockObject $uiComponent + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processFilters( string $name, @@ -257,18 +258,22 @@ private function processFilters( $from = new \DateTime($filterData[$name]['from']); $to = new \DateTime($filterData[$name]['to']); $uiComponent->method('convertDatetime') - ->willReturnMap([ - [$filterData[$name]['from'], true, $from], - [$filterData[$name]['to'], true, $to], - ]); + ->willReturnMap( + [ + [$filterData[$name]['from'], true, $from], + [$filterData[$name]['to'], true, $to], + ] + ); } else { $from = new \DateTime($filterData[$name]['from']); $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); $uiComponent->method('convertDate') - ->willReturnMap([ - [$filterData[$name]['from'], 0, 0, 0, true, $from], - [$filterData[$name]['to'], 23, 59, 59, true, $to], - ]); + ->willReturnMap( + [ + [$filterData[$name]['from'], 0, 0, 0, true, $from], + [$filterData[$name]['to'], 23, 59, 59, true, $to], + ] + ); } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php index 19a1be69ca1d7..d4cf7f1af8d62 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php @@ -58,7 +58,7 @@ protected function setUp() /** * Test to Update filter component according to $component - * + * * @param string $filterType * @param string $filterName * @dataProvider updateDataProvider diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 2750e2a768aab..5df08762e29a7 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -3,15 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Eav\Model\Entity; -use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; -class AttributeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test EAV Entity attribute model + */ +class AttributeTest extends TestCase { /** * @var Attribute @@ -19,33 +24,33 @@ class AttributeTest extends \PHPUnit\Framework\TestCase private $attribute; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ private $objectManager; /** * @var ResolverInterface */ - private $_localeResolver; + private $localeResolver; /** - * {@inheritdoc} + * @inheritdoc */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->attribute = $this->objectManager->get(Attribute::class); - $this->_localeResolver = $this->objectManager->get(ResolverInterface::class); + $this->localeResolver = $this->objectManager->get(ResolverInterface::class); } /** - * {@inheritdoc} + * @inheritdoc */ protected function tearDown() { $this->attribute = null; $this->objectManager = null; - $this->_localeResolver = null; + $this->localeResolver = null; } /** @@ -56,11 +61,17 @@ protected function tearDown() * @dataProvider beforeSaveDataProvider * @throws */ - public function testBeforeSave($defaultValue, $backendType, $locale, $expected) - { + public function testBeforeSave( + string $defaultValue, + string $backendType, + string $frontendInput, + string $locale, + string $expected + ) { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->attribute->setFrontendInput($frontendInput); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->assertEquals($expected, $this->attribute->getDefaultValue()); @@ -74,13 +85,15 @@ public function testBeforeSave($defaultValue, $backendType, $locale, $expected) public function beforeSaveDataProvider() { return [ - ['21/07/18', 'datetime', 'en_AU', '2018-07-21 00:00:00'], - ['07/21/18', 'datetime', 'en_US', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'fr_FR', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'de_DE', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'uk_UA', '2018-07-21 00:00:00'], - ['100.50', 'decimal', 'en_US', '100.50'], - ['100,50', 'decimal', 'uk_UA', '100.5'], + ['21/07/18', 'datetime', 'date', 'en_AU', '2018-07-21 00:00:00'], + ['07/21/18', 'datetime', 'date', 'en_US', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'fr_FR', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'de_DE', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'uk_UA', '2018-07-21 00:00:00'], + ['100.50', 'decimal', 'decimal', 'en_US', '100.50'], + ['100,50', 'decimal', 'decimal', 'uk_UA', '100.5'], + ['07/21/2019 2:30 PM', 'datetime', 'datetime', 'en_US', '2019-07-21 21:30:00'], + ['21.07.2019 14:30', 'datetime', 'datetime', 'uk_UA', '2019-07-21 21:30:00'], ]; } @@ -90,13 +103,13 @@ public function beforeSaveDataProvider() * @param string $locale * @param string $expected * @dataProvider beforeSaveErrorDataDataProvider - * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedException LocalizedException */ public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $expected) { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->expectExceptionMessage($expected); From 0a2eaae1b2f401f1341f4a767386e55c8fd6f39e Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 4 Oct 2019 11:28:48 +0400 Subject: [PATCH 0794/2437] MC-15759: Elasticsearch: Searches That Contain Question Mark Followed by Semicolon Will Result In Error Page (Multiple Queries Error) - Updated automated test script --- .../Mftf/Data/Elasticsearch6ConfigData.xml | 15 ++++++++++++ ...ntElasticsearch6SearchInvalidValueTest.xml | 10 ++++---- .../Test/Mftf/Data/SearchEngineConfigData.xml | 23 +++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml create mode 100644 app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml new file mode 100644 index 0000000000000..7a61f13e62049 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableElasticSearch6Config"> + <data key="path">catalog/search/engine</data> + <data key="value">elasticsearch6</data> + </entity> +</entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml index 5f6949dcafef5..1c105bff9aa0b 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontElasticsearch6SearchInvalidValueTest"> <annotations> - <features value="Search"/> + <features value="Elasticsearch6"/> <stories value="Search Product on Storefront"/> <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> @@ -24,17 +24,17 @@ <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!--Enable Elasticsearch--> - <magentoCLI command="config:set catalog/search/engine elasticsearch6" stepKey="enableElasticsearch"/> + <magentoCLI command="config:set {{EnableElasticSearch6Config.path}} {{EnableElasticSearch6Config.value}}" stepKey="enableElasticsearch6"/> <!--Set Minimal Query Length--> - <magentoCLI command="config:set catalog/search/min_query_length 2" stepKey="setMinQueryLength"/> + <magentoCLI command="config:set {{SetMinQueryLength2Config.path}} {{SetMinQueryLength2Config.value}}" stepKey="setMinQueryLength"/> <!--Reindex indexes and clear cache--> <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> <magentoCLI command="cache:flush config" stepKey="flushCache"/> </before> <after> <!--Set configs to default--> - <magentoCLI command="config:set catalog/search/min_query_length 3" stepKey="setMinQueryLengthPreviousState"/> - <magentoCLI command="config:set catalog/search/engine mysql" stepKey="resetSearchEnginePreviousState"/> + <magentoCLI command="config:set {{SetMinQueryLength3Config.path}} {{SetMinQueryLength3Config.value}}" stepKey="setMinQueryLengthPreviousState"/> + <magentoCLI command="config:set {{SetDefaultSearchEngineConfig.path}} {{SetDefaultSearchEngineConfig.value}}" stepKey="resetSearchEnginePreviousState"/> <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> diff --git a/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml b/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml new file mode 100644 index 0000000000000..7e1019904b4e9 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SetDefaultSearchEngineConfig"> + <data key="path">catalog/search/engine</data> + <data key="value">mysql</data> + </entity> + <entity name="SetMinQueryLength3Config"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">3</data> + </entity> + <entity name="SetMinQueryLength2Config"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">2</data> + </entity> +</entities> From fc7e0240ea3ae2288db1374b7a5d5f36bf891827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Fri, 4 Oct 2019 09:52:08 +0200 Subject: [PATCH 0795/2437] Fix #13278 - revert docblock changes in interface --- .../Magento/SalesRule/Api/Data/RuleInterface.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/SalesRule/Api/Data/RuleInterface.php b/app/code/Magento/SalesRule/Api/Data/RuleInterface.php index b811289798e31..d7d72bef293b5 100644 --- a/app/code/Magento/SalesRule/Api/Data/RuleInterface.php +++ b/app/code/Magento/SalesRule/Api/Data/RuleInterface.php @@ -61,14 +61,14 @@ public function setName($name); /** * Get display label * - * @return RuleLabelInterface[]|null + * @return \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null */ public function getStoreLabels(); /** * Set display label * - * @param RuleLabelInterface[]|null $storeLabels + * @param \Magento\SalesRule\Api\Data\RuleLabelInterface[]|null $storeLabels * @return $this */ public function setStoreLabels(array $storeLabels = null); @@ -182,14 +182,14 @@ public function setIsActive($isActive); /** * Get condition for the rule * - * @return ConditionInterface|null + * @return \Magento\SalesRule\Api\Data\ConditionInterface|null */ public function getCondition(); /** * Set condition for the rule * - * @param ConditionInterface|null $condition + * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $condition * @return $this */ public function setCondition(ConditionInterface $condition = null); @@ -197,14 +197,14 @@ public function setCondition(ConditionInterface $condition = null); /** * Get action condition * - * @return ConditionInterface|null + * @return \Magento\SalesRule\Api\Data\ConditionInterface|null */ public function getActionCondition(); /** * Set action condition * - * @param ConditionInterface|null $actionCondition + * @param \Magento\SalesRule\Api\Data\ConditionInterface|null $actionCondition * @return $this */ public function setActionCondition(ConditionInterface $actionCondition = null); @@ -438,14 +438,14 @@ public function setSimpleFreeShipping($simpleFreeShipping); /** * Retrieve existing extension attributes object or create a new one. * - * @return RuleExtensionInterface|null + * @return \Magento\SalesRule\Api\Data\RuleExtensionInterface|null */ public function getExtensionAttributes(); /** * Set an extension attributes object. * - * @param RuleExtensionInterface $extensionAttributes + * @param \Magento\SalesRule\Api\Data\RuleExtensionInterface $extensionAttributes * @return $this */ public function setExtensionAttributes(RuleExtensionInterface $extensionAttributes); From d7acc36cb36d7e72fe37a2549545ffb88c9f669c Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 4 Oct 2019 12:17:27 +0400 Subject: [PATCH 0796/2437] MC-18215: Error message while creating shipping label - Updated automated test script --- .../AdminGeneralStoreInfomationConfigData.xml | 33 +++++++++++++++++++ .../AdminGeneralStoreInfomationConfigData.xml | 22 ------------- 2 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml diff --git a/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml new file mode 100644 index 0000000000000..a8db2f94d69ab --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminGeneralSetStoreNameConfigData"> + <data key="path">general/store_information/name</data> + <data key="value">New Store Information</data> + </entity> + <entity name="AdminGeneralSetStorePhoneConfigData"> + <data key="path">general/store_information/phone</data> + </entity> + <entity name="AdminGeneralSetCountryConfigData"> + <data key="path">general/store_information/country_id</data> + </entity> + <entity name="AdminGeneralSetCityConfigData"> + <data key="path">general/store_information/city</data> + </entity> + <entity name="AdminGeneralSetPostcodeConfigData"> + <data key="path">general/store_information/postcode</data> + </entity> + <entity name="AdminGeneralSetStreetAddressConfigData"> + <data key="path">general/store_information/street_line1</data> + </entity> + <entity name="AdminGeneralSetStreetAddress2ConfigData"> + <data key="path">general/store_information/street_line2</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml index ae0801736d3a9..e4c020cc449fb 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AdminGeneralStoreInfomationConfigData.xml @@ -8,28 +8,6 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="AdminGeneralSetStoreNameConfigData"> - <data key="path">general/store_information/name</data> - <data key="value">New Store Information</data> - </entity> - <entity name="AdminGeneralSetStorePhoneConfigData"> - <data key="path">general/store_information/phone</data> - </entity> - <entity name="AdminGeneralSetCountryConfigData"> - <data key="path">general/store_information/country_id</data> - </entity> - <entity name="AdminGeneralSetCityConfigData"> - <data key="path">general/store_information/city</data> - </entity> - <entity name="AdminGeneralSetPostcodeConfigData"> - <data key="path">general/store_information/postcode</data> - </entity> - <entity name="AdminGeneralSetStreetAddressConfigData"> - <data key="path">general/store_information/street_line1</data> - </entity> - <entity name="AdminGeneralSetStreetAddress2ConfigData"> - <data key="path">general/store_information/street_line2</data> - </entity> <entity name="AdminGeneralSetVatNumberConfigData"> <data key="path">general/store_information/merchant_vat_number</data> <data key="value">111607872</data> From 04f4b88d6eb23d4e51990a51757bf16cc6f69f95 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <d.ruf@bitexpert.de> Date: Fri, 4 Oct 2019 10:18:46 +0200 Subject: [PATCH 0797/2437] Correctly check if the type is undefined --- lib/web/mage/decorate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/decorate.js b/lib/web/mage/decorate.js index 238735ac455b9..9308c2b60e845 100644 --- a/lib/web/mage/decorate.js +++ b/lib/web/mage/decorate.js @@ -25,7 +25,7 @@ items; if (list.length > 0) { - items = typeof isRecursive === undefined || isRecursive ? + items = typeof isRecursive === 'undefined' || isRecursive ? list.find('li') : list.children(); items.decorate('generic', ['odd', 'even', 'last']); From 70379ee8fc78c36460685a284c24d04f5e0f39a9 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <d.ruf@bitexpert.de> Date: Fri, 4 Oct 2019 10:19:44 +0200 Subject: [PATCH 0798/2437] Correctly check the behaviourType --- .../Ui/view/base/web/js/form/components/insert-listing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/insert-listing.js b/app/code/Magento/Ui/view/base/web/js/form/components/insert-listing.js index b33f0b5c72395..53580fc069c47 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/insert-listing.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/insert-listing.js @@ -155,7 +155,7 @@ define([ updateExternalValueByEditableData: function () { var updatedExtValue; - if (!this.behaviourType === 'edit' || _.isEmpty(this.editableData) || _.isEmpty(this.externalValue())) { + if (!(this.behaviourType === 'edit') || _.isEmpty(this.editableData) || _.isEmpty(this.externalValue())) { return; } From dbf2c29bef8ec4a768156bd7ace0c99647bec631 Mon Sep 17 00:00:00 2001 From: "Kristof Ringleff, Fooman" <kristof@fooman.co.nz> Date: Fri, 4 Oct 2019 22:28:41 +1300 Subject: [PATCH 0799/2437] Fix multiple copy to recipients --- app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index c4523981ac729..ae188309ea646 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -85,8 +85,8 @@ public function sendCopyTo() $copyTo = $this->identityContainer->getEmailCopyTo(); if (!empty($copyTo)) { - $this->configureEmailTemplate(); foreach ($copyTo as $email) { + $this->configureEmailTemplate(); $this->transportBuilder->addTo($email); $transport = $this->transportBuilder->getTransport(); $transport->sendMessage(); From 10f89e73f3515cb9e3a03a04f62bda7fb1a2c0f1 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Fri, 4 Oct 2019 13:50:13 +0300 Subject: [PATCH 0800/2437] MC-21426: Fix all broken Product related tests in Page Builder Component - Fix query for staging. --- .../Catalog/Model/ResourceModel/Category.php | 17 ++++++++++++++--- .../Model/ResourceModel/Product/Collection.php | 7 ++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 797ce72ae9b7a..9e0d174a4cccb 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -18,6 +18,8 @@ use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; use Magento\Catalog\Setup\CategorySetup; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Api\Data\ProductInterface; /** * Resource model for category entity @@ -95,6 +97,11 @@ class Category extends AbstractResource */ private $indexerProcessor; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * Category constructor. * @param \Magento\Eav\Model\Entity\Context $context @@ -106,6 +113,7 @@ class Category extends AbstractResource * @param Processor $indexerProcessor * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param MetadataPool|null $metadataPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -117,7 +125,8 @@ public function __construct( \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, Processor $indexerProcessor, $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + MetadataPool $metadataPool = null ) { parent::__construct( $context, @@ -132,6 +141,7 @@ public function __construct( $this->indexerProcessor = $indexerProcessor; $this->serializer = $serializer ?: ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class); } /** @@ -1160,13 +1170,14 @@ public function getCategoryWithChildren(int $categoryId): array return []; } + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $select = $connection->select() ->from( ['cce' => $this->getTable('catalog_category_entity')], - ['entity_id', 'parent_id', 'path'] + [$linkField, 'parent_id', 'path'] )->join( ['cce_int' => $this->getTable('catalog_category_entity_int')], - 'cce.entity_id = cce_int.entity_id', + 'cce.' . $linkField . ' = cce_int.' . $linkField, ['is_anchor' => 'cce_int.value'] )->where( 'cce_int.attribute_id = ?', diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 42d55892b6ec6..3239d3e1cba26 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2113,13 +2113,14 @@ private function getChildrenCategories(int $categoryId): array $firstCategory = array_shift($categories); if ($firstCategory['is_anchor'] == 1) { - $anchorCategory[] = (int)$firstCategory['entity_id']; + $linkField = $this->getProductEntityMetadata()->getLinkField(); + $anchorCategory[] = (int)$firstCategory[$linkField]; foreach ($categories as $category) { if (in_array($category['parent_id'], $categoryIds) && in_array($category['parent_id'], $anchorCategory)) { - $categoryIds[] = (int)$category['entity_id']; + $categoryIds[] = (int)$category[$linkField]; if ($category['is_anchor'] == 1) { - $anchorCategory[] = (int)$category['entity_id']; + $anchorCategory[] = (int)$category[$linkField]; } } } From 2fee2347b6ffae73b2ae40f9c73237a090379c3c Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 4 Oct 2019 14:51:20 +0400 Subject: [PATCH 0801/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6411 --- .../ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml | 3 +++ .../AdminChangeTableRatesShippingMethodStatusActionGroup.xml | 3 +++ ...AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml | 2 +- .../Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml index cfed144184e40..b8493bf288378 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderSelectShippingMethodActionGroup.xml @@ -9,6 +9,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOrderSelectShippingMethodActionGroup"> + <annotations> + <description>Select Shipping method from admin order page.</description> + </annotations> <arguments> <argument name="methodTitle" type="string" defaultValue="flatrate"/> <argument name="methodName" type="string" defaultValue="fixed"/> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml index 2df78f3a0d1aa..e0fec2a6dc4d2 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml @@ -18,6 +18,9 @@ <selectOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" userInput="{{status}}" stepKey="changeTableRatesMethodStatus"/> </actionGroup> <actionGroup name="AdminImportFileTableRatesShippingMethodActionGroup"> + <annotations> + <description>Import a file in Table Rates tab in Shipping Method config page.</description> + </annotations> <arguments> <argument name="file" type="string" defaultValue="test_tablerates.csv"/> </arguments> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index 3decca9a971d1..cf43f8cf7b735 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -11,7 +11,7 @@ <test name="AdminCreateOrderCustomStoreShippingMethodTableRatesTest"> <annotations> <features value="Shipping"/> - <stories value="Shipping method Table Rates settings gets from wrong store"/> + <stories value="Shipping method Table Rates"/> <title value="Create order on second store with shipping method Table Rates"/> <description value="Create order on second store with shipping method Table Rates"/> <severity value="MAJOR"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml index fc54101c3a5f4..1a43ae1d2bbd1 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminWebsitePageActionGroup.xml @@ -9,6 +9,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminGoCreatedWebsitePageActionGroup"> + <annotations> + <description>Filter website name in grid and go first found website page</description> + </annotations> <arguments> <argument name="websiteName" type="string" defaultValue="SecondWebsite"/> </arguments> From 4ee20488273f435ceb9ae572250466ccddb58c6e Mon Sep 17 00:00:00 2001 From: Lusine Papyan <Lusine_Papyan@epam.com> Date: Fri, 4 Oct 2019 15:09:44 +0400 Subject: [PATCH 0802/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Automation test for MC-6405 --- ...isplayTableRatesShippingMethodForAETest.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml index 5b6bcd1ebcd97..a93377c694df8 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -30,7 +30,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <comment userInput="Rollback config" stepKey="rollbackConfigComment"/> + <!--Rollback config--> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodSystemConfigPage"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreViewToMainWebsite"> <argument name="website" value="_defaultWebsite"/> @@ -41,7 +41,7 @@ <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveSystemConfig"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <comment userInput="Admin Configuration: enable Table Rates and import CSV file with the rates" stepKey="prepareAdminConfigurationComment"/> + <!--Admin Configuration: enable Table Rates and import CSV file with the rates--> <actionGroup ref="AdminOpenShippingMethodsConfigPageActionGroup" stepKey="openShippingMethodConfigPage"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="AdminSwitchStoreView"> <argument name="website" value="_defaultWebsite"/> @@ -49,28 +49,28 @@ <actionGroup ref="AdminChangeTableRatesShippingMethodStatusActionGroup" stepKey="enableTableRatesShippingMethod"/> <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="tablerates.csv" stepKey="attachFileForImport"/> <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> - <comment userInput="Login as created customer" stepKey="loginAsCustomerComment"/> + <!--Login as created customer--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <comment userInput="Add the created product to the shopping cart" stepKey="addProductToCartComment"/> + <!--Add the created product to the shopping cart--> <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> - <comment userInput="Proceed to Checkout from the mini cart" stepKey="proceedToCheckoutComment"/> + <!--Proceed to Checkout from the mini cart--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> - <comment userInput="Shipping Method: select table rate" stepKey="assertShippingMethodComment"/> + <!--Shipping Method: select table rate--> <actionGroup ref="AssertStoreFrontShippingMethodAvailableActionGroup" stepKey="assertShippingMethodAvailable"> <argument name="shippingMethodName" value="Best Way"/> </actionGroup> <actionGroup ref="SetShippingMethodActionGroup" stepKey="setShippingMethodTableRate"> <argument name="shippingMethodName" value="Best Way"/> </actionGroup> - <comment userInput="Proceed to Review and Payments section" stepKey="proceedToReviewAndPaymentsComment"/> + <!--Proceed to Review and Payments section--> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickToSaveShippingInfo"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> <waitForPageLoad stepKey="waitForReviewAndPaymentsPageIsLoaded"/> - <comment userInput="Place order and assert the message of success" stepKey="placeOrderComment"/> - <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"/> + <!--Place order and assert the message of success--> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrderProductSuccessful"/> </test> </tests> From 2a6dc66bbeab6179a717f31c988ed0a4eee8ec4d Mon Sep 17 00:00:00 2001 From: Ivan Koliadynskyy <i.koliadynskyy@gmail.com> Date: Fri, 4 Oct 2019 17:04:19 +0300 Subject: [PATCH 0803/2437] Fix for merging guest cart items and customer cart items --- .../Magento/Quote/Model/QuoteManagement.php | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 84ef699b6209e..1df2cd4d08def 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -298,23 +298,28 @@ public function assignCustomer($cartId, $customerId, $storeId) ); } try { - $this->quoteRepository->getForCustomer($customerId); - throw new StateException( - __("The customer can't be assigned to the cart because the customer already has an active cart.") - ); - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + $customerActiveQuote = $this->quoteRepository->getForCustomer($customerId); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + /** This exception appear when customer have no active cart*/ + $customerActiveQuote = $this->quoteFactory->create(); + $customerActiveQuote->setCustomer($customer); + $customerActiveQuote->setCustomerIsGuest(0); + $customerActiveQuote->setStoreId($storeId); + $customerActiveQuote->setIsActive(true); } - $quote->setCustomer($customer); - $quote->setCustomerIsGuest(0); - /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ - $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'quote_id'); - if ($quoteIdMask->getId()) { - $quoteIdMask->delete(); + if ($customerActiveQuote->getIsActive()) { + /** Merge carts */ + $customerActiveQuote->merge($quote); + $this->quoteRepository->delete($quote); + $this->quoteRepository->save($customerActiveQuote); + + return true; + } else { + throw new \Magento\Framework\Exception\NoSuchEntityException( + __("The customer can't be assigned to the cart. No active cart for customer.") + ); } - $this->quoteRepository->save($quote); - return true; } /** From dc460c817b810553c3a037f9ae65985a07efb975 Mon Sep 17 00:00:00 2001 From: Ivan Koliadynskyy <i.koliadynskyy@gmail.com> Date: Fri, 4 Oct 2019 17:05:21 +0300 Subject: [PATCH 0804/2437] Update for PHPUnit test scenario according to new update for merging guest cart and customer cart. --- .../Test/Unit/Model/QuoteManagementTest.php | 99 ++++++++----------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index cd2afc39733f2..85b030d9d1291 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -7,11 +7,14 @@ namespace Magento\Quote\Test\Unit\Model; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Customer; use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress; use Magento\Quote\Model\CustomerManagement; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Sales\Api\Data\OrderAddressInterface; @@ -199,7 +202,7 @@ protected function setUp() ); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'assignCustomer', 'collectTotals', @@ -275,7 +278,7 @@ public function testCreateEmptyCartAnonymous() $storeId = 345; $quoteId = 2311; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, ['setCollectShippingRates'] @@ -306,14 +309,14 @@ public function testCreateEmptyCartForCustomer() $quoteId = 2311; $userId = 567; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $this->quoteRepositoryMock ->expects($this->once()) ->method('getActiveForCustomer') ->with($userId) ->willThrowException(new NoSuchEntityException()); - $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + $customer = $this->getMockBuilder(CustomerInterface::class) ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, @@ -342,14 +345,14 @@ public function testCreateEmptyCartForCustomerReturnExistsQuote() $storeId = 345; $userId = 567; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $this->quoteRepositoryMock ->expects($this->once()) ->method('getActiveForCustomer') ->with($userId)->willReturn($quoteMock); - $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + $customer = $this->getMockBuilder(CustomerInterface::class) ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, @@ -379,8 +382,8 @@ public function testAssignCustomerFromAnotherStore() $customerId = 455; $storeId = 5; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $quoteMock = $this->createMock(Quote::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -395,7 +398,7 @@ public function testAssignCustomerFromAnotherStore() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -424,10 +427,10 @@ public function testAssignCustomerToNonanonymousCart() $storeId = 5; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -442,7 +445,7 @@ public function testAssignCustomerToNonanonymousCart() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -463,7 +466,7 @@ public function testAssignCustomerToNonanonymousCart() } /** - * @expectedException \Magento\Framework\Exception\StateException + * @expectedException NoSuchEntityException */ public function testAssignCustomerNoSuchCustomer() { @@ -472,10 +475,9 @@ public function testAssignCustomerNoSuchCustomer() $storeId = 5; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -487,36 +489,14 @@ public function testAssignCustomerNoSuchCustomer() ->expects($this->once()) ->method('getById') ->with($customerId) - ->willReturn($customerMock); + ->willThrowException(new NoSuchEntityException()); - $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, - ['load', 'getSharedStoreIds'] + $this->expectExceptionMessage( + "No such entity." ); - $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); - $customerModelMock - ->expects($this->once()) - ->method('load') - ->with($customerId) - ->willReturnSelf(); - - $customerModelMock - ->expects($this->once()) - ->method('getSharedStoreIds') - ->willReturn([$storeId, 'some store value']); - - $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(null); - - $this->quoteRepositoryMock - ->expects($this->once()) - ->method('getForCustomer') - ->with($customerId); $this->model->assignCustomer($cartId, $customerId, $storeId); - $this->expectExceptionMessage( - "The customer can't be assigned to the cart because the customer already has an active cart." - ); } public function testAssignCustomer() @@ -525,18 +505,11 @@ public function testAssignCustomer() $customerId = 455; $storeId = 5; - $this->getPropertyValue($this->model, 'quoteIdMaskFactory') - ->expects($this->once()) - ->method('create') - ->willReturn($this->quoteIdMock); - $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); - $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); - $this->quoteIdMock->expects($this->once())->method('delete'); $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, - ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'merge'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -551,7 +524,7 @@ public function testAssignCustomer() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -574,10 +547,22 @@ public function testAssignCustomer() ->with($customerId) ->willThrowException(new NoSuchEntityException()); - $quoteMock->expects($this->once())->method('setCustomer')->with($customerMock); - $quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $activeQuoteMock = $this->createPartialMock( + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setStoreId', 'setIsActive', 'getIsActive', 'merge'] + ); - $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); + $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($activeQuoteMock); + $activeQuoteMock->expects($this->once())->method('setCustomer')->with($customerMock); + $activeQuoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $activeQuoteMock->expects($this->once())->method('setStoreId')->with($storeId); + $activeQuoteMock->expects($this->once())->method('setIsActive')->with(1); + + $activeQuoteMock->expects($this->once())->method('getIsActive')->willReturn(1); + $activeQuoteMock->expects($this->once())->method('merge')->with($quoteMock)->willReturnSelf(); + $this->quoteRepositoryMock->expects($this->once())->method('delete')->with($quoteMock); + + $this->quoteRepositoryMock->expects($this->once())->method('save')->with($activeQuoteMock); $this->model->assignCustomer($cartId, $customerId, $storeId); } @@ -881,7 +866,7 @@ protected function getQuote( \Magento\Quote\Model\Quote\Address $shippingAddress = null ) { $quote = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'setIsActive', 'getCustomerEmail', @@ -928,7 +913,7 @@ protected function getQuote( ->willReturn($payment); $customer = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['getDefaultBilling', 'getId'] ); $quote->expects($this->any())->method('getCustomerId')->willReturn($customerId); @@ -1021,7 +1006,7 @@ protected function prepareOrderFactory( public function testGetCartForCustomer() { $customerId = 100; - $cartMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $cartMock = $this->createMock(Quote::class); $this->quoteRepositoryMock->expects($this->once()) ->method('getActiveForCustomer') ->with($customerId) From 7e78f8f8109e7618a2739592b7ebe6d4abd9ba74 Mon Sep 17 00:00:00 2001 From: "ivan.pletnyov" <ivan.pletnyov@transoftgroup.com> Date: Fri, 4 Oct 2019 16:13:26 +0300 Subject: [PATCH 0805/2437] MC-20666: Add custom options to simple product --- .../Product/Save/CreateCustomOptionsTest.php | 279 +++++ .../Model/Product/CreateCustomOptionsTest.php | 984 ++++++++++++++++++ .../Catalog/Model/Product/OptionTest.php | 149 --- 3 files changed, 1263 insertions(+), 149 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php delete mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php new file mode 100644 index 0000000000000..c17e8d36846e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -0,0 +1,279 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Base test cases for product custom options with type "field". + * Option add via dispatch product controller action save with options data in POST data. + * + * @magentoAppArea adminhtml + */ +class CreateCustomOptionsTest extends AbstractBackendController +{ + /** + * Test add to product custom option with type "field". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider productWithNewOptionsDataProvider + * + * @param array $productPostData + * @throws NoSuchEntityException + */ + public function testSaveCustomOptionWithTypeField(array $productPostData): void + { + $this->getRequest()->setPostValue($productPostData); + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + /** @var ProductCustomOptionRepositoryInterface $optionRepository */ + $optionRepository = $this->_objectManager->create(ProductCustomOptionRepositoryInterface::class); + $product = $productRepository->get('simple'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + MessageInterface::TYPE_SUCCESS + ); + $productOptions = $optionRepository->getProductOptions($product); + $this->assertCount(2, $productOptions); + foreach ($productOptions as $customOption) { + $postOptionData = $productPostData['product']['options'][$customOption->getTitle()] ?? null; + $this->assertNotNull($postOptionData); + $this->assertEquals($postOptionData['title'], $customOption->getTitle()); + $this->assertEquals($postOptionData['type'], $customOption->getType()); + $this->assertEquals($postOptionData['is_require'], $customOption->getIsRequire()); + $this->assertEquals($postOptionData['sku'], $customOption->getSku()); + $this->assertEquals($postOptionData['price'], $customOption->getPrice()); + $this->assertEquals($postOptionData['price_type'], $customOption->getPriceType()); + if (isset($postOptionData['max_characters'])) { + $this->assertEquals($postOptionData['max_characters'], $customOption->getMaxCharacters()); + } else { + $this->assertEquals(0, $customOption->getMaxCharacters()); + } + } + } + + /** + * Return all data for add option to product for all cases. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function productWithNewOptionsDataProvider(): array + { + return [ + 'required_options' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 1, + 'sku' => 'test-option-title-2', + 'max_characters' => 50, + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ], + ], + ], + 'not_required_options' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 0, + 'sku' => 'test-option-title-2', + 'max_characters' => 50, + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ], + ], + ], + 'options_with_fixed_price' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 1, + 'sku' => 'test-option-title-2', + 'max_characters' => 50, + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'percent', + ], + ], + ], + ], + ], + 'options_with_percent_price' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 1, + 'sku' => 'test-option-title-2', + 'max_characters' => 50, + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 20, + 'price_type' => 'percent', + ], + ], + ], + ], + ], + 'options_with_max_charters_configuration' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 30, + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 1, + 'sku' => 'test-option-title-2', + 'max_characters' => 50, + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ], + ], + ], + 'options_without_max_charters_configuration' => [ + [ + 'product' => [ + 'options' => [ + 'Test option title 1' => [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'Test option title 2' => [ + 'record_id' => 1, + 'sort_order' => 2, + 'is_require' => 1, + 'sku' => 'test-option-title-2', + 'title' => 'Test option title 2', + 'type' => 'field', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ], + ], + ], + ]; + } + + /** + * Delete all custom options from product. + */ + protected function tearDown(): void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + /** @var ProductCustomOptionRepositoryInterface $optionRepository */ + $optionRepository = $this->_objectManager->create(ProductCustomOptionRepositoryInterface::class); + try { + $product = $productRepository->get('simple'); + foreach ($optionRepository->getProductOptions($product) as $customOption) { + $optionRepository->delete($customOption); + } + } catch (\Exception $e) { + $product = null; + } + + parent::tearDown(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php new file mode 100644 index 0000000000000..2715f7a7a0dc3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php @@ -0,0 +1,984 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; +use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test product custom options create. + * Testing option types: "Area", "File", "Drop-down", "Radio-Buttons", + * "Checkbox", "Multiple Select", "Date", "Date & Time" and "Time". + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class CreateCustomOptionsTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * Product repository. + * + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * Custom option factory. + * + * @var ProductCustomOptionInterfaceFactory + */ + private $customOptionFactory; + + /** + * @var ProductCustomOptionValuesInterfaceFactory + */ + private $customOptionValueFactory; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->customOptionFactory = $this->objectManager->create(ProductCustomOptionInterfaceFactory::class); + $this->customOptionValueFactory = $this->objectManager + ->create(ProductCustomOptionValuesInterfaceFactory::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * Test to save option price by store. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_options.php + * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php + * + * @magentoConfigFixture default_store catalog/price/scope 1 + * @magentoConfigFixture secondstore_store catalog/price/scope 1 + * + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + public function testSaveOptionPriceByStore(): void + { + $secondWebsitePrice = 22.0; + $defaultStoreId = $this->storeManager->getStore()->getId(); + $secondStoreId = $this->storeManager->getStore('secondstore')->getId(); + $product = $this->productRepository->get('simple'); + $option = $product->getOptions()[0]; + $defaultPrice = $option->getPrice(); + $option->setPrice($secondWebsitePrice); + $product->setStoreId($secondStoreId); + // set Current store='secondstore' to correctly save product options for 'secondstore' + $this->storeManager->setCurrentStore($secondStoreId); + $this->productRepository->save($product); + $this->storeManager->setCurrentStore($defaultStoreId); + $product = $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID, true); + $option = $product->getOptions()[0]; + $this->assertEquals($defaultPrice, $option->getPrice(), 'Price value by default store is wrong'); + $product = $this->productRepository->get('simple', false, $secondStoreId, true); + $option = $product->getOptions()[0]; + $this->assertEquals($secondWebsitePrice, $option->getPrice(), 'Price value by store_id=1 is wrong'); + } + + /** + * Test add to product custom options with text type. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider productCustomOptionsTypeTextDataProvider + * + * @param array $optionData + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + public function testCreateOptionsWithTypeText(array $optionData): void + { + $option = $this->baseCreateCustomOptionAndAssert($optionData); + $this->assertEquals($optionData['price'], $option->getPrice()); + $this->assertEquals($optionData['price_type'], $option->getPriceType()); + $this->assertEquals($optionData['sku'], $option->getSku()); + + if (isset($optionData['max_characters'])) { + $this->assertEquals($optionData['max_characters'], $option->getMaxCharacters()); + } else { + $this->assertEquals(0, $option->getMaxCharacters()); + } + } + + /** + * Tests removing ineligible characters from file_extension. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider fileExtensionsDataProvider + * + * @param string $rawExtensions + * @param string $expectedExtensions + * @throws NoSuchEntityException + * @throws CouldNotSaveException + * @throws InputException + * @throws StateException + */ + public function testFileExtensions(string $rawExtensions, string $expectedExtensions): void + { + $product = $this->productRepository->get('simple'); + $optionData = [ + 'title' => 'file option', + 'type' => 'file', + 'is_require' => true, + 'sort_order' => 3, + 'price' => 30.0, + 'price_type' => 'percent', + 'sku' => 'sku3', + 'file_extension' => $rawExtensions, + 'image_size_x' => 10, + 'image_size_y' => 20, + ]; + $fileOption = $this->customOptionFactory->create(['data' => $optionData]); + $product->addOption($fileOption); + $this->productRepository->save($product); + $product = $this->productRepository->get('simple'); + $fileOption = $product->getOptions()[0]; + $actualExtensions = $fileOption->getFileExtension(); + $this->assertEquals($expectedExtensions, $actualExtensions); + } + + /** + * Test add to product custom options with select type. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider productCustomOptionsTypeSelectDataProvider + * + * @param array $optionData + * @param array $optionValueData + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + public function testCreateOptionsWithTypeSelect(array $optionData, array $optionValueData): void + { + $optionValue = $this->customOptionValueFactory->create(['data' => $optionValueData]); + $optionData['values'] = [$optionValue]; + $option = $this->baseCreateCustomOptionAndAssert($optionData); + $optionValues = $option->getValues(); + $this->assertCount(1, $optionValues); + $this->assertNotNull($optionValues); + $optionValue = reset($optionValues); + $this->assertEquals($optionValueData['title'], $optionValue->getTitle()); + $this->assertEquals($optionValueData['price'], $optionValue->getPrice()); + $this->assertEquals($optionValueData['price_type'], $optionValue->getPriceType()); + $this->assertEquals($optionValueData['sku'], $optionValue->getSku()); + $this->assertEquals($optionValueData['sort_order'], $optionValue->getSortOrder()); + } + + /** + * Test add to product custom options with date type. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider productCustomOptionsTypeDateDataProvider + * + * @param array $optionData + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + public function testCreateOptionsWithTypeDate(array $optionData): void + { + $option = $this->baseCreateCustomOptionAndAssert($optionData); + $this->assertEquals($optionData['price'], $option->getPrice()); + $this->assertEquals($optionData['price_type'], $option->getPriceType()); + $this->assertEquals($optionData['sku'], $option->getSku()); + } + + /** + * Check that error throws if we save porduct with custom option without some field. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider productCustomOptionsWithErrorDataProvider + * + * @param array $optionData + * @param string $expectedErrorText + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + public function testCreateOptionWithError(array $optionData, string $expectedErrorText): void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); + $createdOption = $this->customOptionFactory->create(['data' => $optionData]); + $product->setOptions([$createdOption]); + $this->expectExceptionMessage($expectedErrorText); + $productRepository->save($product); + } + + /** + * Add option to product with type text data provider. + * + * @return array + */ + public function productCustomOptionsTypeTextDataProvider(): array + { + return [ + 'area_field_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'area_field_not_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'area_field_options_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'area_field_options_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'percent', + ], + ], + 'area_field_options_with_max_charters_configuration' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 30, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'area_field_options_without_max_charters_configuration' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ]; + } + + /** + * Data provider for testFileExtensions. + * + * @return array + */ + public function fileExtensionsDataProvider(): array + { + return [ + ['JPG, PNG, GIF', 'jpg, png, gif'], + ['jpg, jpg, jpg', 'jpg'], + ['jpg, png, gif', 'jpg, png, gif'], + ['jpg png gif', 'jpg, png, gif'], + ['!jpg@png#gif%', 'jpg, png, gif'], + ['jpg, png, 123', 'jpg, png, 123'], + ['', ''], + ]; + } + + /** + * Add option to product with type text data provider. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function productCustomOptionsTypeSelectDataProvider(): array + { + return [ + 'drop_down_field_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'drop_down', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'drop_down_field_not_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'title' => 'Test option 1', + 'type' => 'drop_down', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'drop_down_field_option_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'drop_down', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'drop_down_field_option_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'drop_down', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'percent', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'radio_field_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'radio', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'radio_field_not_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'title' => 'Test option 1', + 'type' => 'radio', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'radio_field_option_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'radio', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'radio_field_option_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'radio', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'percent', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'checkbox_field_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'checkbox', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'checkbox_field_not_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'title' => 'Test option 1', + 'type' => 'checkbox', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'checkbox_field_option_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'checkbox', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'checkbox_field_option_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'checkbox', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'percent', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'multiple_field_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'multiple', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'multiple_field_not_required_option' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'title' => 'Test option 1', + 'type' => 'multiple', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'multiple_field_option_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'multiple', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + 'multiple_field_option_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option 1', + 'type' => 'multiple', + 'price_type' => 'fixed', + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'percent', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + ]; + } + + /** + * Add option to product with type text data provider. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function productCustomOptionsTypeDateDataProvider(): array + { + return [ + 'date_field_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_field_not_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_field_options_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_field_options_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date', + 'price' => 10, + 'price_type' => 'percent', + ], + ], + 'date_time_field_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date_time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_time_field_not_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date_time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_time_field_options_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date_time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'date_time_field_options_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'date_time', + 'price' => 10, + 'price_type' => 'percent', + ], + ], + 'time_field_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'time_field_not_required_options' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'time_field_options_with_fixed_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'time', + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + 'time_field_options_with_percent_price' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => 'time', + 'price' => 10, + 'price_type' => 'percent', + ], + ], + ]; + } + + /** + * Add option to product for get option save error data provider. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function productCustomOptionsWithErrorDataProvider(): array + { + return [ + 'error_option_without_product_sku' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + ], + 'The ProductSku is empty. Set the ProductSku and try again.', + ], + 'error_option_without_type' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'price' => 10, + 'price_type' => 'fixed', + 'product_sku' => 'simple', + ], + "Missed values for option required fields\nInvalid option type", + ], + 'error_option_wrong_price_type' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'test_wrong_price_type', + 'product_sku' => 'simple', + ], + 'Invalid option value', + ], + 'error_option_without_price_type' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price' => 10, + 'product_sku' => 'simple', + ], + 'Invalid option value', + ], + 'error_option_without_price_value' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => 'area', + 'price_type' => 'fixed', + 'product_sku' => 'simple', + ], + 'Invalid option value', + ], + 'error_option_without_title' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + 'product_sku' => 'simple', + ], + "Missed values for option required fields", + ], + 'error_option_with_empty_title' => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => '', + 'type' => 'area', + 'price' => 10, + 'price_type' => 'fixed', + 'product_sku' => 'simple', + ], + "Missed values for option required fields", + ], + ]; + } + + /** + * Delete all custom options from product. + */ + protected function tearDown(): void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var ProductCustomOptionRepositoryInterface $optionRepository */ + $optionRepository = $this->objectManager->create(ProductCustomOptionRepositoryInterface::class); + try { + $product = $productRepository->get('simple'); + foreach ($optionRepository->getProductOptions($product) as $customOption) { + $optionRepository->delete($customOption); + } + } catch (\Exception $e) { + } + + parent::tearDown(); + } + + /** + * Create custom option and save product with created option, check base assertions. + * + * @param array $optionData + * @return ProductCustomOptionInterface + * @throws CouldNotSaveException + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + */ + private function baseCreateCustomOptionAndAssert(array $optionData): ProductCustomOptionInterface + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var ProductCustomOptionRepositoryInterface $optionRepository */ + $optionRepository = $this->objectManager->create(ProductCustomOptionRepositoryInterface::class); + $product = $productRepository->get('simple'); + $createdOption = $this->customOptionFactory->create(['data' => $optionData]); + $createdOption->setProductSku($product->getSku()); + $product->setOptions([$createdOption]); + $productRepository->save($product); + $productCustomOptions = $optionRepository->getProductOptions($product); + $this->assertCount(1, $productCustomOptions); + $option = reset($productCustomOptions); + $this->assertEquals($optionData['title'], $option->getTitle()); + $this->assertEquals($optionData['type'], $option->getType()); + $this->assertEquals($optionData['is_require'], $option->getIsRequire()); + + return $option; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php deleted file mode 100644 index bfb49686447c0..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Model\Product; - -use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; - -/** - * @magentoAppArea adminhtml - * @magentoAppIsolation enabled - * @magentoDbIsolation enabled - */ -class OptionTest extends \PHPUnit\Framework\TestCase -{ - /** - * Product repository. - * - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * Custom option factory. - * - * @var ProductCustomOptionInterfaceFactory - */ - private $customOptionFactory; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); - $this->customOptionFactory = Bootstrap::getObjectManager()->create(ProductCustomOptionInterfaceFactory::class); - $this->storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); - } - - /** - * Tests removing ineligible characters from file_extension. - * - * @param string $rawExtensions - * @param string $expectedExtensions - * @dataProvider fileExtensionsDataProvider - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - */ - public function testFileExtensions(string $rawExtensions, string $expectedExtensions) - { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->get('simple'); - /** @var \Magento\Catalog\Model\Product\Option $fileOption */ - $fileOption = $this->createFileOption($rawExtensions); - $product->addOption($fileOption); - $product->save(); - $product = $this->productRepository->get('simple'); - $fileOption = $product->getOptions()[0]; - $actualExtensions = $fileOption->getFileExtension(); - $this->assertEquals($expectedExtensions, $actualExtensions); - } - - /** - * Data provider for testFileExtensions. - * - * @return array - */ - public function fileExtensionsDataProvider() - { - return [ - ['JPG, PNG, GIF', 'jpg, png, gif'], - ['jpg, jpg, jpg', 'jpg'], - ['jpg, png, gif', 'jpg, png, gif'], - ['jpg png gif', 'jpg, png, gif'], - ['!jpg@png#gif%', 'jpg, png, gif'], - ['jpg, png, 123', 'jpg, png, 123'], - ['', ''], - ]; - } - - /** - * Create file type option for product. - * - * @param string $rawExtensions - * @return \Magento\Catalog\Api\Data\ProductCustomOptionInterface|void - */ - private function createFileOption(string $rawExtensions) - { - $data = [ - 'title' => 'file option', - 'type' => 'file', - 'is_require' => true, - 'sort_order' => 3, - 'price' => 30.0, - 'price_type' => 'percent', - 'sku' => 'sku3', - 'file_extension' => $rawExtensions, - 'image_size_x' => 10, - 'image_size_y' => 20, - ]; - - return $this->customOptionFactory->create(['data' => $data]); - } - - /** - * Test to save option price by store - * - * @magentoDataFixture Magento/Catalog/_files/product_with_options.php - * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php - * @magentoConfigFixture default_store catalog/price/scope 1 - * @magentoConfigFixture secondstore_store catalog/price/scope 1 - */ - public function testSaveOptionPriceByStore() - { - $secondWebsitePrice = 22.0; - $defaultStoreId = $this->storeManager->getStore()->getId(); - $secondStoreId = $this->storeManager->getStore('secondstore')->getId(); - - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->get('simple'); - $option = $product->getOptions()[0]; - $defaultPrice = $option->getPrice(); - - $option->setPrice($secondWebsitePrice); - $product->setStoreId($secondStoreId); - // set Current store='secondstore' to correctly save product options for 'secondstore' - $this->storeManager->setCurrentStore($secondStoreId); - $this->productRepository->save($product); - $this->storeManager->setCurrentStore($defaultStoreId); - - $product = $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID, true); - $option = $product->getOptions()[0]; - $this->assertEquals($defaultPrice, $option->getPrice(), 'Price value by default store is wrong'); - - $product = $this->productRepository->get('simple', false, $secondStoreId, true); - $option = $product->getOptions()[0]; - $this->assertEquals($secondWebsitePrice, $option->getPrice(), 'Price value by store_id=1 is wrong'); - } -} From 9f77a81dc7921077a0edd600d18d51a95115d002 Mon Sep 17 00:00:00 2001 From: Ivan Koliadynskyy <i.koliadynskyy@gmail.com> Date: Fri, 4 Oct 2019 18:07:41 +0300 Subject: [PATCH 0806/2437] Add use of NoSuchEntityException exception class with namespace in test code. --- .../Quote/Test/Unit/Model/QuoteManagementTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index 85b030d9d1291..c72dc1979e0e8 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -10,7 +10,6 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Customer; use Magento\Framework\App\RequestInterface; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress; use Magento\Quote\Model\CustomerManagement; @@ -315,7 +314,7 @@ public function testCreateEmptyCartForCustomer() ->expects($this->once()) ->method('getActiveForCustomer') ->with($userId) - ->willThrowException(new NoSuchEntityException()); + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); $customer = $this->getMockBuilder(CustomerInterface::class) ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); $quoteAddress = $this->createPartialMock( @@ -466,7 +465,7 @@ public function testAssignCustomerToNonanonymousCart() } /** - * @expectedException NoSuchEntityException + * @expectedException \Magento\Framework\Exception\NoSuchEntityException */ public function testAssignCustomerNoSuchCustomer() { @@ -489,7 +488,7 @@ public function testAssignCustomerNoSuchCustomer() ->expects($this->once()) ->method('getById') ->with($customerId) - ->willThrowException(new NoSuchEntityException()); + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); $this->expectExceptionMessage( "No such entity." @@ -545,7 +544,7 @@ public function testAssignCustomer() ->expects($this->once()) ->method('getForCustomer') ->with($customerId) - ->willThrowException(new NoSuchEntityException()); + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); $activeQuoteMock = $this->createPartialMock( Quote::class, @@ -1001,7 +1000,7 @@ protected function prepareOrderFactory( } /** - * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCartForCustomer() { From 1f6883cbe1bbb764fa7c21a2dbd098d04eaa1b0f Mon Sep 17 00:00:00 2001 From: Cari Spruiell <spruiell@adobe.com> Date: Fri, 4 Oct 2019 10:13:16 -0500 Subject: [PATCH 0807/2437] PB-27: Resolve & improve GraphQL error with Products widget - update performance benchmark with new test --- setup/performance-toolkit/benchmark.jmx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 00ae0bdbb8a47..fe4a424f2de8d 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -504,6 +504,11 @@ <stringProp name="Argument.value">${__P(graphqlGetCmsPageByIdPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="graphqlGetCmsPageWithPageBuilderProductListPercentage" elementType="Argument"> + <stringProp name="Argument.name">graphqlGetCmsPageWithPageBuilderProductListPercentage</stringProp> + <stringProp name="Argument.value">${__P(graphqlGetCmsPageWithPageBuilderProductListPercentage,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="graphqlGetConfigurableProductDetailsByNamePercentage" elementType="Argument"> <stringProp name="Argument.name">graphqlGetConfigurableProductDetailsByNamePercentage</stringProp> <stringProp name="Argument.value">${__P(graphqlGetConfigurableProductDetailsByNamePercentage,0)}</stringProp> From 616a5f482cacb56e01343442bb831b7bbb70f9e9 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Fri, 4 Oct 2019 11:47:11 -0500 Subject: [PATCH 0808/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- app/code/Magento/CatalogRule/etc/indexer.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/etc/indexer.xml b/app/code/Magento/CatalogRule/etc/indexer.xml index 340918ed63531..e648ea567631c 100644 --- a/app/code/Magento/CatalogRule/etc/indexer.xml +++ b/app/code/Magento/CatalogRule/etc/indexer.xml @@ -17,7 +17,6 @@ <indexer id="catalog_product_price"> <dependencies> <indexer id="catalogrule_rule" /> - <indexer id="catalogrule_product" /> </dependencies> </indexer> </config> From 43b3924ec974015f520c3cc7ef0581cbee4cba97 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 4 Oct 2019 12:10:20 -0500 Subject: [PATCH 0809/2437] MC-21459: Check/Money Order Language in Admin order view is always from Default Store View --- .../view/adminhtml/templates/info/checkmo.phtml | 3 ++- .../view/adminhtml/templates/info/pdf/checkmo.phtml | 3 ++- .../view/adminhtml/templates/info/purchaseorder.phtml | 3 ++- .../Payment/view/adminhtml/templates/info/default.phtml | 3 ++- .../Payment/view/adminhtml/templates/info/pdf/default.phtml | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml index 36f9d35327fce..28395f8eeb849 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml @@ -7,8 +7,9 @@ /** * @var $block \Magento\OfflinePayments\Block\Info\Checkmo */ +$paymentTitle = $block->getMethod()->getConfigData('title', $block->getInfo()->getOrder()->getStoreId()); ?> -<?= $block->escapeHtml($block->getMethod()->getTitle()) ?> +<?= $block->escapeHtml($paymentTitle) ?> <?php if ($block->getInfo()->getAdditionalInformation()) : ?> <?php if ($block->getPayableTo()) : ?> <br /><?= $block->escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml index d8d952526e67b..f85a8f8357dd9 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml @@ -7,8 +7,9 @@ /** * @var $block \Magento\OfflinePayments\Block\Info\Checkmo */ +$paymentTitle = $block->getMethod()->getConfigData('title', $block->getInfo()->getOrder()->getStoreId()); ?> -<?= $block->escapeHtml($block->getMethod()->getTitle()) ?> +<?= $block->escapeHtml($paymentTitle) ?> {{pdf_row_separator}} <?php if ($block->getInfo()->getAdditionalInformation()) : ?> {{pdf_row_separator}} diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/purchaseorder.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/purchaseorder.phtml index 2a6de7f0cc356..ae7f654a1350b 100644 --- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/purchaseorder.phtml +++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/purchaseorder.phtml @@ -6,8 +6,9 @@ /** * @var $block \Magento\OfflinePayments\Block\Info\Purchaseorder */ +$paymentTitle = $block->getMethod()->getConfigData('title', $block->getInfo()->getOrder()->getStoreId()); ?> -<div class="order-payment-method-name"><?= $block->escapeHtml($block->getMethod()->getTitle()) ?></div> +<div class="order-payment-method-name"><?= $block->escapeHtml($paymentTitle) ?></div> <table class="data-table admin__table-secondary"> <tr> <th><?= $block->escapeHtml(__('Purchase Order Number')) ?>:</th> diff --git a/app/code/Magento/Payment/view/adminhtml/templates/info/default.phtml b/app/code/Magento/Payment/view/adminhtml/templates/info/default.phtml index 8b9c37f112560..3cd88bddbfb1f 100644 --- a/app/code/Magento/Payment/view/adminhtml/templates/info/default.phtml +++ b/app/code/Magento/Payment/view/adminhtml/templates/info/default.phtml @@ -9,8 +9,9 @@ * @see \Magento\Payment\Block\Info */ $specificInfo = $block->getSpecificInformation(); +$paymentTitle = $block->getMethod()->getConfigData('title', $block->getInfo()->getOrder()->getStoreId()); ?> -<?= $block->escapeHtml($block->getMethod()->getTitle()) ?> +<?= $block->escapeHtml($paymentTitle) ?> <?php if ($specificInfo) : ?> <table class="data-table admin__table-secondary"> diff --git a/app/code/Magento/Payment/view/adminhtml/templates/info/pdf/default.phtml b/app/code/Magento/Payment/view/adminhtml/templates/info/pdf/default.phtml index a8583ea5549fe..54b9e48d07a94 100644 --- a/app/code/Magento/Payment/view/adminhtml/templates/info/pdf/default.phtml +++ b/app/code/Magento/Payment/view/adminhtml/templates/info/pdf/default.phtml @@ -8,8 +8,9 @@ * @see \Magento\Payment\Block\Info * @var \Magento\Payment\Block\Info $block */ +$paymentTitle = $block->getMethod()->getConfigData('title', $block->getInfo()->getOrder()->getStoreId()); ?> -<?= $block->escapeHtml($block->getMethod()->getTitle()) ?>{{pdf_row_separator}} +<?= $block->escapeHtml($paymentTitle) ?>{{pdf_row_separator}} <?php if ($specificInfo = $block->getSpecificInformation()) : ?> <?php foreach ($specificInfo as $label => $value) : ?> From 6496f4e2bff89ee709f45cc16f3f64d31c971cdf Mon Sep 17 00:00:00 2001 From: Roman Kabanov <roman@convert.no> Date: Fri, 4 Oct 2019 09:45:56 +0300 Subject: [PATCH 0810/2437] Correct image params - allow to disable image frame, fix image params hash --- app/code/Magento/Catalog/Helper/Image.php | 12 ++- .../Catalog/Model/View/Asset/Image.php | 10 +-- .../Catalog/Test/Unit/Helper/ImageTest.php | 85 +++++++++++++++++-- .../Test/Unit/Model/View/Asset/ImageTest.php | 53 +++++++++--- 4 files changed, 129 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 9b8d0ad75a8c9..110b798df9df9 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -213,31 +213,29 @@ protected function setImageProperties() // Set 'keep frame' flag $frame = $this->getFrame(); - if (!empty($frame)) { - $this->_getModel()->setKeepFrame($frame); - } + $this->_getModel()->setKeepFrame($frame); // Set 'constrain only' flag $constrain = $this->getAttribute('constrain'); - if (!empty($constrain)) { + if (null !== $constrain) { $this->_getModel()->setConstrainOnly($constrain); } // Set 'keep aspect ratio' flag $aspectRatio = $this->getAttribute('aspect_ratio'); - if (!empty($aspectRatio)) { + if (null !== $aspectRatio) { $this->_getModel()->setKeepAspectRatio($aspectRatio); } // Set 'transparency' flag $transparency = $this->getAttribute('transparency'); - if (!empty($transparency)) { + if (null !== $transparency) { $this->_getModel()->setKeepTransparency($transparency); } // Set background color $background = $this->getAttribute('background'); - if (!empty($background)) { + if (null !== $background) { $this->_getModel()->setBackgroundColor($background); } diff --git a/app/code/Magento/Catalog/Model/View/Asset/Image.php b/app/code/Magento/Catalog/Model/View/Asset/Image.php index dfae9f4b0da9c..4128b77308208 100644 --- a/app/code/Magento/Catalog/Model/View/Asset/Image.php +++ b/app/code/Magento/Catalog/Model/View/Asset/Image.php @@ -200,11 +200,11 @@ private function convertToReadableFormat($miscParams) $miscParams['image_width'] = 'w:' . ($miscParams['image_width'] ?? 'empty'); $miscParams['quality'] = 'q:' . ($miscParams['quality'] ?? 'empty'); $miscParams['angle'] = 'r:' . ($miscParams['angle'] ?? 'empty'); - $miscParams['keep_aspect_ratio'] = (isset($miscParams['keep_aspect_ratio']) ? '' : 'non') . 'proportional'; - $miscParams['keep_frame'] = (isset($miscParams['keep_frame']) ? '' : 'no') . 'frame'; - $miscParams['keep_transparency'] = (isset($miscParams['keep_transparency']) ? '' : 'no') . 'transparency'; - $miscParams['constrain_only'] = (isset($miscParams['constrain_only']) ? 'do' : 'not') . 'constrainonly'; - $miscParams['background'] = isset($miscParams['background']) + $miscParams['keep_aspect_ratio'] = (!empty($miscParams['keep_aspect_ratio']) ? '' : 'non') . 'proportional'; + $miscParams['keep_frame'] = (!empty($miscParams['keep_frame']) ? '' : 'no') . 'frame'; + $miscParams['keep_transparency'] = (!empty($miscParams['keep_transparency']) ? '' : 'no') . 'transparency'; + $miscParams['constrain_only'] = (!empty($miscParams['constrain_only']) ? 'do' : 'not') . 'constrainonly'; + $miscParams['background'] = !empty($miscParams['background']) ? 'rgb' . implode(',', $miscParams['background']) : 'nobackground'; return $miscParams; diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php index 2759371dc96e7..bddf969b83229 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php @@ -29,6 +29,11 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ protected $assetRepository; + /** + * @var \Magento\Framework\Config\View|\PHPUnit\Framework\MockObject\MockObject + */ + protected $configView; + /** * @var \Magento\Framework\View\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -58,6 +63,10 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->configView = $this->getMockBuilder(\Magento\Framework\Config\View::class) + ->disableOriginalConstructor() + ->getMock(); + $this->viewConfig = $this->getMockBuilder(\Magento\Framework\View\ConfigInterface::class) ->getMockForAbstractClass(); @@ -151,23 +160,89 @@ public function initDataProvider() ]; } + /** + * @param array $data - optional 'frame' key + * @param bool $whiteBorders view config + * @param bool $expectedKeepFrame + * @dataProvider initKeepFrameDataProvider + */ + public function testInitKeepFrame($data, $whiteBorders, $expectedKeepFrame) + { + $imageId = 'test_image_id'; + $attributes = []; + + $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->prepareAttributes($data, $imageId); + + $this->configView->expects(isset($data['frame']) ? $this->never() : $this->once()) + ->method('getVarValue') + ->with('Magento_Catalog', 'product_image_white_borders') + ->willReturn($whiteBorders); + + $this->viewConfig->expects($this->once()) + ->method('getViewConfig') + ->willReturn($this->configView); + + $this->image->expects($this->once()) + ->method('setKeepFrame') + ->with($expectedKeepFrame) + ->willReturnSelf(); + + $this->helper->init($productMock, $imageId, $attributes); + } + + /** + * @return array + */ + public function initKeepFrameDataProvider() + { + return [ + // when frame defined explicitly, it wins + [ + 'mediaImage' => [ + 'frame' => 1, + ], + 'whiteBorders' => true, + 'expected' => true, + ], + [ + 'mediaImage' => [ + 'frame' => 0, + ], + 'whiteBorders' => true, + 'expected' => false, + ], + // when frame is not defined, var is used + [ + 'mediaImage' => [], + 'whiteBorders' => true, + 'expected' => true, + ], + [ + 'mediaImage' => [], + 'whiteBorders' => false, + 'expected' => false, + ], + ]; + } + /** * @param $data * @param $imageId */ protected function prepareAttributes($data, $imageId) { - $configViewMock = $this->getMockBuilder(\Magento\Framework\Config\View::class) - ->disableOriginalConstructor() - ->getMock(); - $configViewMock->expects($this->once()) + $this->configView->expects($this->once()) ->method('getMediaAttributes') ->with('Magento_Catalog', Image::MEDIA_TYPE_CONFIG_NODE, $imageId) ->willReturn($data); $this->viewConfig->expects($this->once()) ->method('getViewConfig') - ->willReturn($configViewMock); + ->willReturn($this->configView); } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php index eb7b70c8a1718..6832d5b3399d7 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/View/Asset/ImageTest.php @@ -7,6 +7,7 @@ use Magento\Catalog\Model\Product\Media\ConfigInterface; use Magento\Catalog\Model\View\Asset\Image; +use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Asset\ContextInterface; @@ -103,9 +104,10 @@ public function testGetContext() /** * @param string $filePath * @param array $miscParams + * @param string $readableParams * @dataProvider getPathDataProvider */ - public function testGetPath($filePath, $miscParams) + public function testGetPath($filePath, $miscParams, $readableParams) { $imageModel = $this->objectManager->getObject( Image::class, @@ -118,11 +120,13 @@ public function testGetPath($filePath, $miscParams) 'miscParams' => $miscParams ] ); - $miscParams['background'] = isset($miscParams['background']) ? implode(',', $miscParams['background']) : ''; $absolutePath = '/var/www/html/magento2ce/pub/media/catalog/product'; - $hashPath = md5(implode('_', $miscParams)); + $hashPath = 'somehash'; $this->context->method('getPath')->willReturn($absolutePath); - $this->encryptor->method('hash')->willReturn($hashPath); + $this->encryptor->expects(static::once()) + ->method('hash') + ->with($readableParams, $this->anything()) + ->willReturn($hashPath); static::assertEquals( $absolutePath . '/cache/'. $hashPath . $filePath, $imageModel->getPath() @@ -132,9 +136,10 @@ public function testGetPath($filePath, $miscParams) /** * @param string $filePath * @param array $miscParams + * @param string $readableParams * @dataProvider getPathDataProvider */ - public function testGetUrl($filePath, $miscParams) + public function testGetUrl($filePath, $miscParams, $readableParams) { $imageModel = $this->objectManager->getObject( Image::class, @@ -147,11 +152,13 @@ public function testGetUrl($filePath, $miscParams) 'miscParams' => $miscParams ] ); - $miscParams['background'] = isset($miscParams['background']) ? implode(',', $miscParams['background']) : ''; $absolutePath = 'http://localhost/pub/media/catalog/product'; - $hashPath = md5(implode('_', $miscParams)); + $hashPath = 'somehash'; $this->context->expects(static::once())->method('getBaseUrl')->willReturn($absolutePath); - $this->encryptor->expects(static::once())->method('hash')->willReturn($hashPath); + $this->encryptor->expects(static::once()) + ->method('hash') + ->with($readableParams, $this->anything()) + ->willReturn($hashPath); static::assertEquals( $absolutePath . '/cache/' . $hashPath . $filePath, $imageModel->getUrl() @@ -166,7 +173,8 @@ public function getPathDataProvider() return [ [ '/some_file.png', - [], //default value for miscParams + [], //default value for miscParams, + 'h:empty_w:empty_q:empty_r:empty_nonproportional_noframe_notransparency_notconstrainonly_nobackground', ], [ '/some_file_2.png', @@ -174,15 +182,32 @@ public function getPathDataProvider() 'image_type' => 'thumbnail', 'image_height' => 75, 'image_width' => 75, - 'keep_aspect_ratio' => 'proportional', - 'keep_frame' => 'frame', - 'keep_transparency' => 'transparency', - 'constrain_only' => 'doconstrainonly', + 'keep_aspect_ratio' => true, + 'keep_frame' => true, + 'keep_transparency' => true, + 'constrain_only' => true, 'background' => [233,1,0], 'angle' => null, 'quality' => 80, ], - ] + 'h:75_w:75_proportional_frame_transparency_doconstrainonly_rgb233,1,0_r:empty_q:80', + ], + [ + '/some_file_3.png', + [ + 'image_type' => 'thumbnail', + 'image_height' => 75, + 'image_width' => 75, + 'keep_aspect_ratio' => false, + 'keep_frame' => false, + 'keep_transparency' => false, + 'constrain_only' => false, + 'background' => [233,1,0], + 'angle' => 90, + 'quality' => 80, + ], + 'h:75_w:75_nonproportional_noframe_notransparency_notconstrainonly_rgb233,1,0_r:90_q:80', + ], ]; } } From cfbe6eeb08d0dead5443bb249c00aac43fffc1e2 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Fri, 4 Oct 2019 13:33:37 -0500 Subject: [PATCH 0811/2437] PB-41: Multiple AJAX requests while searching in ui-select in PageBuilder - Debounce requests using Knockout's notifyWhenChangesStop for Product - Product is the only UrlInput type with searchUrl --- app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php b/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php index be73940237db4..453da64dc5b23 100644 --- a/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php +++ b/app/code/Magento/Catalog/Ui/Component/UrlInput/Product.php @@ -46,6 +46,7 @@ public function getConfig(): array 'template' => 'ui/grid/filters/elements/ui-select', 'searchUrl' => $this->urlBuilder->getUrl('catalog/product/search'), 'filterPlaceholder' => __('Product Name or SKU'), + 'filterRateLimitMethod' => 'notifyWhenChangesStop', 'isDisplayEmptyPlaceholder' => true, 'emptyOptionsHtml' => __('Start typing to find products'), 'missingValuePlaceholder' => __('Product with ID: %s doesn\'t exist'), From 65a6eb46a3385e360e47495570158134e3565944 Mon Sep 17 00:00:00 2001 From: Stsiapan Korf <Stsiapan_Korf@epam.com> Date: Fri, 4 Oct 2019 21:25:18 +0300 Subject: [PATCH 0812/2437] MC-18826: Increase test coverage for Cart & Checkout and Order Processing functional areas - Fix comments for MC-6405 --- ...nGroup.xml => StorefrontSetShippingMethodActionGroup.xml} | 5 ++++- .../StorefrontDisplayTableRatesShippingMethodForAETest.xml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) rename app/code/Magento/Shipping/Test/Mftf/ActionGroup/{StorefrontShipmentActionGroup.xml => StorefrontSetShippingMethodActionGroup.xml} (75%) diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontSetShippingMethodActionGroup.xml similarity index 75% rename from app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml rename to app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontSetShippingMethodActionGroup.xml index 6e75ed9217171..7fdfe6d88b8e6 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontShipmentActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/StorefrontSetShippingMethodActionGroup.xml @@ -8,7 +8,10 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SetShippingMethodActionGroup"> + <actionGroup name="StorefrontSetShippingMethodActionGroup"> + <annotations> + <description>Selects the provided Shipping Method on checkout shipping and wait loading mask.</description> + </annotations> <arguments> <argument name="shippingMethodName" type="string" defaultValue="Flat Rate"/> </arguments> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml index a93377c694df8..bb29a4a28bcf6 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -63,7 +63,7 @@ <actionGroup ref="AssertStoreFrontShippingMethodAvailableActionGroup" stepKey="assertShippingMethodAvailable"> <argument name="shippingMethodName" value="Best Way"/> </actionGroup> - <actionGroup ref="SetShippingMethodActionGroup" stepKey="setShippingMethodTableRate"> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodTableRate"> <argument name="shippingMethodName" value="Best Way"/> </actionGroup> <!--Proceed to Review and Payments section--> From 1f8379f8848e0af6bc9778cec1d9d59a479634cb Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 4 Oct 2019 14:04:20 -0500 Subject: [PATCH 0813/2437] MC-18685: Remove custom layout updates from admin --- .../Magento/Install/Test/TestCase/InstallTest.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php index b219305a7be65..42c3a5a0ed117 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php +++ b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php @@ -184,7 +184,18 @@ public function test( // Step 3: Web Configuration. $assertAdminUri->processAssert($this->installPage); $this->installPage->getWebConfigBlock()->clickAdvancedOptions(); - $this->installPage->getWebConfigBlock()->fill($installConfig); + //Waiting for modules list to show and fill it out + $browser->waitUntil( + function () use ($installConfig) { + try { + $this->installPage->getWebConfigBlock()->fill($installConfig); + return true; + } catch (\Throwable $exception) { + //Modules list is not yet loaded, waiting some more + return false; + } + } + ); $this->installPage->getWebConfigBlock()->clickNext(); // Step 4: Customize Your Store $this->installPage->getCustomizeStoreBlock()->fill($installConfig); From f582c7598251002b5f3ee62c6762cda7f8cfa6d8 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 4 Oct 2019 14:35:19 -0500 Subject: [PATCH 0814/2437] MC-21481: TransportBuilder doesn't add "to" email-addresses, if given in array --- .../Mail/Template/TransportBuilderMock.php | 3 +- .../Mail/TransportInterfaceMock.php | 11 +- .../Email/Model/_files/email_template.php | 6 +- .../Framework/Mail/TransportBuilderTest.php | 100 ++++++++++++++++++ 4 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Mail/TransportBuilderTest.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php b/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php index 9f697a1be6339..3f236821b24a4 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php @@ -38,11 +38,12 @@ public function getSentMessage() * Return transport mock. * * @return \Magento\TestFramework\Mail\TransportInterfaceMock + * @throws \Magento\Framework\Exception\LocalizedException */ public function getTransport() { $this->prepareMessage(); $this->reset(); - return new \Magento\TestFramework\Mail\TransportInterfaceMock(); + return new \Magento\TestFramework\Mail\TransportInterfaceMock($this->message); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php index 8f967b501a59f..58f031813b48f 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php @@ -8,6 +8,13 @@ class TransportInterfaceMock implements \Magento\Framework\Mail\TransportInterface { + private $message; + + public function __construct($message = '') + { + $this->message = $message; + } + /** * Mock of send a mail using transport * @@ -21,10 +28,10 @@ public function sendMessage() /** * Get message * - * @return string + * @return mixed */ public function getMessage() { - return ''; + return $this->message; } } diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template.php index bbbcc3133d4ba..6d5f760d7894d 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template.php @@ -10,9 +10,9 @@ $template->setOptions(['area' => 'test area', 'store' => 1]); $template->setData( [ - 'template_text' => - file_get_contents(__DIR__ . '/template_fixture.html'), - 'template_code' => \Magento\Theme\Model\Config\ValidatorTest::TEMPLATE_CODE + 'template_text' => file_get_contents(__DIR__ . '/template_fixture.html'), + 'template_code' => \Magento\Theme\Model\Config\ValidatorTest::TEMPLATE_CODE, + 'template_type' => \Magento\Email\Model\Template::TYPE_TEXT ] ); $template->save(); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Mail/TransportBuilderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Mail/TransportBuilderTest.php new file mode 100644 index 0000000000000..03bdc9a365526 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Mail/TransportBuilderTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Framework\Mail; + +use Magento\Email\Model\BackendTemplate; +use Magento\Email\Model\Template; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class EmailMessageTest + */ +class TransportBuilderTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $di; + + /** + * @var TransportBuilder + */ + protected $builder; + + /** + * @var Template + */ + protected $template; + + protected function setUp() + { + $this->di = Bootstrap::getObjectManager(); + $this->builder = $this->di->get(TransportBuilder::class); + $this->template = $this->di->get(Template::class); + } + + /** + * @magentoDataFixture Magento/Email/Model/_files/email_template.php + * @magentoDbIsolation enabled + * + * @param string|array $email + * @dataProvider emailDataProvider + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testAddToEmail($email) + { + $templateId = $this->template->load('email_exception_fixture', 'template_code')->getId(); + + $this->builder->setTemplateModel(BackendTemplate::class); + + $vars = ['reason' => 'Reason', 'customer' => 'Customer']; + $options = ['area' => 'frontend', 'store' => 1]; + $this->builder->setTemplateIdentifier($templateId)->setTemplateVars($vars)->setTemplateOptions($options); + + $this->builder->addTo($email); + + /** @var EmailMessage $emailMessage */ + $emailMessage = $this->builder->getTransport(); + + $addresses = $emailMessage->getMessage()->getTo(); + + $emails = []; + /** @var Address $toAddress */ + foreach ($addresses as $address) { + $emails[] = $address->getEmail(); + } + + if (is_string($email)) { + $this->assertCount(1, $emails); + $this->assertEquals($email, $emails[0]); + } else { + $this->assertEquals($email, $emails); + } + } + + /** + * @return array + */ + public function emailDataProvider(): array + { + return [ + [ + 'billy.everything@someserver.com', + ], + [ + [ + 'billy.everything@someserver.com', + 'john.doe@someserver.com', + ] + ] + ]; + } +} From 6ff21a67ba555b2dc24b677a0368085e976a457f Mon Sep 17 00:00:00 2001 From: Ivan Koliadynskyy <i.koliadynskyy@gmail.com> Date: Fri, 4 Oct 2019 22:46:25 +0300 Subject: [PATCH 0815/2437] Fix for Static Tests build --- app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index c72dc1979e0e8..59b42b7376597 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -495,7 +495,6 @@ public function testAssignCustomerNoSuchCustomer() ); $this->model->assignCustomer($cartId, $customerId, $storeId); - } public function testAssignCustomer() From de79f954f25d1189993274529763e528f5ac93f0 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 4 Oct 2019 14:58:02 -0500 Subject: [PATCH 0816/2437] MC-21491: Pricing :: FPT display settings - implement schema & resolver --- .../Model/Resolver/StoreConfig.php | 115 ++++++++++++++++++ .../Magento/WeeeGraphQl/etc/schema.graphqls | 14 +++ 2 files changed, 129 insertions(+) create mode 100644 app/code/Magento/WeeeGraphQl/Model/Resolver/StoreConfig.php diff --git a/app/code/Magento/WeeeGraphQl/Model/Resolver/StoreConfig.php b/app/code/Magento/WeeeGraphQl/Model/Resolver/StoreConfig.php new file mode 100644 index 0000000000000..49c034185d806 --- /dev/null +++ b/app/code/Magento/WeeeGraphQl/Model/Resolver/StoreConfig.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WeeeGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Weee\Helper\Data; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Weee\Model\Tax as WeeeDisplayConfig; +use Magento\Framework\Pricing\Render; + +class StoreConfig implements ResolverInterface +{ + /** + * @var string + */ + private static $weeeDisplaySettingsNone = 'NONE'; + + /** + * @var array + */ + private static $weeeDisplaySettings = [ + WeeeDisplayConfig::DISPLAY_INCL => 'INCLUDING_FPT', + WeeeDisplayConfig::DISPLAY_INCL_DESCR => 'INCLUDING_FPT_AND_FPT_DESCRIPTION', + WeeeDisplayConfig::DISPLAY_EXCL_DESCR_INCL => 'EXCLUDING_FPT_INCLUDING_FPT_AND_FPT_DESCRIPTION', + WeeeDisplayConfig::DISPLAY_EXCL => 'EXCLUDING_FPT' + ]; + + /** + * @var Data + */ + private $weeeHelper; + + /** + * @var TaxHelper + */ + private $taxHelper; + + /** + * @var array + */ + private $computedFptSettings = []; + + /** + * @param Data $weeeHelper + * @param TaxHelper $taxHelper + */ + public function __construct(Data $weeeHelper, TaxHelper $taxHelper) + { + $this->weeeHelper = $weeeHelper; + $this->taxHelper = $taxHelper; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (empty($this->computedFptSettings)) { + /** @var StoreInterface $store */ + $store = $context->getExtensionAttributes()->getStore(); + $storeId = (int)$store->getId(); + + $this->computedFptSettings = [ + 'product_fixed_product_tax_display_setting' => self::$weeeDisplaySettingsNone, + 'category_fixed_product_tax_display_setting' => self::$weeeDisplaySettingsNone, + 'sales_fixed_product_tax_display_setting' => self::$weeeDisplaySettingsNone, + ]; + if ($this->weeeHelper->isEnabled($store)) { + $productFptDisplay = $this->getWeeDisplaySettingsByZone(Render::ZONE_ITEM_VIEW, $storeId); + $categoryFptDisplay = $this->getWeeDisplaySettingsByZone(Render::ZONE_ITEM_LIST, $storeId); + $salesModulesFptDisplay = $this->getWeeDisplaySettingsByZone(Render::ZONE_SALES, $storeId); + + $this->computedFptSettings = [ + 'product_fixed_product_tax_display_setting' => self::$weeeDisplaySettings[$productFptDisplay] ?? + self::$weeeDisplaySettingsNone, + 'category_fixed_product_tax_display_setting' => self::$weeeDisplaySettings[$categoryFptDisplay] ?? + self::$weeeDisplaySettingsNone, + 'sales_fixed_product_tax_display_setting' => self::$weeeDisplaySettings[$salesModulesFptDisplay] ?? + self::$weeeDisplaySettingsNone, + ]; + } + } + + return $this->computedFptSettings[$info->fieldName] ?? null; + } + + /** + * Get the weee system display setting + * + * @param string $zone + * @param string $storeId + * @return string + */ + private function getWeeDisplaySettingsByZone(string $zone, int $storeId): int + { + return (int) $this->weeeHelper->typeOfDisplay( + null, + $zone, + $storeId + ); + } +} diff --git a/app/code/Magento/WeeeGraphQl/etc/schema.graphqls b/app/code/Magento/WeeeGraphQl/etc/schema.graphqls index 95ca88ee7a997..0d29703289c50 100644 --- a/app/code/Magento/WeeeGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WeeeGraphQl/etc/schema.graphqls @@ -14,3 +14,17 @@ type FixedProductTax @doc(description: "A single FPT that can be applied to a pr amount: Money @doc(description: "Amount of the FPT as a money object.") label: String @doc(description: "The label assigned to the FPT to be displayed on the frontend.") } + +type StoreConfig { + product_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") + category_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") + sales_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") +} + +enum FixedProductTaxDisplaySettings @doc(description: "This enumeration display settings for the fixed product tax") { + INCLUDING_FPT + INCLUDING_FPT_AND_FPT_DESCRIPTION + EXCLUDING_FPT_INCLUDING_FPT_AND_FPT_DESCRIPTION + EXCLUDING_FPT + NONE +} From 44fc4c0a3e0b829b8b6d6c7f940249f0bcce3c39 Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 4 Oct 2019 15:39:48 -0500 Subject: [PATCH 0817/2437] MC-21481: TransportBuilder doesn't add "to" email-addresses, if given in array --- .../TestFramework/Mail/Template/TransportBuilderMock.php | 3 +++ .../Magento/TestFramework/Mail/TransportInterfaceMock.php | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php b/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php index 3f236821b24a4..cd9512c227893 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Mail/Template/TransportBuilderMock.php @@ -6,6 +6,9 @@ namespace Magento\TestFramework\Mail\Template; +/** + * Class TransportBuilderMock + */ class TransportBuilderMock extends \Magento\Framework\Mail\Template\TransportBuilder { /** diff --git a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php index 58f031813b48f..4cc9533a0e2de 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php @@ -6,10 +6,18 @@ namespace Magento\TestFramework\Mail; +/** + * Class TransportInterfaceMock + */ class TransportInterfaceMock implements \Magento\Framework\Mail\TransportInterface { private $message; + /** + * TransportInterfaceMock constructor. + * + * @param string $message + */ public function __construct($message = '') { $this->message = $message; From 0a308f0a7663d490d8256def18a41e69a63d57fd Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 4 Oct 2019 15:52:15 -0500 Subject: [PATCH 0818/2437] MC-21459: Check/Money Order Language in Admin order view is always from Default Store View --- .../Magento/Payment/Block/InfoTest.php | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Payment/Block/InfoTest.php b/dev/tests/integration/testsuite/Magento/Payment/Block/InfoTest.php index 3bd966018b945..ff4f3f8a58bcf 100644 --- a/dev/tests/integration/testsuite/Magento/Payment/Block/InfoTest.php +++ b/dev/tests/integration/testsuite/Magento/Payment/Block/InfoTest.php @@ -5,9 +5,24 @@ */ namespace Magento\Payment\Block; +use Magento\Framework\View\Element\Text; +use Magento\Framework\View\LayoutInterface; +use Magento\OfflinePayments\Model\Banktransfer; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\Payment\Block\Info as BlockInfo; +use Magento\Payment\Block\Info\Instructions; +use Magento\Payment\Model\Info; +use Magento\Sales\Model\Order; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Class InfoTest + */ class InfoTest extends \PHPUnit\Framework\TestCase { /** + * Tests payment info block. + * * @magentoConfigFixture current_store payment/banktransfer/title Bank Method Title * @magentoConfigFixture current_store payment/checkmo/title Checkmo Title Of The Method * @magentoAppArea adminhtml @@ -15,37 +30,32 @@ class InfoTest extends \PHPUnit\Framework\TestCase public function testGetChildPdfAsArray() { /** @var $layout \Magento\Framework\View\Layout */ - $layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - ); - $block = $layout->createBlock(\Magento\Payment\Block\Info::class, 'block'); + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); + $block = $layout->createBlock(BlockInfo::class, 'block'); - /** @var $paymentInfoBank \Magento\Payment\Model\Info */ - $paymentInfoBank = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Payment\Model\Info::class - ); - $paymentInfoBank->setMethodInstance( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\OfflinePayments\Model\Banktransfer::class - ) + /** @var $paymentInfoBank Info */ + $paymentInfoBank = Bootstrap::getObjectManager()->create( + Info::class ); - /** @var $childBank \Magento\Payment\Block\Info\Instructions */ - $childBank = $layout->addBlock(\Magento\Payment\Block\Info\Instructions::class, 'child.one', 'block'); + $order = Bootstrap::getObjectManager()->create(Order::class); + $banktransferPayment = Bootstrap::getObjectManager()->create(Banktransfer::class); + $paymentInfoBank->setMethodInstance($banktransferPayment); + $paymentInfoBank->setOrder($order); + /** @var $childBank Instructions */ + $childBank = $layout->addBlock(Instructions::class, 'child.one', 'block'); $childBank->setInfo($paymentInfoBank); $nonExpectedHtml = 'non-expected html'; - $childHtml = $layout->addBlock(\Magento\Framework\View\Element\Text::class, 'child.html', 'block'); + $childHtml = $layout->addBlock(Text::class, 'child.html', 'block'); $childHtml->setText($nonExpectedHtml); - /** @var $paymentInfoCheckmo \Magento\Payment\Model\Info */ - $paymentInfoCheckmo = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Payment\Model\Info::class - ); - $paymentInfoCheckmo->setMethodInstance( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\OfflinePayments\Model\Checkmo::class - ) + /** @var $paymentInfoCheckmo Info */ + $paymentInfoCheckmo = Bootstrap::getObjectManager()->create( + Info::class ); + $checkmoPayment = Bootstrap::getObjectManager()->create(Checkmo::class); + $paymentInfoCheckmo->setMethodInstance($checkmoPayment); + $paymentInfoCheckmo->setOrder($order); /** @var $childCheckmo \Magento\OfflinePayments\Block\Info\Checkmo */ $childCheckmo = $layout->addBlock( \Magento\OfflinePayments\Block\Info\Checkmo::class, From 379ca7e401387ca487d7a26c733705fde6f386a8 Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 4 Oct 2019 16:02:48 -0500 Subject: [PATCH 0819/2437] MC-20456: Category filtering - add api-functional tests --- .../GraphQl/Catalog/CategoryListTest.php | 425 ++++++++++++++++++ .../Magento/Catalog/_files/categories.php | 2 + 2 files changed, 427 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php new file mode 100644 index 0000000000000..cbf09ca5d2e69 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -0,0 +1,425 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test CategoryList GraphQl query + */ +class CategoryListTest extends GraphQlAbstract +{ + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterSingleCategoryDataProvider + * @param $field + * @param $condition + * @param $value + */ + public function testFilterSingleCategoryByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categoryList(filters: { $field : { $condition : "$value" } }){ + id + name + url_key + url_path + children_count + path + position + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $this->assertResponseFields($result['categoryList'][0], $expectedResult); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @dataProvider filterMultipleCategoriesDataProvider + * @param $field + * @param $condition + * @param $value + * @param $expectedResult + */ + public function testFilterMultipleCategoriesByField($field, $condition, $value, $expectedResult) + { + $query = <<<QUERY +{ + categoryList(filters: { $field : { $condition : $value } }){ + id + name + url_key + url_path + children_count + path + position + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(count($expectedResult), $result['categoryList']); + foreach ($expectedResult as $i => $expected) { + $this->assertResponseFields($result['categoryList'][$i], $expected); + } + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterCategoryByMultipleFields() + { + $query = <<<QUERY +{ + categoryList(filters: {ids: {in: ["6","7","8","9","10"]}, name: {match: "Movable"}}){ + id + name + url_key + url_path + children_count + path + position + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(3, $result['categoryList']); + + $expectedCategories = [7 => 'Movable', 9 => 'Movable Position 1', 10 => 'Movable Position 2']; + $actualCategories = array_column($result['categoryList'], 'name', 'id'); + $this->assertEquals($expectedCategories, $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testFilterWithInactiveCategory() + { + $query = <<<QUERY +{ + categoryList(filters: {url_key: {in: ["inactive", "category-2"]}}){ + id + name + url_key + url_path + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $actualCategories = array_column($result['categoryList'], 'url_key', 'id'); + $this->assertContains('category-2', $actualCategories); + $this->assertNotContains('inactive', $actualCategories); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testQueryChildCategoriesWithProducts() + { + $query = <<<QUERY +{ + categoryList(filters: {ids: {in: ["3"]}}){ + id + name + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + } + } + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $a = 3; + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $baseCategory = $result['categoryList'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.1', 'description' => 'Category 1.1 description.'], + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.1', $firstChildCategory['name']); + $this->assertEquals('Category 1.1 description.', $firstChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = [['name' =>'Category 1.1.1']]; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + //Check second child category + $secondChildCategory = $baseCategory['children'][1]; + $this->assertEquals('Category 1.2', $secondChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $secondChildCategory['description']); + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($secondChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($secondChildCategory, $firstChildCategoryChildren); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + */ + public function testNoResultsFound() + { + $query = <<<QUERY +{ + categoryList(filters: {url_key: {in: ["inactive", "does-not-exist"]}}){ + id + name + url_key + url_path + children_count + path + position + } +} +QUERY; + + $result = $this->graphQlQuery($query); + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('categoryList', $result); + $this->assertEquals([], $result['categoryList']); + } + + /** + * @return array + */ + public function filterSingleCategoryDataProvider(): array + { + return [ + [ + 'ids', + 'eq', + '4', + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ] + ], + [ + 'name', + 'match', + 'Movable Position 2', + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ], + [ + 'url_key', + 'eq', + 'category-1-1-1', + [ + 'id' => '5', + 'name' => 'Category 1.1.1', + 'url_key' => 'category-1-1-1', + 'url_path' => 'category-1/category-1-1/category-1-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4/5', + 'position' => '1' + ] + ], + ]; + } + + /** + * @return array + */ + public function filterMultipleCategoriesDataProvider(): array + { + return[ + //Filter by multiple IDs + [ + 'ids', + 'in', + '["4", "9", "10"]', + [ + [ + 'id' => '4', + 'name' => 'Category 1.1', + 'url_key' => 'category-1-1', + 'url_path' => 'category-1/category-1-1', + 'children_count' => '0', + 'path' => '1/2/3/4', + 'position' => '1' + ], + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ] + ] + ], + //Filter by multiple url keys + [ + 'url_key', + 'in', + '["category-1-2", "movable"]', + [ + [ + 'id' => '7', + 'name' => 'Movable', + 'url_key' => 'movable', + 'url_path' => 'movable', + 'children_count' => '0', + 'path' => '1/2/7', + 'position' => '3' + ], + [ + 'id' => '13', + 'name' => 'Category 1.2', + 'url_key' => 'category-1-2', + 'url_path' => 'category-1/category-1-2', + 'children_count' => '0', + 'path' => '1/2/3/13', + 'position' => '2' + ] + ] + ], + //Filter by matching multiple names + [ + 'name', + 'match', + '"Position"', + [ + [ + 'id' => '9', + 'name' => 'Movable Position 1', + 'url_key' => 'movable-position-1', + 'url_path' => 'movable-position-1', + 'children_count' => '0', + 'path' => '1/2/9', + 'position' => '5' + ], + [ + 'id' => '10', + 'name' => 'Movable Position 2', + 'url_key' => 'movable-position-2', + 'url_path' => 'movable-position-2', + 'children_count' => '0', + 'path' => '1/2/10', + 'position' => '6' + ], + [ + 'id' => '11', + 'name' => 'Movable Position 3', + 'url_key' => 'movable-position-3', + 'url_path' => 'movable-position-3', + 'children_count' => '0', + 'path' => '1/2/11', + 'position' => '7' + ] + ] + ] + ]; + } + + /** + * Check category products + * + * @param array $category + * @param array $expectedProducts + */ + private function assertCategoryProducts(array $category, array $expectedProducts) + { + $this->assertEquals(count($expectedProducts), $category['products']['total_count']); + $this->assertCount(count($expectedProducts), $category['products']['items']); + $this->assertResponseFields($category['products']['items'], $expectedProducts); + } + + /** + * Check category child categories + * + * @param array $category + * @param array $expectedChildren + */ + private function assertCategoryChildren(array $category, array $expectedChildren) + { + $this->assertArrayHasKey('children', $category); + $this->assertCount(count($expectedChildren), $category['children']); + foreach ($expectedChildren as $i => $expectedChild) { + $this->assertResponseFields($category['children'][$i], $expectedChild); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php index a5ab961932461..25bb55ffbc32c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php @@ -64,6 +64,7 @@ ->setIsActive(true) ->setIsAnchor(true) ->setPosition(1) + ->setDescription('Category 1.1 description.') ->save(); $category = $objectManager->create(\Magento\Catalog\Model\Category::class); @@ -79,6 +80,7 @@ ->setPosition(1) ->setCustomUseParentSettings(0) ->setCustomDesign('Magento/blank') + ->setDescription('This is the description for Category 1.1.1') ->save(); $category = $objectManager->create(\Magento\Catalog\Model\Category::class); From 3a298f658503b9629d903c81ddc1c616990a634f Mon Sep 17 00:00:00 2001 From: Roman Lytvynenko <lytvynen@adobe.com> Date: Fri, 4 Oct 2019 16:14:00 -0500 Subject: [PATCH 0820/2437] MC-21481: TransportBuilder doesn't add "to" email-addresses, if given in array --- .../Magento/TestFramework/Mail/TransportInterfaceMock.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php index 4cc9533a0e2de..3340608d4a750 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Mail/TransportInterfaceMock.php @@ -30,6 +30,7 @@ public function __construct($message = '') */ public function sendMessage() { + //phpcs:ignore Squiz.PHP.NonExecutableCode.ReturnNotRequired return; } From a505d6dd9a651beffd79bab876e9adedd9a7882d Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Fri, 4 Oct 2019 16:15:02 -0500 Subject: [PATCH 0821/2437] MC-15986: Category Filtering - add changes in category filter --- .../Model/Category/CategoryFilter.php | 41 +++++++++++++++---- .../Model/Resolver/CategoryList.php | 10 +---- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php index 42f35a8711858..8de0d13e51e9a 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Category/CategoryFilter.php @@ -7,7 +7,9 @@ namespace Magento\CatalogGraphQl\Model\Category; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Catalog\Model\ResourceModel\Category\Collection; /** * Category filter allows to filter collection using 'id, url_key, name' from search criteria. @@ -32,24 +34,47 @@ public function __construct( * Filter for filtering the requested categories id's based on url_key, ids, name in the result. * * @param array $args - * @param \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection - * @return bool + * @param Collection $categoryCollection */ public function applyFilters( array $args, - \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection - ): bool { + Collection $categoryCollection + ): void { + $categoryCollection->addAttributeToFilter(CategoryInterface::KEY_IS_ACTIVE, ['eq' => 1]); foreach ($args['filters'] as $field => $cond) { foreach ($cond as $condType => $value) { if ($field === 'ids') { $categoryCollection->addIdFilter($value); - } elseif ($condType === 'match') { - $categoryCollection->addAttributeToFilter($field, ['like' => "%{$value}%"]); } else { - $categoryCollection->addAttributeToFilter($field, [$condType => $value]); + $this->addAttributeFilter($categoryCollection, $field, $condType, $value); } } } - return true; + } + + /** + * @param Collection $categoryCollection + * @param string $field + * @param string $condType + * @param string|array $value + */ + private function addAttributeFilter($categoryCollection, $field, $condType, $value) + { + if ($condType === 'match') { + $this->addMatchFilter($categoryCollection, $field, $value); + return; + } + $categoryCollection->addAttributeToFilter($field, [$condType => $value]); + } + + /** + * + * @param Collection $categoryCollection + * @param string $field + * @param string $value + */ + private function addMatchFilter($categoryCollection, $field, $value) + { + $categoryCollection->addAttributeToFilter($field, ['like' => "%{$value}%"]); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php index 140cb68be6799..5e51791593cb5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryList.php @@ -7,7 +7,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver; -use Magento\Catalog\Model\Category; use Magento\CatalogGraphQl\Model\Resolver\Category\CheckCategoryIsActive; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree; use Magento\Framework\GraphQl\Config\Element\Field; @@ -77,10 +76,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (isset($value[$field->getName()])) { return $value[$field->getName()]; } - $categoryCollection = $this->collectionFactory->create(); - $categoryCollection->addAttributeToFilter('is_active', 1); - $categoryCollection->addAttributeToSelect(['name','url_key', 'ids']); if (!isset($args['filters'])) { $rootCategoryIds = [(int)$context->getExtensionAttributes()->getStore()->getRootCategoryId()]; @@ -91,14 +87,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $rootCategoryIds[] = (int)$category->getId(); } } - $result = []; foreach ($rootCategoryIds as $rootCategoryId) { - if ($rootCategoryId !== Category::TREE_ROOT_ID) { - $this->checkCategoryIsActive->execute($rootCategoryId); - } $categoryTree = $this->categoryTree->getTree($info, $rootCategoryId); - if (empty($categoryTree) || ($categoryTree->count() == 0)) { + if (empty($categoryTree)) { throw new GraphQlNoSuchEntityException(__('Category doesn\'t exist')); } $result[] = current($this->extractDataFromCategoryTree->execute($categoryTree)); From b659839bb3db98050622ccb129728faef7e3e8e4 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 4 Oct 2019 17:02:17 -0500 Subject: [PATCH 0822/2437] MC-21491: Pricing :: FPT display settings - modify docs --- app/code/Magento/WeeeGraphQl/etc/schema.graphqls | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/WeeeGraphQl/etc/schema.graphqls b/app/code/Magento/WeeeGraphQl/etc/schema.graphqls index 0d29703289c50..467b5f701c2f7 100644 --- a/app/code/Magento/WeeeGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WeeeGraphQl/etc/schema.graphqls @@ -16,15 +16,15 @@ type FixedProductTax @doc(description: "A single FPT that can be applied to a pr } type StoreConfig { - product_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") - category_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") - sales_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") + product_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The product page display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") + category_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The category page display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") + sales_fixed_product_tax_display_setting : FixedProductTaxDisplaySettings @doc(description: "The sales modules pages display setting for the fixed product tax") @resolver(class: "Magento\\WeeeGraphQl\\Model\\Resolver\\StoreConfig") } enum FixedProductTaxDisplaySettings @doc(description: "This enumeration display settings for the fixed product tax") { - INCLUDING_FPT - INCLUDING_FPT_AND_FPT_DESCRIPTION - EXCLUDING_FPT_INCLUDING_FPT_AND_FPT_DESCRIPTION - EXCLUDING_FPT - NONE + INCLUDING_FPT @doc(description: "Fixed product tax amount(s) is included into the price and details from fixed_product_taxes should not be displayed") + INCLUDING_FPT_AND_FPT_DESCRIPTION @doc(description: "Fixed product tax amount(s) is included into the price and the details from fixed_product_taxes should be displayed") + EXCLUDING_FPT_INCLUDING_FPT_AND_FPT_DESCRIPTION @doc(description: "Fixed product tax amount(s) is included into the price and the details from fixed_product_taxes should be shown, also price without the amount(s) of fixed_product_taxes should be displayed") + EXCLUDING_FPT @doc(description: "Fixed product tax amount(s) is excluded from the price and details from fixed_product_taxes should not be displayed") + NONE @doc(description: "Fixed product tax feature is disabled and we should not show or query fixed_product_taxes field") } From b35c2693366b0841063548ecdeeb43eff9a550fd Mon Sep 17 00:00:00 2001 From: Daniel Renaud <drenaud@magento.com> Date: Fri, 4 Oct 2019 17:08:58 -0500 Subject: [PATCH 0823/2437] MC-21480: Add caching for CategoryList resolver - integration test --- .../Catalog/CategoryListCacheTest.php | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/GraphQlCache/Controller/Catalog/CategoryListCacheTest.php diff --git a/dev/tests/integration/testsuite/Magento/GraphQlCache/Controller/Catalog/CategoryListCacheTest.php b/dev/tests/integration/testsuite/Magento/GraphQlCache/Controller/Catalog/CategoryListCacheTest.php new file mode 100644 index 0000000000000..b91e552678960 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQlCache/Controller/Catalog/CategoryListCacheTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQlCache\Controller\Catalog; + +use Magento\GraphQlCache\Controller\AbstractGraphqlCacheTest; + +/** + * Test caching works for categoryList query + * + * @magentoAppArea graphql + * @magentoCache full_page enabled + * @magentoDbIsolation disabled + */ +class CategoryListCacheTest extends AbstractGraphqlCacheTest +{ + /** + * Test cache tags are generated + * + * @magentoDataFixture Magento/Catalog/_files/category_product.php + */ + public function testRequestCacheTagsForCategoryList(): void + { + $categoryId ='333'; + $query + = <<<QUERY + { + categoryList(filters: {ids: {in: ["$categoryId"]}}) { + id + name + url_key + description + product_count + } + } +QUERY; + $response = $this->dispatchGraphQlGETRequest(['query' => $query]); + $this->assertEquals('MISS', $response->getHeader('X-Magento-Cache-Debug')->getFieldValue()); + $actualCacheTags = explode(',', $response->getHeader('X-Magento-Tags')->getFieldValue()); + $expectedCacheTags = ['cat_c','cat_c_' . $categoryId, 'FPC']; + $this->assertEquals($expectedCacheTags, $actualCacheTags); + } + + /** + * Test request is served from cache + * + * @magentoDataFixture Magento/Catalog/_files/category_product.php + */ + public function testSecondRequestIsServedFromCache() + { + $categoryId ='333'; + $query + = <<<QUERY + { + categoryList(filters: {ids: {in: ["$categoryId"]}}) { + id + name + url_key + description + product_count + } + } +QUERY; + $expectedCacheTags = ['cat_c','cat_c_' . $categoryId, 'FPC']; + + $response = $this->dispatchGraphQlGETRequest(['query' => $query]); + $this->assertEquals('MISS', $response->getHeader('X-Magento-Cache-Debug')->getFieldValue()); + $actualCacheTags = explode(',', $response->getHeader('X-Magento-Tags')->getFieldValue()); + $this->assertEquals($expectedCacheTags, $actualCacheTags); + + $cacheResponse = $this->dispatchGraphQlGETRequest(['query' => $query]); + $this->assertEquals('HIT', $cacheResponse->getHeader('X-Magento-Cache-Debug')->getFieldValue()); + $actualCacheTags = explode(',', $cacheResponse->getHeader('X-Magento-Tags')->getFieldValue()); + $this->assertEquals($expectedCacheTags, $actualCacheTags); + } +} From aa7665b2278ebfc3c34991b499c8f3492b42613d Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Fri, 4 Oct 2019 17:11:06 -0500 Subject: [PATCH 0824/2437] MC-20534: Migrate remaining failed MFTF tests to SearchEngineMysqlSuite --- .../Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml | 3 +-- .../NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index be5d9dca425a3..21f8e2e070e32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -117,8 +117,7 @@ <waitForPageLoad stepKey="waitforCategoryPageToLoad2"/> <!--Open product display page--> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByNumber('1')}}" stepKey="goToProduct2DisplayPage"/> - <!--<click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityOne.name)}}" stepKey="clickProductToGoProductPage"/>--> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productWithHTMLEntityTwo.name)}}" stepKey="clickProductToGoSecondProductPage"/> <waitForPageLoad stepKey="waitForProductDisplayPageLoad3"/> <!--Verify the breadcrumbs on Product Display page--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml index 3c98f9177f4a7..fd6656b1d1b28 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/NoErrorCartCheckoutForProductsDeletedFromMiniCartTest.xml @@ -26,6 +26,8 @@ <field key="price">100.00</field> <requiredEntity createDataKey="createCategory"/> </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> From 55682ec22817c37ebb29caff5702e47d536b3fde Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 4 Oct 2019 17:31:09 -0500 Subject: [PATCH 0825/2437] MC-18685: Remove custom layout updates from admin --- .../CheckProductsOrderActionGroup.xml | 3 ++ .../Install/Test/TestCase/InstallTest.php | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml index 7fbe71cbee301..536ac347f5768 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml @@ -19,6 +19,9 @@ <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForPageLoad5"/> + <executeJS function="window.localStorage.clear()" step="clearWidgetCache" /> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePageAfterCacheCleared"/> + <waitForPageLoad stepKey="waitForPageLoad5AfterCacheCleared"/> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" userInput="alt" stepKey="grabFirstProductName1_1"/> <assertEquals expected="{{product_1.name}}" actual="($grabFirstProductName1_1)" message="notExpectedOrder" stepKey="compare1"/> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('2')}}" userInput="alt" stepKey="grabFirstProductName2_2"/> diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php index 42c3a5a0ed117..add66b1da3d62 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php +++ b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php @@ -184,21 +184,23 @@ public function test( // Step 3: Web Configuration. $assertAdminUri->processAssert($this->installPage); $this->installPage->getWebConfigBlock()->clickAdvancedOptions(); - //Waiting for modules list to show and fill it out - $browser->waitUntil( - function () use ($installConfig) { - try { - $this->installPage->getWebConfigBlock()->fill($installConfig); - return true; - } catch (\Throwable $exception) { - //Modules list is not yet loaded, waiting some more - return false; - } - } - ); + $this->installPage->getWebConfigBlock()->fill($installConfig); $this->installPage->getWebConfigBlock()->clickNext(); // Step 4: Customize Your Store - $this->installPage->getCustomizeStoreBlock()->fill($installConfig); + //Waiting for block to properly load + \PHPUnit\Framework\Assert::assertTrue( + $browser->waitUntil( + function () use ($installConfig) { + try { + $this->installPage->getCustomizeStoreBlock()->fill($installConfig); + return true; + } catch (\Throwable $exception) { + //Not loaded yet + return false; + } + } + ) + ); $this->installPage->getCustomizeStoreBlock()->clickNext(); // Step 5: Create Admin Account. $this->installPage->getCreateAdminBlock()->fill($user); From d0e2ced7c02a04a32de03d6bad0cb06ff8fc431f Mon Sep 17 00:00:00 2001 From: Ivan Koliadynskyy <i.koliadynskyy@gmail.com> Date: Sat, 5 Oct 2019 04:27:49 +0300 Subject: [PATCH 0826/2437] Fix assignCustomer for API test. Additional unit test for assignCustomer when customer has active cart. --- .../Magento/Quote/Model/QuoteManagement.php | 31 ++--- .../Test/Unit/Model/QuoteManagementTest.php | 107 +++++++++++++++--- 2 files changed, 110 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 1df2cd4d08def..1aea905706cd5 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -301,25 +301,28 @@ public function assignCustomer($cartId, $customerId, $storeId) $customerActiveQuote = $this->quoteRepository->getForCustomer($customerId); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { /** This exception appear when customer have no active cart*/ - $customerActiveQuote = $this->quoteFactory->create(); - $customerActiveQuote->setCustomer($customer); - $customerActiveQuote->setCustomerIsGuest(0); - $customerActiveQuote->setStoreId($storeId); - $customerActiveQuote->setIsActive(true); + $customerActiveQuote = false; } - if ($customerActiveQuote->getIsActive()) { + if ($customerActiveQuote) { /** Merge carts */ - $customerActiveQuote->merge($quote); - $this->quoteRepository->delete($quote); - $this->quoteRepository->save($customerActiveQuote); + $quote->merge($customerActiveQuote); + $this->quoteRepository->delete($customerActiveQuote); + } + $quote->setCustomer($customer); + $quote->setCustomerIsGuest(0); + $quote->setStoreId($storeId); + $quote->setIsActive(1); - return true; - } else { - throw new \Magento\Framework\Exception\NoSuchEntityException( - __("The customer can't be assigned to the cart. No active cart for customer.") - ); + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'quote_id'); + if ($quoteIdMask->getId()) { + $quoteIdMask->delete(); } + + $this->quoteRepository->save($quote); + + return true; } /** diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index 59b42b7376597..e5753c8c27fa2 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -497,16 +497,27 @@ public function testAssignCustomerNoSuchCustomer() $this->model->assignCustomer($cartId, $customerId, $storeId); } - public function testAssignCustomer() + public function testAssignCustomerWithActiveCart() { $cartId = 220; $customerId = 455; $storeId = 5; + $this->getPropertyValue($this->model, 'quoteIdMaskFactory') + ->expects($this->once()) + ->method('create') + ->willReturn($this->quoteIdMock); + $quoteMock = $this->createPartialMock( Quote::class, - ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'merge'] + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setStoreId', 'setIsActive', 'getIsActive', 'merge'] + ); + + $activeQuoteMock = $this->createPartialMock( + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setStoreId', 'setIsActive', 'getIsActive', 'merge'] ); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock @@ -538,29 +549,97 @@ public function testAssignCustomer() ->willReturn([$storeId, 'some store value']); $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(null); - $this->quoteRepositoryMock ->expects($this->once()) ->method('getForCustomer') ->with($customerId) - ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + ->willReturn($activeQuoteMock); - $activeQuoteMock = $this->createPartialMock( + $quoteMock->expects($this->once())->method('merge')->with($activeQuoteMock)->willReturnSelf(); + $this->quoteRepositoryMock->expects($this->once())->method('delete')->with($activeQuoteMock); + + $quoteMock->expects($this->once())->method('setCustomer')->with($customerMock); + $quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $quoteMock->expects($this->once())->method('setStoreId')->with($storeId); + $quoteMock->expects($this->once())->method('setIsActive')->with(1); + + $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); + $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); + $this->quoteIdMock->expects($this->once())->method('delete'); + $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); + + $this->model->assignCustomer($cartId, $customerId, $storeId); + } + + public function testAssignCustomer() + { + $cartId = 220; + $customerId = 455; + $storeId = 5; + $activeQuoteMock = null; + + $this->getPropertyValue($this->model, 'quoteIdMaskFactory') + ->expects($this->once()) + ->method('create') + ->willReturn($this->quoteIdMock); + + $quoteMock = $this->createPartialMock( Quote::class, ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setStoreId', 'setIsActive', 'getIsActive', 'merge'] ); - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($activeQuoteMock); - $activeQuoteMock->expects($this->once())->method('setCustomer')->with($customerMock); - $activeQuoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); - $activeQuoteMock->expects($this->once())->method('setStoreId')->with($storeId); - $activeQuoteMock->expects($this->once())->method('setIsActive')->with(1); + $customerMock = $this->createMock(CustomerInterface::class); + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($quoteMock); + + $this->customerRepositoryMock + ->expects($this->once()) + ->method('getById') + ->with($customerId) + ->willReturn($customerMock); + + $customerModelMock = $this->createPartialMock( + Customer::class, + ['load', 'getSharedStoreIds'] + ); - $activeQuoteMock->expects($this->once())->method('getIsActive')->willReturn(1); - $activeQuoteMock->expects($this->once())->method('merge')->with($quoteMock)->willReturnSelf(); - $this->quoteRepositoryMock->expects($this->once())->method('delete')->with($quoteMock); + $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); - $this->quoteRepositoryMock->expects($this->once())->method('save')->with($activeQuoteMock); + $customerModelMock + ->expects($this->once()) + ->method('load') + ->with($customerId) + ->willReturnSelf(); + + $customerModelMock + ->expects($this->once()) + ->method('getSharedStoreIds') + ->willReturn([$storeId, 'some store value']); + + $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(null); + + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getForCustomer') + ->with($customerId) + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + + $this->assertEquals(false, $activeQuoteMock); + $quoteMock->expects($this->never())->method('merge'); + $this->quoteRepositoryMock->expects($this->never())->method('delete'); + + $quoteMock->expects($this->once())->method('setCustomer')->with($customerMock); + $quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $quoteMock->expects($this->once())->method('setStoreId')->with($storeId); + $quoteMock->expects($this->once())->method('setIsActive')->with(1); + + $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); + $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); + $this->quoteIdMock->expects($this->once())->method('delete'); + $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); $this->model->assignCustomer($cartId, $customerId, $storeId); } From e8cfc5525ffc08915197d7713030f8b7127c819f Mon Sep 17 00:00:00 2001 From: Gabriel Caruso <carusogabriel34@gmail.com> Date: Sat, 5 Oct 2019 16:58:49 +0200 Subject: [PATCH 0827/2437] Remove useless aliasses for imports --- .../ConfigurableProduct/UpdateConfigurableCartItemsTest.php | 2 +- .../GraphQl/Quote/EditQuoteItemWithCustomOptionsTest.php | 2 +- .../_files/product_downloadable_with_custom_options.php | 2 +- .../product_downloadable_with_purchased_separately_links.php | 2 +- ...roduct_downloadable_without_purchased_separately_links.php | 2 +- .../Test/Unit/Rule/Design/AllPurposeActionTest.php | 4 ++-- lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php | 2 +- .../Framework/Api/ExtensionAttribute/JoinProcessor.php | 2 +- .../Framework/Api/ExtensionAttribute/JoinProcessorHelper.php | 2 +- .../Controller/Test/Unit/Router/Route/FactoryTest.php | 2 +- lib/internal/Magento/Framework/ObjectManager/Profiler/Log.php | 2 +- .../Framework/ObjectManager/Test/Unit/Config/CompiledTest.php | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php index 4f4e7ecab6fe3..8f32caa9dcf0f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/UpdateConfigurableCartItemsTest.php @@ -9,7 +9,7 @@ namespace Magento\GraphQl\ConfigurableProduct; use Magento\ConfigurableProduct\Model\Product\Type\Configurable; -use Magento\Framework\Exception\NoSuchEntityException as NoSuchEntityException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; use Magento\Quote\Model\Quote\Item; use Magento\Quote\Model\QuoteFactory; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/EditQuoteItemWithCustomOptionsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/EditQuoteItemWithCustomOptionsTest.php index 62c1ae0dab3c7..d1edf742931c3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/EditQuoteItemWithCustomOptionsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/EditQuoteItemWithCustomOptionsTest.php @@ -9,7 +9,7 @@ use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\Exception\NoSuchEntityException as NoSuchEntityException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Model\Quote\Item; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_custom_options.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_custom_options.php index b5528dd27ee7c..f0a26f8a36d99 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_custom_options.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -use Magento\TestFramework\Helper\Bootstrap as Bootstrap; +use Magento\TestFramework\Helper\Bootstrap; require __DIR__ . '/product_downloadable_with_purchased_separately_links.php'; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_purchased_separately_links.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_purchased_separately_links.php index 6ceb4d90787e1..f8028b2587fa3 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_purchased_separately_links.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_with_purchased_separately_links.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -use Magento\TestFramework\Helper\Bootstrap as Bootstrap; +use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Downloadable\Model\Product\Type as ProductType; use Magento\Catalog\Model\Product\Visibility as ProductVisibility; diff --git a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_without_purchased_separately_links.php b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_without_purchased_separately_links.php index f55261be04ce6..e6540f15b010b 100644 --- a/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_without_purchased_separately_links.php +++ b/dev/tests/integration/testsuite/Magento/Downloadable/_files/product_downloadable_without_purchased_separately_links.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -use Magento\TestFramework\Helper\Bootstrap as Bootstrap; +use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; use Magento\Downloadable\Model\Product\Type as ProductType; use Magento\Catalog\Model\Product\Visibility as ProductVisibility; diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Test/Unit/Rule/Design/AllPurposeActionTest.php b/dev/tests/static/framework/Magento/CodeMessDetector/Test/Unit/Rule/Design/AllPurposeActionTest.php index 408853141e538..ac7e8b507a824 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Test/Unit/Rule/Design/AllPurposeActionTest.php +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Test/Unit/Rule/Design/AllPurposeActionTest.php @@ -11,9 +11,9 @@ use Magento\CodeMessDetector\Rule\Design\AllPurposeAction; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\ActionInterface; -use PHPUnit\Framework\TestCase as TestCase; +use PHPUnit\Framework\TestCase; use PHPUnit_Framework_MockObject_MockObject as MockObject; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker as InvocationMocker; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPMD\Report; use PHPMD\Node\ClassNode; diff --git a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php index af781e19d045d..567216c29dcad 100644 --- a/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php +++ b/lib/internal/Magento/Framework/Acl/Loader/ResourceLoader.php @@ -8,7 +8,7 @@ namespace Magento\Framework\Acl\Loader; use Magento\Framework\Acl; -use Magento\Framework\Acl\AclResource as AclResource; +use Magento\Framework\Acl\AclResource; use Magento\Framework\Acl\AclResource\ProviderInterface; use Magento\Framework\Acl\AclResourceFactory; diff --git a/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessor.php b/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessor.php index 28f053e1afa84..253ed558fa363 100644 --- a/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessor.php +++ b/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessor.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Api\ExtensionAttribute; use Magento\Framework\Api\ExtensionAttribute\Config; -use Magento\Framework\Api\ExtensionAttribute\Config\Converter as Converter; +use Magento\Framework\Api\ExtensionAttribute\Config\Converter; use Magento\Framework\Data\Collection\AbstractDb as DbCollection; use Magento\Framework\Reflection\TypeProcessor; use Magento\Framework\Api\ExtensibleDataInterface; diff --git a/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessorHelper.php b/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessorHelper.php index f49c1fd0b5154..24d04dfa01db9 100644 --- a/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessorHelper.php +++ b/lib/internal/Magento/Framework/Api/ExtensionAttribute/JoinProcessorHelper.php @@ -7,7 +7,7 @@ namespace Magento\Framework\Api\ExtensionAttribute; use Magento\Framework\Api\ExtensionAttribute\Config; -use Magento\Framework\Api\ExtensionAttribute\Config\Converter as Converter; +use Magento\Framework\Api\ExtensionAttribute\Config\Converter; use Magento\Framework\Api\SimpleDataObjectConverter; /** diff --git a/lib/internal/Magento/Framework/Controller/Test/Unit/Router/Route/FactoryTest.php b/lib/internal/Magento/Framework/Controller/Test/Unit/Router/Route/FactoryTest.php index 87adadbd34e3b..df08e77762628 100644 --- a/lib/internal/Magento/Framework/Controller/Test/Unit/Router/Route/FactoryTest.php +++ b/lib/internal/Magento/Framework/Controller/Test/Unit/Router/Route/FactoryTest.php @@ -9,7 +9,7 @@ use \Magento\Framework\Controller\Router\Route\Factory; use Magento\Framework\Controller\Router\Route\Factory as RouteFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class FactoryTest extends \PHPUnit\Framework\TestCase { diff --git a/lib/internal/Magento/Framework/ObjectManager/Profiler/Log.php b/lib/internal/Magento/Framework/ObjectManager/Profiler/Log.php index c1c1e7c17709a..5803b900bd80f 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Profiler/Log.php +++ b/lib/internal/Magento/Framework/ObjectManager/Profiler/Log.php @@ -5,7 +5,7 @@ */ namespace Magento\Framework\ObjectManager\Profiler; -use Magento\Framework\ObjectManager\Profiler\Tree\Item as Item; +use Magento\Framework\ObjectManager\Profiler\Tree\Item; /** * Class Log diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php index 000e9fb529a68..e61d8f089065f 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/CompiledTest.php @@ -6,7 +6,7 @@ namespace Magento\Framework\ObjectManager\Test\Unit\Config; use Magento\Framework\ObjectManager\Config\Compiled; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class CompiledTest extends \PHPUnit\Framework\TestCase { From e737e7df68821f36727437c673e3b8eac35537cd Mon Sep 17 00:00:00 2001 From: Gabriel Caruso <carusogabriel34@gmail.com> Date: Sat, 5 Oct 2019 16:33:09 +0200 Subject: [PATCH 0828/2437] Remove requirement of always available SPL extension Since PHP 5.3.0, the SPL extension is always available and it can not be disabled. Ref: https://php.net/manual/en/spl.installation.php --- composer.json | 1 - composer.lock | 3 +-- .../Framework/Composer/_files/testFromClone/composer.json | 1 - lib/internal/Magento/Framework/composer.json | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/composer.json b/composer.json index fdbfb664c9b1b..f1497b17ecf9b 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,6 @@ "ext-pdo_mysql": "*", "ext-simplexml": "*", "ext-soap": "*", - "ext-spl": "*", "ext-xsl": "*", "ext-zip": "*", "lib-libxml": "*", diff --git a/composer.lock b/composer.lock index 9d6805ac8be48..0ccc8d7482d26 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "856f519091be654f930aa51de44332a7", + "content-hash": "b9aa969dbc121878e5a9ed27aee0c252", "packages": [ { "name": "braintree/braintree_php", @@ -10260,7 +10260,6 @@ "ext-pdo_mysql": "*", "ext-simplexml": "*", "ext-soap": "*", - "ext-spl": "*", "ext-xsl": "*", "ext-zip": "*", "lib-libxml": "*" diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json index fe1d382b361fa..404db202c6e72 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json @@ -17,7 +17,6 @@ "ext-intl": "*", "ext-mcrypt": "*", "ext-simplexml": "*", - "ext-spl": "*", "lib-libxml": "*", "composer/composer": "1.0.0-alpha9", "magento/magento-composer-installer": "*", diff --git a/lib/internal/Magento/Framework/composer.json b/lib/internal/Magento/Framework/composer.json index af2eb913fe3fe..dfbfb5a25debe 100644 --- a/lib/internal/Magento/Framework/composer.json +++ b/lib/internal/Magento/Framework/composer.json @@ -19,7 +19,6 @@ "ext-intl": "*", "ext-openssl": "*", "ext-simplexml": "*", - "ext-spl": "*", "ext-xsl": "*", "ext-bcmath": "*", "lib-libxml": "*", From f4cbd32d8013fc653ed5c5fafc3baf167ec01f04 Mon Sep 17 00:00:00 2001 From: lfolco <me@laurafolco.com> Date: Sat, 5 Oct 2019 17:47:17 -0400 Subject: [PATCH 0829/2437] handle parameter $requiredAttributeIds when getting products (magento/magento2#24483) --- .../Model/Product/Type/Configurable.php | 51 ++++-- .../Model/Product/Type/ConfigurableTest.php | 27 ++++ ...duct_configurable_with_metadescription.php | 145 ++++++++++++++++++ ...igurable_with_metadescription_rollback.php | 39 +++++ 4 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index c60953e33e9eb..5b50cc0ebd5e0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -9,12 +9,14 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Config; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; use Magento\ConfigurableProduct\Model\Product\Type\Collection\SalableProcessor; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Api\SearchCriteriaBuilder; /** * Configurable product type implementation @@ -194,9 +196,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ private $salableProcessor; + /** + * @var ProductAttributeRepositoryInterface|null + */ + private $productAttributeRepository; + + /** + * @var SearchCriteriaBuilder|null + */ + private $searchCriteriaBuilder; + /** * @codingStandardsIgnoreStart/End - * * @param \Magento\Catalog\Model\Product\Option $catalogProductOption * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Catalog\Model\Product\Type $catalogProductType @@ -214,9 +225,13 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor + * @param \Magento\Framework\Cache\FrontendInterface|null $cache + * @param \Magento\Customer\Model\Session|null $customerSession * @param \Magento\Framework\Serialize\Serializer\Json $serializer * @param ProductInterfaceFactory $productFactory * @param SalableProcessor $salableProcessor + * @param ProductAttributeRepositoryInterface|null $productAttributeRepository + * @param SearchCriteriaBuilder|null $searchCriteriaBuilder * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -241,7 +256,9 @@ public function __construct( \Magento\Customer\Model\Session $customerSession = null, \Magento\Framework\Serialize\Serializer\Json $serializer = null, ProductInterfaceFactory $productFactory = null, - SalableProcessor $salableProcessor = null + SalableProcessor $salableProcessor = null, + ProductAttributeRepositoryInterface $productAttributeRepository = null, + SearchCriteriaBuilder $searchCriteriaBuilder = null ) { $this->typeConfigurableFactory = $typeConfigurableFactory; $this->_eavAttributeFactory = $eavAttributeFactory; @@ -256,6 +273,10 @@ public function __construct( $this->productFactory = $productFactory ?: ObjectManager::getInstance() ->get(ProductInterfaceFactory::class); $this->salableProcessor = $salableProcessor ?: ObjectManager::getInstance()->get(SalableProcessor::class); + $this->productAttributeRepository = $productAttributeRepository ?: + ObjectManager::getInstance()->get(ProductAttributeRepositoryInterface::class); + $this->searchCriteriaBuilder = $searchCriteriaBuilder ?: + ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); parent::__construct( $catalogProductOption, $eavConfig, @@ -1231,19 +1252,16 @@ public function isPossibleBuyFromList($product) /** * Returns array of sub-products for specified configurable product - * - * $requiredAttributeIds - one dimensional array, if provided * Result array contains all children for specified configurable product * * @param \Magento\Catalog\Model\Product $product - * @param array $requiredAttributeIds + * @param array $requiredAttributeIds Attributes to include in the select; one-dimensional array * @return ProductInterface[] - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getUsedProducts($product, $requiredAttributeIds = null) { if (!$product->hasData($this->_usedProducts)) { - $collection = $this->getConfiguredUsedProductCollection($product, false); + $collection = $this->getConfiguredUsedProductCollection($product, false, $requiredAttributeIds); $usedProducts = array_values($collection->getItems()); $product->setData($this->_usedProducts, $usedProducts); } @@ -1390,16 +1408,18 @@ private function getUsedProductsCacheKey($keyParts) /** * Prepare collection for retrieving sub-products of specified configurable product - * * Retrieve related products collection with additional configuration * * @param \Magento\Catalog\Model\Product $product * @param bool $skipStockFilter + * @param array $requiredAttributeIds Attributes to include in the select * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection + * @throws \Magento\Framework\Exception\LocalizedException */ private function getConfiguredUsedProductCollection( \Magento\Catalog\Model\Product $product, - $skipStockFilter = true + $skipStockFilter = true, + $requiredAttributeIds = null ) { $collection = $this->getUsedProductCollection($product); @@ -1407,8 +1427,19 @@ private function getConfiguredUsedProductCollection( $collection->setFlag('has_stock_status_filter', true); } + $attributesForSelect = $this->getAttributesForCollection($product); + if ($requiredAttributeIds) { + $this->searchCriteriaBuilder->addFilter('attribute_id', $requiredAttributeIds, 'in'); + $requiredAttributes = $this->productAttributeRepository + ->getList($this->searchCriteriaBuilder->create())->getItems(); + $requiredAttributeCodes = []; + foreach ($requiredAttributes as $requiredAttribute) { + $requiredAttributeCodes[] = $requiredAttribute->getAttributeCode(); + } + $attributesForSelect = array_unique(array_merge($attributesForSelect, $requiredAttributeCodes)); + } $collection - ->addAttributeToSelect($this->getAttributesForCollection($product)) + ->addAttributeToSelect($attributesForSelect) ->addFilterByRequiredOptions() ->setStoreId($product->getStoreId()); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php index 78fa4733a2562..0d2043434d359 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php @@ -254,6 +254,33 @@ public function testGetUsedProducts() } } + /** + * Tests the $requiredAttributes parameter; uses meta_description as an example of an attribute that is not + * included in default attribute select. + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php + */ + public function testGetUsedProductsWithRequiredAttributes() + { + $requiredAttributeIds = [86]; + $products = $this->model->getUsedProducts($this->product, $requiredAttributeIds); + foreach ($products as $product) { + self::assertNotNull($product->getData('meta_description')); + } + } + + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php + */ + public function testGetUsedProductsWithoutRequiredAttributes() + { + $products = $this->model->getUsedProducts($this->product); + foreach ($products as $product) { + self::assertNull($product->getData('meta_description')); + } + } + /** * Test getUsedProducts returns array with same indexes regardless collections was cache or not. * diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php new file mode 100644 index 0000000000000..d0afeeaf19fe8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php @@ -0,0 +1,145 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Setup\CategorySetup; +use Magento\ConfigurableProduct\Helper\Product\Options\Factory; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Eav\Api\Data\AttributeOptionInterface; +use Magento\TestFramework\Helper\Bootstrap; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); + +require __DIR__ . '/configurable_attribute.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(CategorySetup::class); + +/* Create simple products per each option value*/ +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); + +$attributeValues = []; +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$associatedProductIds = []; +$productIds = [10, 20]; +array_shift($options); //remove the first option which is empty + +foreach ($options as $option) { + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); + $productId = array_shift($productIds); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setId($productId) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Option' . $option->getLabel()) + ->setSku('simple_' . $productId) + ->setPrice($productId) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setMetaDescription('meta_description' . $productId) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $product = $productRepository->save($product); + + /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ + $stockItem = Bootstrap::getObjectManager()->create(\Magento\CatalogInventory\Model\Stock\Item::class); + $stockItem->load($productId, 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($productId); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} + +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +// Remove any previously created product with the same id. +/** @var \Magento\Framework\Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $productToDelete = $productRepository->getById(1); + $productRepository->delete($productToDelete); + + /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */ + $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class); + $itemResource->getConnection()->delete( + $itemResource->getMainTable(), + 'product_id = ' . $productToDelete->getId() + ); +} catch (\Exception $e) { + // Nothing to remove +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php new file mode 100644 index 0000000000000..21953dea6f587 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = $objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +foreach (['simple_10', 'simple_20', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + if ($product->getId()) { + $productRepository->delete($product); + } + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +require __DIR__ . '/configurable_attribute_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 8d1a45f9c301b713b4a1bca2cc0c557c5c9772f3 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <mac1@daniel-ruf.de> Date: Fri, 4 Oct 2019 23:57:06 +0200 Subject: [PATCH 0830/2437] Remove useless parentheses --- .../Magento/Catalog/Controller/Adminhtml/Product/Edit.php | 2 +- .../Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php | 2 +- .../Magento/NewRelicReporting/Model/Apm/Deployments.php | 2 +- .../Observer/MakePersistentQuoteGuestObserver.php | 4 ++-- app/code/Magento/Ui/Component/Control/SplitButton.php | 6 +++--- .../app/Magento/Catalog/Test/Fixture/Product/TaxClass.php | 2 +- setup/src/Magento/Setup/Model/Installer.php | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php index c31ceabcda655..02c474f096d05 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Edit.php @@ -51,7 +51,7 @@ public function execute() $productId = (int) $this->getRequest()->getParam('id'); $product = $this->productBuilder->build($this->getRequest()); - if (($productId && !$product->getEntityId())) { + if ($productId && !$product->getEntityId()) { /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); $this->messageManager->addErrorMessage(__('This product doesn\'t exist.')); diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 4039ff862f6fe..287b5b514ee8e 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -726,7 +726,7 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // TODO: getAttributeModel() should not be used when MAGETWO-48284 is complete $childData = $this->arrayManager->get($configPath, $meta, []); - if (($rules = $this->catalogEavValidationRules->build($this->getAttributeModel($attribute), $childData))) { + if ($rules = $this->catalogEavValidationRules->build($this->getAttributeModel($attribute), $childData)) { $meta = $this->arrayManager->merge($configPath, $meta, ['validation' => $rules]); } diff --git a/app/code/Magento/NewRelicReporting/Model/Apm/Deployments.php b/app/code/Magento/NewRelicReporting/Model/Apm/Deployments.php index bacdd3e4a81fe..f12b784cfb5ba 100644 --- a/app/code/Magento/NewRelicReporting/Model/Apm/Deployments.php +++ b/app/code/Magento/NewRelicReporting/Model/Apm/Deployments.php @@ -88,7 +88,7 @@ public function setDeployment($description, $change = false, $user = false) return false; } - if (($response->getStatus() < 200 || $response->getStatus() > 210)) { + if ($response->getStatus() < 200 || $response->getStatus() > 210) { $this->logger->warning('Deployment marker request did not send a 200 status code.'); return false; } diff --git a/app/code/Magento/Persistent/Observer/MakePersistentQuoteGuestObserver.php b/app/code/Magento/Persistent/Observer/MakePersistentQuoteGuestObserver.php index 94726bc9b1d05..31356059ee8bd 100644 --- a/app/code/Magento/Persistent/Observer/MakePersistentQuoteGuestObserver.php +++ b/app/code/Magento/Persistent/Observer/MakePersistentQuoteGuestObserver.php @@ -65,8 +65,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var $action \Magento\Persistent\Controller\Index */ $action = $observer->getEvent()->getControllerAction(); if ($action instanceof \Magento\Persistent\Controller\Index) { - if ((($this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn()) - || $this->_persistentData->isShoppingCartPersist()) + if (($this->_persistentSession->isPersistent() && !$this->_customerSession->isLoggedIn()) + || $this->_persistentData->isShoppingCartPersist() ) { $this->quoteManager->setGuest(true); } diff --git a/app/code/Magento/Ui/Component/Control/SplitButton.php b/app/code/Magento/Ui/Component/Control/SplitButton.php index ef57268566ba8..e1f475344d267 100644 --- a/app/code/Magento/Ui/Component/Control/SplitButton.php +++ b/app/code/Magento/Ui/Component/Control/SplitButton.php @@ -83,12 +83,12 @@ public function getButtonAttributesHtml() 'style' => $this->getStyle(), ]; - if (($idHard = $this->getIdHard())) { + if ($idHard = $this->getIdHard()) { $attributes['id'] = $idHard; } //TODO perhaps we need to skip data-mage-init when disabled="disabled" - if (($dataAttribute = $this->getDataAttribute())) { + if ($dataAttribute = $this->getDataAttribute()) { $this->getDataAttributes($dataAttribute, $attributes); } @@ -112,7 +112,7 @@ public function getToggleAttributesHtml() $title = $this->getLabel(); } - if (($currentClass = $this->getClass())) { + if ($currentClass = $this->getClass()) { $classes[] = $currentClass; } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/TaxClass.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/TaxClass.php index 23e0236fe7baa..70d2730868dbf 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/TaxClass.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Product/TaxClass.php @@ -52,7 +52,7 @@ class TaxClass extends DataSource public function __construct(FixtureFactory $fixtureFactory, array $params, $data = []) { $this->params = $params; - if ((!isset($data['dataset']) && !isset($data['tax_product_class']))) { + if (!isset($data['dataset']) && !isset($data['tax_product_class'])) { $this->data = $data; return; } diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php index 7a097e49c6289..23f8a13c8bfe8 100644 --- a/setup/src/Magento/Setup/Model/Installer.php +++ b/setup/src/Magento/Setup/Model/Installer.php @@ -429,7 +429,7 @@ private function createModulesConfig($request, $dryRun = false) $disable = $this->readListOfModules($all, $request, InstallCommand::INPUT_KEY_DISABLE_MODULES); $result = []; foreach ($all as $module) { - if ((isset($currentModules[$module]) && !$currentModules[$module])) { + if (isset($currentModules[$module]) && !$currentModules[$module]) { $result[$module] = 0; } else { $result[$module] = 1; @@ -925,7 +925,7 @@ private function throwExceptionForNotWritablePaths(array $paths) */ private function handleDBSchemaData($setup, $type, array $request) { - if (!(($type === 'schema') || ($type === 'data'))) { + if (!($type === 'schema' || $type === 'data')) { throw new \Magento\Setup\Exception("Unsupported operation type $type is requested"); } $resource = new \Magento\Framework\Module\ModuleResource($this->context); From a289dc4bf5e1b789289d775dcc817fde6669cc40 Mon Sep 17 00:00:00 2001 From: Adarsh Manickam <adarsh.apple@icloud.com> Date: Tue, 1 Oct 2019 10:43:26 +0530 Subject: [PATCH 0831/2437] Issue#24785: Added JS method to handle input checks with MFTF --- .../StorefrontDownloadableProductSection.xml | 1 + ...wnloadableLinksDownloadableProductTest.xml | 122 ++++++++++++++++++ .../view/frontend/web/js/downloadable.js | 25 ++++ 3 files changed, 148 insertions(+) create mode 100644 app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml diff --git a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml index 20b62ef060309..dc2a58be138e7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontDownloadableProductSection.xml @@ -15,5 +15,6 @@ <element name="downloadableLinkSampleByTitle" type="text" selector="//label[contains(., '{{title}}')]/a[contains(@class, 'sample link')]" parameterized="true"/> <element name="downloadableSampleLabel" type="text" selector="//a[contains(.,normalize-space('{{title}}'))]" parameterized="true" timeout="30"/> <element name="downloadableLinkSelectAllCheckbox" type="checkbox" selector="#links_all" /> + <element name="downloadableLinkSelectAllLabel" type="text" selector="label[for='links_all']" /> </section> </sections> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml new file mode 100644 index 0000000000000..b0424b1976c1c --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="ManualSelectAllDownloadableLinksDownloadableProductTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Downloadable Product"/> + <title value="Manual select all downloadable links downloadable product test"/> + <description value="Manually selecting all downloadable links must change 'Select/Unselect all' button label to 'Unselect all', and 'Select all' otherwise"/> + <severity value="MAJOR"/> + <group value="Downloadable"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create downloadable product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad stepKey="waitForProductGridPageLoad"/> + <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <argument name="productType" value="downloadable"/> + </actionGroup> + + <!-- Fill downloadable product values --> + <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Add downloadable product to category --> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" + parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + + <!-- Fill downloadable link information before the creation link --> + <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> + + <!-- Links can be purchased separately --> + <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" + stepKey="checkOptionPurchaseSeparately"/> + + <!-- Add first downloadable link --> + <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <argument name="link" value="downloadableLinkWithMaxDownloads"/> + </actionGroup> + + <!-- Add second downloadable link --> + <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <argument name="link" value="downloadableLink"/> + </actionGroup> + + <!-- Save product --> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + </before> + <after> + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Delete created downloadable product --> + <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <argument name="product" value="DownloadableProduct"/> + </actionGroup> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Step 1: Navigate to store front Product page as guest --> + <amOnPage url="/{{DownloadableProduct.sku}}.html" + stepKey="amOnStorefrontProductPage"/> + + <!-- Step 2: Check first downloadable link checkbox --> + <click + selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLinkWithMaxDownloads.title)}}" + stepKey="selectFirstCheckbox"/> + + <!-- Step 3: Check second downloadable link checkbox --> + <click + selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" + stepKey="selectSecondCheckbox"/> + + <!-- Step 4: Grab "Select/Unselect All" button label text --> + <grabTextFrom + selector="{{StorefrontDownloadableProductSection.downloadableLinkSelectAllLabel}}" + stepKey="grabUnselectAllButtonText"/> + + <!-- Step 5: Assert that 'Select/Unselect all' button text is 'Unselect all' after manually checking all checkboxes --> + <assertEquals + message="Assert that 'Select/Unselect all' button text is 'Unselect all' after manually checking all checkboxes" + stepKey="assertButtonTextOne"> + <expectedResult type="string">Unselect all</expectedResult> + <actualResult type="string">{$grabUnselectAllButtonText}</actualResult> + </assertEquals> + + <!-- Step 6: Uncheck second downloadable link checkbox --> + <click + selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" + stepKey="unselectSecondCheckbox"/> + + <!-- Step 7: Grab "Select/Unselect All" button label text --> + <grabTextFrom + selector="{{StorefrontDownloadableProductSection.downloadableLinkSelectAllLabel}}" + stepKey="grabSelectAllButtonText"/> + + <!-- Step 8: Assert that 'Select/Unselect all' button text is 'Select all' after manually unchecking one checkbox --> + <assertEquals + message="Assert that 'Select/Unselect all' button text is 'Select all' after manually unchecking one checkbox" + stepKey="assertButtonTextTwo"> + <expectedResult type="string">Select all</expectedResult> + <actualResult type="string">{$grabSelectAllButtonText}</actualResult> + </assertEquals> + + </test> +</tests> diff --git a/app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js b/app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js index a1e8c785c696a..5fc14f45d280a 100644 --- a/app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js +++ b/app/code/Magento/Downloadable/view/frontend/web/js/downloadable.js @@ -65,6 +65,31 @@ define([ } } }); + + this.reloadAllCheckText(); + }, + + /** + * Reload all-elements-checkbox's label + * @private + */ + reloadAllCheckText: function () { + let allChecked = true, + allElementsCheck = $(this.options.allElements), + allElementsLabel = $('label[for="' + allElementsCheck.attr('id') + '"] > span'); + $(this.options.linkElement).each(function () { + if (!this.checked) { + allChecked = false; + } + }); + + if (allChecked) { + allElementsLabel.text(allElementsCheck.attr('data-checked')); + allElementsCheck.prop('checked', true) + } else { + allElementsLabel.text(allElementsCheck.attr('data-notchecked')); + allElementsCheck.prop('checked', false) + } } }); From 3374a5a9d11ee3a5577fd5f25a600852a9875039 Mon Sep 17 00:00:00 2001 From: Brent Robert <brent.robert@phpro.be> Date: Sun, 6 Oct 2019 14:34:26 +0200 Subject: [PATCH 0832/2437] Make email preview responsive Make email preview responsive --- .../Magento/backend/web/css/styles-old.less | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 44fca79c31be5..b2afde435a627 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -4070,6 +4070,21 @@ } } +.adminhtml-email_template-preview { + .cms-revision-preview { + padding-top: 56.25%; + position: relative; + + #preview_iframe { + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + } +} + .admin__scope-old { .buttons-set { margin: 0 0 15px; From f0cfb96351cda42bd76afc203111988266940c67 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <mac1@daniel-ruf.de> Date: Sun, 6 Oct 2019 16:14:44 +0200 Subject: [PATCH 0833/2437] Replace deprecated "big" element --- .../Sales/view/adminhtml/templates/order/details.phtml | 4 ++-- .../Sales/view/adminhtml/templates/order/totals/due.phtml | 4 ++-- .../Sales/view/adminhtml/templates/order/totals/grand.phtml | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/details.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/details.phtml index b700f1b3a65ad..70373f177d8be 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/details.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/details.phtml @@ -81,8 +81,8 @@ $_order = $block->getOrder() ?> </tr> <?php endif; ?> <tr bgcolor="#DEE5E8"> - <td colspan="2" align="right" style="padding:3px 9px"><strong><big><?= $block->escapeHtml(__('Grand Total')) ?></big></strong></td> - <td align="right" style="padding:6px 9px"><strong><big><?= /* @noEscape */ $_order->formatPrice($_order->getGrandTotal()) ?></big></strong></td> + <td colspan="2" align="right" style="padding:3px 9px"><strong style="font-size: larger"><?= $block->escapeHtml(__('Grand Total')) ?></strong></td> + <td align="right" style="padding:6px 9px"><strong style="font-size: larger"><?= /* @noEscape */ $_order->formatPrice($_order->getGrandTotal()) ?></strong></td> </tr> </tfoot> </table> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totals/due.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totals/due.phtml index 87d7c85c2d9ed..f8e914a2c9b2f 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totals/due.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totals/due.phtml @@ -6,7 +6,7 @@ ?> <?php if ($block->getCanDisplayTotalDue()) : ?> <tr> - <td class="label"><big><strong><?= $block->escapeHtml(__('Total Due')) ?></strong></big></td> - <td class="emph"><big><?= /* @noEscape */ $block->displayPriceAttribute('total_due', true) ?></big></td> + <td class="label"><strong style="font-size: larger"><?= $block->escapeHtml(__('Total Due')) ?></strong></td> + <td class="emph" style="font-size: larger"><?= /* @noEscape */ $block->displayPriceAttribute('total_due', true) ?></td> </tr> <?php endif; ?> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/totals/grand.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/totals/grand.phtml index dc76799251c7a..af5d58d47fce1 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/totals/grand.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/totals/grand.phtml @@ -9,13 +9,13 @@ <tr> <td class="label"> - <strong><big> + <strong style="font-size: larger"> <?php if ($block->getGrandTotalTitle()) : ?> <?= $block->escapeHtml($block->getGrandTotalTitle()) ?> <?php else : ?> <?= $block->escapeHtml(__('Grand Total')) ?> <?php endif; ?> - </big></strong> + </strong> </td> - <td class="emph"><big><?= /* @noEscape */ $block->displayPriceAttribute('grand_total', true) ?></big></td> + <td class="emph" style="font-size: larger"><?= /* @noEscape */ $block->displayPriceAttribute('grand_total', true) ?></td> </tr> From 5d6b72df4ad46b4391b314581dee831399821517 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <mac1@daniel-ruf.de> Date: Sun, 6 Oct 2019 16:42:07 +0200 Subject: [PATCH 0834/2437] Remove duplicate CSS properties --- .../module/components/_currency-addon.less | 2 +- .../_files/static/theme/web/css/styles.css | 33 ------------------- lib/web/mage/calendar.css | 1 - lib/web/mage/translate-inline-vde.css | 1 - 4 files changed, 1 insertion(+), 36 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less index 659b1fa811db1..fa158589feb96 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less @@ -22,10 +22,10 @@ position: relative; display: -webkit-inline-flex; display: -ms-inline-flexbox; + display: inline-flex; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; - display: inline-flex; flex-flow: row nowrap; width: 100%; diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/web/css/styles.css b/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/web/css/styles.css index 90ca42321c921..35305fb2392d0 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/web/css/styles.css +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/static/theme/web/css/styles.css @@ -484,7 +484,6 @@ table .col-draggable .draggable-handle { border: 0; display: inline; line-height: 1.42857143; - margin: 0; padding: 0; color: #1979c3; text-decoration: none; @@ -621,7 +620,6 @@ fieldset[disabled] .grid-actions .action-reset { background: none; border: 0; display: inline; - line-height: 1.42857143; margin: 0; padding: 0; color: #1979c3; @@ -683,7 +681,6 @@ fieldset[disabled] .pager .action-next { text-decoration: none; } .pager .action-previous > span { - clip: rect(0, 0, 0, 0); border: 0; clip: rect(0 0 0 0); height: 1px; @@ -733,7 +730,6 @@ fieldset[disabled] .pager .action-next { text-decoration: none; } .pager .action-next > span { - clip: rect(0, 0, 0, 0); border: 0; clip: rect(0 0 0 0); height: 1px; @@ -1510,7 +1506,6 @@ fieldset[disabled] .pager .action-next { text-decoration: none; } .search-global-field .label > span { - clip: rect(0, 0, 0, 0); border: 0; clip: rect(0 0 0 0); height: 1px; @@ -1622,7 +1617,6 @@ fieldset[disabled] .pager .action-next { color: #676056; } .notifications-action .text { - clip: rect(0, 0, 0, 0); border: 0; clip: rect(0 0 0 0); height: 1px; @@ -1707,7 +1701,6 @@ fieldset[disabled] .pager .action-next { z-index: 1; top: 12px; right: 12px; - display: inline-block; background-image: none; background: none; border: 0; @@ -2358,7 +2351,6 @@ fieldset[disabled] .notifications-close.action { text-decoration: none; } #product-variations-matrix .actions-image-uploader { - display: inline-block; position: relative; display: block; width: 50px; @@ -3109,7 +3101,6 @@ body > * { background-image: none; background: none; border: 0; - margin: 0; padding: 0; -moz-box-sizing: content-box; box-shadow: none; @@ -3396,7 +3387,6 @@ fieldset[disabled] .page-main-actions .page-actions .action-add.mselect-button-a box-shadow: none; text-shadow: none; text-decoration: none; - line-height: inherit; font-weight: 400; color: #026294; line-height: normal; @@ -3464,7 +3454,6 @@ fieldset[disabled] .store-switcher .actions.dropdown .action.toggle { margin-top: 10px; } .store-switcher .actions.dropdown ul.dropdown-menu .dropdown-toolbar a { - display: inline-block; text-decoration: none; display: block; } @@ -4833,17 +4822,11 @@ fieldset[disabled] .control-text + .ui-datepicker-trigger { width: 100%; } .filters-toggle { - background: #f2ebde; - padding: 6px 13px; color: #645d53; - border: 1px solid #ada89e; cursor: pointer; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 1.3rem; - font-weight: 500; - line-height: 1.4rem; box-sizing: border-box; - margin: 3px; vertical-align: middle; display: inline-block; background-image: none; @@ -4946,7 +4929,6 @@ fieldset[disabled] .filters-toggle { .filters-item .action-remove { background-image: none; background: #f2ebde; - padding: 6px 13px; color: #645d53; border: 1px solid #ada89e; cursor: pointer; @@ -5038,17 +5020,11 @@ fieldset[disabled] .filters-item .action-remove { position: absolute; top: 3px; right: 7px; - background: #f2ebde; - padding: 6px 13px; color: #645d53; - border: 1px solid #ada89e; cursor: pointer; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 1.3rem; - font-weight: 500; - line-height: 1.4rem; box-sizing: border-box; - margin: 3px; vertical-align: middle; display: inline-block; background-image: none; @@ -5283,13 +5259,11 @@ fieldset[disabled] .filters-form .action-close { border: 1px solid #c2c2c2; border-radius: 1px; height: 32px; - width: 100%; padding: 0 9px; font-size: 14px; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.428571429; background-clip: padding-box; - vertical-align: baseline; width: auto; white-space: nowrap; vertical-align: middle; @@ -5487,17 +5461,11 @@ fieldset[disabled] .filters .field-date .group .hasDatepicker + .ui-datepicker-t background: rgba(0, 0, 0, 0.2); } .mass-select-toggle { - background: #f2ebde; - padding: 6px 13px; color: #645d53; - border: 1px solid #ada89e; cursor: pointer; font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 1.3rem; - font-weight: 500; - line-height: 1.4rem; box-sizing: border-box; - margin: 3px; vertical-align: middle; display: inline-block; background-image: none; @@ -5754,7 +5722,6 @@ fieldset[disabled] .mass-select-toggle { .page-actions.fixed .page-actions-inner, .page-content, .page-footer { - width: auto; min-width: 960px; max-width: 1300px; margin: 0 auto; diff --git a/lib/web/mage/calendar.css b/lib/web/mage/calendar.css index 071270feeeb3e..da19f389d3085 100644 --- a/lib/web/mage/calendar.css +++ b/lib/web/mage/calendar.css @@ -141,7 +141,6 @@ padding: 0; } .ui-datepicker { - background: #ffffff; padding: 15px; border: #ffffff 4px solid; -webkit-box-sizing: border-box; diff --git a/lib/web/mage/translate-inline-vde.css b/lib/web/mage/translate-inline-vde.css index 369c1fb9a2f07..f898bb01c43d9 100644 --- a/lib/web/mage/translate-inline-vde.css +++ b/lib/web/mage/translate-inline-vde.css @@ -52,7 +52,6 @@ } .translate-dialog .ui-dialog-buttonset { - padding-right: 0; margin-right: 0; float: right; background: rgba(223, 243, 250, 1.0); From 9dfedbc9c373d8d7a75d76033bd77d280a27e468 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <mac1@daniel-ruf.de> Date: Sun, 6 Oct 2019 17:24:50 +0200 Subject: [PATCH 0835/2437] Replace deprecated "nobr" element --- lib/web/css/docs/layout.html | 8 ++++---- lib/web/css/docs/responsive.html | 6 +++--- lib/web/css/docs/source/_layout.less | 8 ++++---- lib/web/css/docs/source/_responsive.less | 6 +++--- lib/web/css/docs/source/_variables.less | 8 ++++---- lib/web/css/docs/variables.html | 8 ++++---- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/web/css/docs/layout.html b/lib/web/css/docs/layout.html index 77ed0597f0748..b338c66ffae71 100644 --- a/lib/web/css/docs/layout.html +++ b/lib/web/css/docs/layout.html @@ -55,25 +55,25 @@ <tr> <th>@layout-class-1column</th> <td class="vars_value">page-layout-1column</td> - <td class="vars_value">'' | false | <nobr>page-layout-1column</nobr> | <nobr>page-layout-2columns-left</nobr> | <nobr>page-layout-2columns-right</nobr> | <nobr>page-layout-3columns</nobr></td> + <td class="vars_value">'' | false | <span style="white-space: nowrap">page-layout-1column</span> | <span style="white-space: nowrap">page-layout-2columns-left</span> | <span style="white-space: nowrap">page-layout-2columns-right</span> | <span style="white-space: nowrap">page-layout-3columns</span></td> <td>Class name for one column layout</td> </tr> <tr> <th>@layout-class-2columns__left</th> <td class="vars_value">page-layout-2columns-left</td> - <td class="vars_value">'' | false | <nobr>page-layout-1column</nobr> | <nobr>page-layout-2columns-left</nobr> | <nobr>page-layout-2columns-right</nobr> | <nobr>page-layout-3columns</nobr></td> + <td class="vars_value">'' | false | <span style="white-space: nowrap">page-layout-1column</span> | <span style="white-space: nowrap">page-layout-2columns-left</span> | <span style="white-space: nowrap">page-layout-2columns-right</span> | <span style="white-space: nowrap">page-layout-3columns</span></td> <td>Class name for two columns layout with left sidebar</td> </tr> <tr> <th nowrap="nowrap">@layout-class-2columns__right</th> <td class="vars_value">page-layout-2columns-right</td> - <td class="vars_value">'' | false | <nobr>page-layout-1column</nobr> | <nobr>page-layout-2columns-left</nobr> | <nobr>page-layout-2columns-right</nobr> | <nobr>page-layout-3columns</nobr></td> + <td class="vars_value">'' | false | <span style="white-space: nowrap">page-layout-1column</span> | <span style="white-space: nowrap">page-layout-2columns-left</span> | <span style="white-space: nowrap">page-layout-2columns-right</span> | <span style="white-space: nowrap">page-layout-3columns</span></td> <td>Class name for two columns layout with right sidebar</td> </tr> <tr> <th>@layout-class-3columns</th> <td class="vars_value">page-layout-3columns</td> - <td class="vars_value">'' | false | <nobr>page-layout-1column</nobr> | <nobr>page-layout-2columns-left</nobr> | <nobr>page-layout-2columns-right</nobr> | <nobr>page-layout-3columns</nobr></td> + <td class="vars_value">'' | false | <span style="white-space: nowrap">page-layout-1column</span> | <span style="white-space: nowrap">page-layout-2columns-left</span> | <span style="white-space: nowrap">page-layout-2columns-right</span> | <span style="white-space: nowrap">page-layout-3columns</span></td> <td>Class name for three columns layout with left sidebar</td> </tr> <tr> diff --git a/lib/web/css/docs/responsive.html b/lib/web/css/docs/responsive.html index 48d0bd551bd92..ebc42e698f60b 100644 --- a/lib/web/css/docs/responsive.html +++ b/lib/web/css/docs/responsive.html @@ -7,7 +7,7 @@ <!DOCTYPE html><html><head><title>responsive | Magento UI Library

Responsive

-

Magento UI library provides a strong approach for working with media queries. It`s based on recursive call of .media-width() mixin defined anywhere in project but invoked in one place in lib/web/css/source/lib/_responsive.less. That's why in the resulting styles.css we have every media query only once with all the rules there, not a multiple calls for the same query.

+

Magento UI library provides a strong approach for working with media queries. It`s based on recursive call of .media-width() mixin defined anywhere in project but invoked in one place in lib/web/css/source/lib/_responsive.less. That's why in the resulting styles.css we have every media query only once with all the rules there, not a multiple calls for the same query.

To see the media queries work resize window to understand which breakpoint is applied.

+
getChildHtml('form.additional.info') ?> From e891e8184993eda1900763340d3454679e90a326 Mon Sep 17 00:00:00 2001 From: Kajal Solanki Date: Fri, 8 Nov 2019 10:32:55 +0530 Subject: [PATCH 1944/2437] Product item rating stars overlaps a hovered item on category page issue resolved --- .../luma/Magento_Catalog/web/css/source/module/_listings.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less index 92945d61e4368..f75bf5b238914 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less @@ -292,7 +292,7 @@ margin: -10px; padding: 9px; position: relative; - z-index: 2; + z-index: 9; .product-item-inner { display: block; From 6e1822d1b1243a293075e8eef2adc2d6b30d024d Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Fri, 8 Nov 2019 10:08:04 +0200 Subject: [PATCH 1945/2437] Static Test Fix --- .../Widget/Grid/Column/Renderer/Action.php | 3 +- .../Helper/Dashboard/AbstractDashboard.php | 17 ++++++ .../Eav/Model/Entity/AbstractEntity.php | 55 +++++++++++-------- .../Magento/Rule/Model/Action/Collection.php | 19 +++++++ .../Magento/Rule/Model/Condition/Combine.php | 38 +++++++++++++ 5 files changed, 107 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php index 4affcacca4e5f..a7d85a4cfef4c 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php @@ -104,6 +104,7 @@ protected function _toLinkHtml($action, \Magento\Framework\DataObject $row) $this->_transformActionData($action, $actionCaption, $row); if (isset($action['confirm'])) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $action['onclick'] = 'return window.confirm(\'' . addslashes( $this->escapeHtml($action['confirm']) ) . '\')'; @@ -144,7 +145,7 @@ protected function _transformActionData(&$action, &$actionCaption, \Magento\Fram if (is_array($action['url']) && isset($action['field'])) { $params = [$action['field'] => $this->_getValue($row)]; if (isset($action['url']['params'])) { - $params = array_merge($action['url']['params'], $params); + $params[] = $action['url']['params']; } $action['href'] = $this->getUrl($action['url']['base'], $params); unset($action['field']); diff --git a/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php b/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php index 3405d1ae34a31..7cb8690b4ec27 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php +++ b/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php @@ -8,6 +8,7 @@ /** * Adminhtml abstract dashboard helper. * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ @@ -28,6 +29,8 @@ abstract class AbstractDashboard extends \Magento\Framework\App\Helper\AbstractH protected $_params = []; /** + * Return collections + * * @return array|\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection */ public function getCollection() @@ -39,6 +42,8 @@ public function getCollection() } /** + * Init collections + * * @return void */ abstract protected function _initCollection(); @@ -54,6 +59,8 @@ public function getItems() } /** + * Return items count + * * @return int */ public function getCount() @@ -62,6 +69,8 @@ public function getCount() } /** + * Return column + * * @param string $index * @return array */ @@ -85,6 +94,8 @@ public function getColumn($index) } /** + * Set params with value + * * @param string $name * @param mixed $value * @return void @@ -95,6 +106,8 @@ public function setParam($name, $value) } /** + * Set params + * * @param array $params * @return void */ @@ -104,6 +117,8 @@ public function setParams(array $params) } /** + * Get params with name + * * @param string $name * @return mixed */ @@ -117,6 +132,8 @@ public function getParam($name) } /** + * Get params + * * @return array */ public function getParams() diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 190646a6aebf3..7f6dbc106ceb8 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -11,21 +11,22 @@ use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface as DefaultAttributesProvider; use Magento\Framework\App\Config\Element; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\DuplicateException; use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor; use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; -use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface as DefaultAttributesProvider; -use Magento\Framework\Model\ResourceModel\AbstractResource; -use Magento\Framework\App\ObjectManager; /** * Entity/Attribute/Model - entity abstract * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @author Magento Core Team * @SuppressWarnings(PHPMD.TooManyFields) @@ -266,6 +267,8 @@ public function setConnection($connection) /** * Resource initialization * + * phpcs:disable Magento2.CodeAnalysis.EmptyBlock + * * @return void */ protected function _construct() @@ -687,6 +690,7 @@ public function walkAttributes($partMethod, array $args = [], $collectExceptionM } try { + // phpcs:disable Magento2.Functions.DiscouragedFunction $results[$attrCode] = call_user_func_array([$instance, $method], $args); } catch (\Magento\Eav\Model\Entity\Attribute\Exception $e) { if ($collectExceptionMessages) { @@ -826,9 +830,9 @@ public function getValueTablePrefix() $prefix = (string) $this->getEntityType()->getValueTablePrefix(); if (!empty($prefix)) { $this->_valueTablePrefix = $prefix; - /** - * entity type prefix include DB table name prefix - */ + /** + * entity type prefix include DB table name prefix + */ //$this->_resource->getTableName($prefix); } else { $this->_valueTablePrefix = $this->getEntityTable(); @@ -991,9 +995,9 @@ public function getDefaultAttributeSourceModel() /** * Load entity's attributes into the object * - * @param AbstractModel $object - * @param int $entityId - * @param array|null $attributes + * @param AbstractModel $object + * @param int $entityId + * @param array|null $attributes * @return $this */ public function load($object, $entityId, $attributes = []) @@ -1131,8 +1135,8 @@ protected function _getLoadAttributesSelect($object, $table) /** * Initialize attribute value for object * - * @param DataObject $object - * @param array $valueRow + * @param DataObject $object + * @param array $valueRow * @return $this */ protected function _setAttributeValue($object, $valueRow) @@ -1237,7 +1241,7 @@ protected function _getOrigObject($object) /** * Aggregate Data for attributes that will be deleted * - * @param array &$delete + * @param &array $delete * @param AbstractAttribute $attribute * @param AbstractEntity $object * @return void @@ -1248,6 +1252,7 @@ private function _aggregateDeleteData(&$delete, $attribute, $object) if (!isset($delete[$tableName])) { $delete[$tableName] = []; } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $delete[$tableName] = array_merge((array)$delete[$tableName], $valuesData); } } @@ -1422,7 +1427,7 @@ protected function _prepareStaticValue($key, $value) /** * Save object collected data * - * @param array $saveData array('newObject', 'entityRow', 'insert', 'update', 'delete') + * @param array $saveData array('newObject', 'entityRow', 'insert', 'update', 'delete') * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -1517,9 +1522,9 @@ protected function _processSaveData($saveData) /** * Insert entity attribute value * - * @param DataObject $object - * @param AbstractAttribute $attribute - * @param mixed $value + * @param DataObject $object + * @param AbstractAttribute $attribute + * @param mixed $value * @return $this */ protected function _insertAttribute($object, $attribute, $value) @@ -1530,10 +1535,10 @@ protected function _insertAttribute($object, $attribute, $value) /** * Update entity attribute value * - * @param DataObject $object - * @param AbstractAttribute $attribute - * @param mixed $valueId - * @param mixed $value + * @param DataObject $object + * @param AbstractAttribute $attribute + * @param mixed $valueId + * @param mixed $value * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -1892,10 +1897,12 @@ protected function _getDefaultAttributes() */ public function getDefaultAttributes() { - return array_unique(array_merge( - $this->_getDefaultAttributes(), - [$this->getEntityIdField(), $this->getLinkField()] - )); + return array_unique( + array_merge( + $this->_getDefaultAttributes(), + [$this->getEntityIdField(), $this->getLinkField()] + ) + ); } /** diff --git a/app/code/Magento/Rule/Model/Action/Collection.php b/app/code/Magento/Rule/Model/Action/Collection.php index 0434257d5fe3b..33e385e264f72 100644 --- a/app/code/Magento/Rule/Model/Action/Collection.php +++ b/app/code/Magento/Rule/Model/Action/Collection.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Rule\Model\Action; /** + * Collections + * * @api * @since 100.0.2 */ @@ -61,6 +64,8 @@ public function asArray(array $arrAttributes = []) } /** + * Load array + * * @param array $arr * @return $this */ @@ -80,6 +85,8 @@ public function loadArray(array $arr) } /** + * Add actions + * * @param ActionInterface $action * @return $this */ @@ -99,6 +106,8 @@ public function addAction(ActionInterface $action) } /** + * As html + * * @return string */ public function asHtml() @@ -111,6 +120,8 @@ public function asHtml() } /** + * Return new child element + * * @return $this */ public function getNewChildElement() @@ -129,6 +140,8 @@ public function getNewChildElement() } /** + * Return as html recursive + * * @return string */ public function asHtmlRecursive() @@ -142,6 +155,8 @@ public function asHtmlRecursive() } /** + * Add string + * * @param string $format * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -153,6 +168,8 @@ public function asString($format = '') } /** + * Return string as recursive + * * @param int $level * @return string */ @@ -166,6 +183,8 @@ public function asStringRecursive($level = 0) } /** + * Process + * * @return $this */ public function process() diff --git a/app/code/Magento/Rule/Model/Condition/Combine.php b/app/code/Magento/Rule/Model/Condition/Combine.php index 200446b56b66f..a8a8e5fb0f843 100644 --- a/app/code/Magento/Rule/Model/Condition/Combine.php +++ b/app/code/Magento/Rule/Model/Condition/Combine.php @@ -6,6 +6,8 @@ namespace Magento\Rule\Model\Condition; /** + * Combine + * * @api * @since 100.0.2 */ @@ -22,6 +24,8 @@ class Combine extends AbstractCondition protected $_logger; /** + * Construct + * * @param Context $context * @param array $data */ @@ -54,6 +58,8 @@ public function __construct(Context $context, array $data = []) /* start aggregator methods */ /** + * Load aggregation options + * * @return $this */ public function loadAggregatorOptions() @@ -63,6 +69,8 @@ public function loadAggregatorOptions() } /** + * Return agregator selected options + * * @return array */ public function getAggregatorSelectOptions() @@ -75,6 +83,8 @@ public function getAggregatorSelectOptions() } /** + * Get Agregator name + * * @return string */ public function getAggregatorName() @@ -83,6 +93,8 @@ public function getAggregatorName() } /** + * Return agregator element + * * @return object */ public function getAggregatorElement() @@ -112,6 +124,8 @@ public function getAggregatorElement() /* end aggregator methods */ /** + * Load value options + * * @return $this */ public function loadValueOptions() @@ -121,6 +135,8 @@ public function loadValueOptions() } /** + * Adds condition + * * @param object $condition * @return $this */ @@ -142,6 +158,8 @@ public function addCondition($condition) } /** + * Return value element type + * * @return string */ public function getValueElementType() @@ -181,6 +199,8 @@ public function asArray(array $arrAttributes = []) } /** + * As xml + * * @param string $containerKey * @param string $itemKey * @return string @@ -202,6 +222,8 @@ public function asXml($containerKey = 'conditions', $itemKey = 'condition') } /** + * Load array + * * @param array $arr * @param string $key * @return $this @@ -230,6 +252,8 @@ public function loadArray($arr, $key = 'conditions') } /** + * Load xml + * * @param array|string $xml * @return $this */ @@ -247,6 +271,8 @@ public function loadXml($xml) } /** + * As html + * * @return string */ public function asHtml() @@ -263,6 +289,8 @@ public function asHtml() } /** + * Get new child element + * * @return $this */ public function getNewChildElement() @@ -282,6 +310,8 @@ public function getNewChildElement() } /** + * As html recursive + * * @return string */ public function asHtmlRecursive() @@ -300,6 +330,8 @@ public function asHtmlRecursive() } /** + * As string + * * @param string $format * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -311,6 +343,8 @@ public function asString($format = '') } /** + * As string recursive + * * @param int $level * @return string */ @@ -324,6 +358,8 @@ public function asStringRecursive($level = 0) } /** + * Validate + * * @param \Magento\Framework\Model\AbstractModel $model * @return bool */ @@ -374,6 +410,8 @@ protected function _isValid($entity) } /** + * Set js From object + * * @param \Magento\Framework\Data\Form $form * @return $this */ From 8e417d7c620685029c01518d037b3035c73d2170 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Thu, 7 Nov 2019 14:19:28 +0200 Subject: [PATCH 1946/2437] MC-20682: Storefront: Add/remove product from other storeviews and websites --- .../Catalog/Block/Product/ViewTest.php | 82 ++++++ .../Catalog/Controller/Product/ViewTest.php | 245 +++++++++++++----- ...multistore_different_short_description.php | 47 ++++ ...e_different_short_description_rollback.php | 29 +++ .../Catalog/_files/product_two_websites.php | 36 +++ .../_files/product_two_websites_rollback.php | 29 +++ 6 files changed, 403 insertions(+), 65 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php index 99924e731dad8..ca2f915b42c18 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php @@ -9,10 +9,12 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View\Description; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; @@ -43,6 +45,22 @@ class ViewTest extends TestCase /** @var Json */ private $json; + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var Description */ + private $descriptionBlock; + + /** @var array */ + private const SHORT_DESCRIPTION_BLOCK_DATA = [ + 'at_call' => 'getShortDescription', + 'at_code' => 'short_description', + 'overview' => 'overview', + 'at_label' => 'none', + 'title' => 'Overview', + 'add_attribute' => 'description', + ]; + /** * @inheritdoc */ @@ -54,6 +72,8 @@ protected function setUp() $this->layout = $this->objectManager->get(LayoutInterface::class); $this->registry = $this->objectManager->get(Registry::class); $this->json = $this->objectManager->get(Json::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->descriptionBlock = $this->layout->createBlock(Description::class); } /** @@ -179,6 +199,68 @@ public function testAddToCartBlockVisibility(): void $this->assertContains((string)__('Add to Cart'), $output); } + /** + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/product_multistore_different_short_description.php + * @return void + */ + public function testProductShortDescription(): void + { + $product = $this->productRepository->get('simple-different-short-description'); + $currentStoreId = $this->storeManager->getStore()->getId(); + $output = $this->renderDescriptionBlock($product); + + $this->assertContains('First store view short description', $output); + + $secondStore = $this->storeManager->getStore('fixturestore'); + $this->storeManager->setCurrentStore($secondStore->getId()); + + try { + $product = $this->productRepository->get( + 'simple-different-short-description', + false, + $secondStore->getId(), + true + ); + $newBlockOutput = $this->renderDescriptionBlock($product, true); + + $this->assertContains('Second store view short description', $newBlockOutput); + } finally { + $this->storeManager->setCurrentStore($currentStoreId); + } + } + + /** + * @param ProductInterface $product + * @param bool $refreshBlock + * @return string + */ + private function renderDescriptionBlock(ProductInterface $product, bool $refreshBlock = false): string + { + $this->registerProduct($product); + $descriptionBlock = $this->getDescriptionBlock($refreshBlock); + $descriptionBlock->addData(self::SHORT_DESCRIPTION_BLOCK_DATA); + $descriptionBlock->setTemplate('Magento_Catalog::product/view/attribute.phtml'); + + return $this->descriptionBlock->toHtml(); + } + + /** + * Get description block + * + * @param bool $refreshBlock + * @return Description + */ + private function getDescriptionBlock(bool $refreshBlock): Description + { + if ($refreshBlock) { + $this->descriptionBlock = $this->layout->createBlock(Description::class); + } + + return $this->descriptionBlock; + } + /** * Register the product * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php index a7e4b3e8bddcd..495fe4aa710df 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php @@ -7,18 +7,22 @@ namespace Magento\Catalog\Controller\Product; +use Magento\Catalog\Api\AttributeSetRepositoryInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; use Magento\Eav\Model\AttributeSetSearchResults; use Magento\Eav\Model\Entity\Attribute\Set; +use Magento\Eav\Model\Entity\Type; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Framework\App\Http; use Magento\Framework\Data\Collection; use Magento\Framework\Registry; -use Magento\Catalog\Api\AttributeSetRepositoryInterface; -use Magento\Eav\Model\Entity\Type; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Request; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Magento\Catalog\Api\Data\ProductAttributeInterface; @@ -31,6 +35,7 @@ * Integration test for product view front action. * * @magentoAppArea frontend + * @magentoDbIsolation enabled * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ViewTest extends AbstractController @@ -58,6 +63,18 @@ class ViewTest extends AbstractController /** @var Registry */ private $registry; + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var SortOrderBuilder */ + private $sortOrderBuilder; + + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + /** * @inheritdoc */ @@ -71,6 +88,10 @@ protected function setUp() $this->productEntityType = $this->_objectManager->create(Type::class) ->loadByCode(Product::ENTITY); $this->registry = $this->_objectManager->get(Registry::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + $this->websiteRepository = $this->_objectManager->get(WebsiteRepositoryInterface::class); + $this->sortOrderBuilder = $this->_objectManager->create(SortOrderBuilder::class); + $this->searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); } /** @@ -106,8 +127,7 @@ public function testViewActionCustomAttributeSetWithoutCountryOfManufacture(): v { /** @var MockObject|LoggerInterface $logger */ $logger = $this->setupLoggerMock(); - - $product = $this->getProductBySku('simple_with_com'); + $product = $this->productRepository->get('simple_with_com'); $attributeSetCustom = $this->getProductAttributeSetByName('custom_attribute_set_wout_com'); $product->setAttributeSetId($attributeSetCustom->getAttributeSetId()); $this->productRepository->save($product); @@ -119,7 +139,7 @@ public function testViewActionCustomAttributeSetWithoutCountryOfManufacture(): v ->with( "Attempt to load value of nonexistent EAV attribute", [ - 'attribute_id' => $attributeCountryOfManufacture->getAttributeId(), + 'attribute_id' => $attributeCountryOfManufacture->getAttributeId(), 'entity_type' => ProductInterface::class, ] ); @@ -127,65 +147,6 @@ public function testViewActionCustomAttributeSetWithoutCountryOfManufacture(): v $this->dispatch(sprintf('catalog/product/view/id/%s/', $product->getId())); } - /** - * Setup logger mock to check there are no warning messages logged. - * - * @return MockObject - */ - private function setupLoggerMock() : MockObject - { - $logger = $this->getMockBuilder(LoggerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->_objectManager->addSharedInstance($logger, MagentoMonologLogger::class); - - return $logger; - } - - /** - * Get product instance by sku. - * - * @param string $sku - * @return Product - */ - private function getProductBySku(string $sku): Product - { - return $this->productRepository->get($sku); - } - - /** - * Get product attribute set by name. - * - * @param string $attributeSetName - * @return Set|null - */ - private function getProductAttributeSetByName(string $attributeSetName): ?Set - { - /** @var SortOrderBuilder $sortOrderBuilder */ - $sortOrderBuilder = $this->_objectManager->create(SortOrderBuilder::class); - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); - $searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); - $searchCriteriaBuilder->addFilter('entity_type_id', $this->productEntityType->getId()); - $attributeSetIdSortOrder = $sortOrderBuilder - ->setField('attribute_set_id') - ->setDirection(Collection::SORT_ORDER_DESC) - ->create(); - $searchCriteriaBuilder->addSortOrder($attributeSetIdSortOrder); - $searchCriteriaBuilder->setPageSize(1); - $searchCriteriaBuilder->setCurrentPage(1); - - /** @var AttributeSetSearchResults $searchResult */ - $searchResult = $this->attributeSetRepository->getList($searchCriteriaBuilder->create()); - $items = $searchResult->getItems(); - - if (count($items) > 0) { - return reset($items); - } - - return null; - } - /** * @magentoDataFixture Magento/Quote/_files/is_not_salable_product.php * @return void @@ -235,6 +196,104 @@ public function testProductNotVisibleIndividually(): void $this->assert404NotFound(); } + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testProductVisibleOnTwoWebsites(): void + { + $currentStore = $this->storeManager->getStore(); + $product = $this->productRepository->get('simple-on-two-websites'); + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + $this->cleanUpCachedObjects(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testRemoveProductFromOneWebsiteVisibility(): void + { + $websiteId = $this->websiteRepository->get('test')->getId(); + $currentStore = $this->storeManager->getStore(); + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $product = $this->updateProduct('simple-on-two-websites', ['website_ids' => [$websiteId]]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assert404NotFound(); + $this->cleanUpCachedObjects(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStore->getId()); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testProductAttributeByStores(): void + { + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $product = $this->productRepository->get('simple-on-two-websites'); + $currentStoreId = $this->storeManager->getStore()->getId(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + $product = $this->updateProduct($product, ['status' => 2]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assert404NotFound(); + $this->cleanUpCachedObjects(); + $this->storeManager->setCurrentStore($currentStoreId); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStoreId); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductWithoutWebsite(): void + { + $product = $this->updateProduct('simple2', ['website_ids' => []]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + + $this->assert404NotFound(); + } + + /** + * @param string|ProductInterface $product + * @param array $data + * @return ProductInterface + */ + public function updateProduct($product, array $data): ProductInterface + { + $product = is_string($product) ? $this->productRepository->get($product) : $product; + $product->addData($data); + + return $this->productRepository->save($product); + } + /** * @inheritdoc */ @@ -258,13 +317,69 @@ private function assertProductIsVisible(ProductInterface $product): void $this->getResponse()->getHttpResponseCode(), 'Wrong response code is returned' ); + $currentProduct = $this->registry->registry('current_product'); + $this->assertNotNull($currentProduct); $this->assertEquals( $product->getSku(), - $this->registry->registry('current_product')->getSku(), + $currentProduct->getSku(), 'Wrong product is registered' ); } + /** + * Clean up cached objects. + * + * @return void + */ + private function cleanUpCachedObjects(): void + { + $this->_objectManager->removeSharedInstance(Http::class); + $this->_objectManager->removeSharedInstance(Request::class); + $this->_objectManager->removeSharedInstance(Response::class); + $this->_request = null; + $this->_response = null; + } + + /** + * Setup logger mock to check there are no warning messages logged. + * + * @return MockObject + */ + private function setupLoggerMock(): MockObject + { + $logger = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->_objectManager->addSharedInstance($logger, MagentoMonologLogger::class); + + return $logger; + } + + /** + * Get product attribute set by name. + * + * @param string $attributeSetName + * @return Set|null + */ + private function getProductAttributeSetByName(string $attributeSetName): ?Set + { + $this->searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); + $this->searchCriteriaBuilder->addFilter('entity_type_id', $this->productEntityType->getId()); + $attributeSetIdSortOrder = $this->sortOrderBuilder + ->setField('attribute_set_id') + ->setDirection(Collection::SORT_ORDER_DESC) + ->create(); + $this->searchCriteriaBuilder->addSortOrder($attributeSetIdSortOrder); + $this->searchCriteriaBuilder->setPageSize(1); + $this->searchCriteriaBuilder->setCurrentPage(1); + /** @var AttributeSetSearchResults $searchResult */ + $searchResult = $this->attributeSetRepository->getList($this->searchCriteriaBuilder->create()); + $items = $searchResult->getItems(); + $result = count($items) > 0 ? reset($items) : null; + + return $result; + } + /** * Update product visibility * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php new file mode 100644 index 0000000000000..d710379ef0483 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php @@ -0,0 +1,47 @@ +create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); + +$currentStoreId = $storeManager->getStore()->getId(); +$storeId = $storeManager->getStore('fixturestore')->getId(); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product One') + ->setSku('simple-different-short-description') + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setShortDescription('First store view short description') + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); + +try { + $storeManager->setCurrentStore($store->getId()); + $product = $productRepository->get('simple-different-short-description', false, $storeId); + $product->setShortDescription('Second store view short description'); + $productRepository->save($product); +} finally { + $storeManager->setCurrentStore($currentStoreId); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php new file mode 100644 index 0000000000000..4191b9ff00acc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php @@ -0,0 +1,29 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('simple-different-short-description'); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/core_fixturestore_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php new file mode 100644 index 0000000000000..e4ce83f4135d6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php @@ -0,0 +1,36 @@ +create(ProductFactory::class); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$websiteRepository = $objectManager->create(WebsiteRepositoryInterface::class); +$websiteId = $websiteRepository->get('test')->getId(); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1, $websiteId]) + ->setName('Simple Product on two websites') + ->setSku('simple-on-two-websites') + ->setPrice(10) + ->setDescription('Description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php new file mode 100644 index 0000000000000..09cafb990a910 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php @@ -0,0 +1,29 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $productRepository->deleteById('simple-on-two-websites'); +} catch (NoSuchEntityException $e) { + //product already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; From bf9dd581a4a148693825f6f4afd70a7c5925c893 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Fri, 8 Nov 2019 12:55:47 +0200 Subject: [PATCH 1947/2437] MC-22831: [Function Test] MC-6192: Magento\FunctionalTestingFramework.functional.AdminCheckResultsOfColorAndOtherFiltersTest --- .../OpenProductAttributeFromSearchResultInGridActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml index c08c831c4f7d6..b8803fabda00d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml @@ -16,7 +16,7 @@ - + From 85415eff8ddd81c9b204ec6c093efe49d467ba9e Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Fri, 8 Nov 2019 12:07:03 +0100 Subject: [PATCH 1948/2437] Relocate Watermark insert on image after resize. #25514 --- app/code/Magento/MediaStorage/Service/ImageResize.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index 63353b2536a5a..d061ddbd3dc46 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -311,6 +311,10 @@ private function resize(array $imageParams, string $originalImagePath, string $o ] ); + if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { + $image->resize($imageParams['image_width'], $imageParams['image_height']); + } + if (isset($imageParams['watermark_file'])) { if ($imageParams['watermark_height'] !== null) { $image->setWatermarkHeight($imageParams['watermark_height']); @@ -331,9 +335,6 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->watermark($this->getWatermarkFilePath($imageParams['watermark_file'])); } - if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { - $image->resize($imageParams['image_width'], $imageParams['image_height']); - } $image->save($imageAsset->getPath()); if ($this->fileStorageDatabase->checkDbUsage()) { From 6c62e90eac946e84b2259a39e9c8d0a5534579f4 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 13:20:15 +0200 Subject: [PATCH 1949/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- .../Rule/Design/SerializationAware.php | 34 ------------- .../resources/rulesets/design.xml | 48 +++++++++---------- .../Magento/Test/Php/_files/phpmd/ruleset.xml | 5 +- 3 files changed, 27 insertions(+), 60 deletions(-) delete mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php deleted file mode 100644 index e38fba8558bad..0000000000000 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/SerializationAware.php +++ /dev/null @@ -1,34 +0,0 @@ -getName() === '__wakeup' || $method->getName() === '__sleep') { - $this->addViolation($method, [$method->getName(), $method->getParent()->getFullQualifiedName()]); - } - } -} diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml index 5f2461812bab7..73354c46d76b2 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml +++ b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml @@ -10,6 +10,29 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> + + + + + 1 + + + + + @@ -60,31 +83,6 @@ class OrderProcessor $currentOrder = $this->session->get('current_order'); ... } -} - ]]> - - - - - - - 2 - - - diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml index e65a9a089da9e..7a402818eb0b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml @@ -29,6 +29,9 @@ + + + @@ -43,8 +46,8 @@ + - From b11f053bc29564ef13fb071161b52276a5701ed0 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Fri, 8 Nov 2019 13:30:01 +0200 Subject: [PATCH 1950/2437] MC-20259: PUT /V1/products/:sku/media/:entryId is not idempotent --- .../Model/Product/Gallery/CreateHandler.php | 2 +- .../Product/Gallery/GalleryManagement.php | 20 ++-- .../Model/Product/Gallery/UpdateHandler.php | 11 ++ .../MediaGalleryProcessor.php | 12 +- .../Product/Gallery/GalleryManagementTest.php | 27 ++++- ...uteMediaGalleryManagementInterfaceTest.php | 107 ++++++++++++++---- 6 files changed, 135 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index b374b754d7de1..225a3a4c44a9b 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -162,7 +162,7 @@ public function execute($product, $arguments = []) if (!empty($image['removed'])) { $clearImages[] = $image['file']; - } elseif (empty($image['value_id'])) { + } elseif (empty($image['value_id']) || !empty($image['recreate'])) { $newFile = $this->moveImageFromTmp($image['file']); $image['new_file'] = $newFile; $newImages[$image['file']] = $image; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 9e5cf084c25a1..a9afb7cec45e2 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -71,10 +71,12 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $product->setMediaGalleryEntries($existingMediaGalleryEntries); try { $product = $this->productRepository->save($product); - } catch (InputException $inputException) { - throw $inputException; } catch (\Exception $e) { - throw new StateException(__("The product can't be saved.")); + if ($e instanceof InputException) { + throw $e; + } else { + throw new StateException(__("The product can't be saved.")); + } } foreach ($product->getMediaGalleryEntries() as $entry) { @@ -98,19 +100,13 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) ); } $found = false; + $entryTypes = (array)$entry->getTypes(); foreach ($existingMediaGalleryEntries as $key => $existingEntry) { - $entryTypes = (array)$entry->getTypes(); - $existingEntryTypes = (array)$existingMediaGalleryEntries[$key]->getTypes(); - $existingMediaGalleryEntries[$key]->setTypes(array_diff($existingEntryTypes, $entryTypes)); + $existingEntryTypes = (array)$existingEntry->getTypes(); + $existingEntry->setTypes(array_diff($existingEntryTypes, $entryTypes)); if ($existingEntry->getId() == $entry->getId()) { $found = true; - - $file = $entry->getContent(); - - if ($file && $file->getBase64EncodedData() || $entry->getFile()) { - $entry->setId(null); - } $existingMediaGalleryEntries[$key] = $entry; } } diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php index 189135776b68b..049846ef36490 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Model\Product\Gallery; +use Magento\Catalog\Model\ResourceModel\Product\Gallery; use Magento\Framework\EntityManager\Operation\ExtensionInterface; /** @@ -75,6 +76,16 @@ protected function processNewImage($product, array &$image) $image['value_id'], $product->getData($this->metadata->getLinkField()) ); + } elseif (!empty($image['recreate'])) { + $data['value_id'] = $image['value_id']; + $data['value'] = $image['file']; + $data['attribute_id'] = $this->getAttribute()->getAttributeId(); + + if (!empty($image['media_type'])) { + $data['media_type'] = $image['media_type']; + } + + $this->resourceModel->saveDataRow(Gallery::GALLERY_TABLE, $data); } return $data; diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php index fdcf2956dbdef..2aa92b8f0316e 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -92,7 +92,17 @@ public function processMediaGallery(ProductInterface $product, array $mediaGalle if ($updatedEntry['file'] === null) { unset($updatedEntry['file']); } - $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + if (isset($updatedEntry['content'])) { + //need to recreate image and reset object + $existingEntry['recreate'] = true; + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $newEntry = array_merge($existingEntry, $updatedEntry); + $newEntries[] = $newEntry; + unset($existingMediaGallery[$key]); + } else { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + } } else { //set the removed flag $existingEntry['removed'] = true; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php index 1d12645019d1e..6d4e98b60ad18 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php @@ -7,6 +7,9 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Gallery; +/** + * Tests for \Magento\Catalog\Model\Product\Gallery\GalleryManagement. + */ class GalleryManagementTest extends \PHPUnit\Framework\TestCase { /** @@ -39,11 +42,16 @@ class GalleryManagementTest extends \PHPUnit\Framework\TestCase */ protected $attributeValueMock; + /** + * @inheritdoc + */ protected function setUp() { $this->productRepositoryMock = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); $this->contentValidatorMock = $this->createMock(\Magento\Framework\Api\ImageContentValidatorInterface::class); - $this->productMock = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ + $this->productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ 'setStoreId', 'getData', 'getStoreId', @@ -51,7 +59,8 @@ protected function setUp() 'getCustomAttribute', 'getMediaGalleryEntries', 'setMediaGalleryEntries', - ]); + ] + ); $this->mediaGalleryEntryMock = $this->createMock(\Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class); $this->model = new \Magento\Catalog\Model\Product\Gallery\GalleryManagement( @@ -151,6 +160,8 @@ public function testUpdateWithNonExistingImage() $existingEntryMock->expects($this->once())->method('getId')->willReturn(43); $this->productMock->expects($this->once())->method('getMediaGalleryEntries') ->willReturn([$existingEntryMock]); + $existingEntryMock->expects($this->once())->method('getTypes')->willReturn([]); + $entryMock->expects($this->once())->method('getTypes')->willReturn([]); $entryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->model->update($productSku, $entryMock); } @@ -172,12 +183,19 @@ public function testUpdateWithCannotSaveException() $existingEntryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->productMock->expects($this->once())->method('getMediaGalleryEntries') ->willReturn([$existingEntryMock]); + $existingEntryMock->expects($this->once())->method('getTypes')->willReturn([]); + $entryMock->expects($this->once())->method('getTypes')->willReturn([]); $entryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock) ->willThrowException(new \Exception()); $this->model->update($productSku, $entryMock); } + /** + * Check update gallery entry behavior. + * + * @return void + */ public function testUpdate() { $productSku = 'testProduct'; @@ -203,14 +221,13 @@ public function testUpdate() ->willReturn([$existingEntryMock, $existingSecondEntryMock]); $entryMock->expects($this->exactly(2))->method('getId')->willReturn($entryId); - $entryMock->expects($this->once())->method('getFile')->willReturn("base64"); - $entryMock->expects($this->once())->method('setId')->with(null); - $entryMock->expects($this->exactly(2))->method('getTypes')->willReturn(['image']); + $entryMock->expects($this->once())->method('getTypes')->willReturn(['image']); $this->productMock->expects($this->once())->method('setMediaGalleryEntries') ->with([$entryMock, $existingSecondEntryMock]) ->willReturnSelf(); $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock); + $this->assertTrue($this->model->update($productSku, $entryMock)); } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php index 1cd299149507c..a192936aeaccf 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php @@ -11,11 +11,11 @@ use Magento\Framework\Api\Data\ImageContentInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Model\ProductFactory; -use Magento\TestFramework\ObjectManager; use Magento\Catalog\Model\Product\Attribute\Backend\Media\ImageEntryConverter; use Magento\Catalog\Model\ProductRepository; use Magento\Framework\Webapi\Rest\Request; use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Framework\ObjectManagerInterface; /** * Class ProductAttributeMediaGalleryManagementInterfaceTest @@ -48,11 +48,18 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends WebapiAbstract */ protected $testImagePath; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** * @inheritDoc */ protected function setUp() { + $this->objectManager = Bootstrap::getObjectManager(); + $this->createServiceInfo = [ 'rest' => [ 'resourcePath' => '/V1/products/simple/media', @@ -98,9 +105,7 @@ protected function setUp() */ protected function getTargetSimpleProduct() { - $objectManager = Bootstrap::getObjectManager(); - - return $objectManager->get(ProductFactory::class)->create()->load(1); + return $this->objectManager->get(ProductFactory::class)->create()->load(1); } /** @@ -241,6 +246,10 @@ public function testCreateWithNotDefaultStoreId() */ public function testUpdate() { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); $requestData = [ 'sku' => 'simple', 'entry' => [ @@ -257,19 +266,48 @@ public function testUpdate() . '/' . $this->getTargetGalleryEntryId(); $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'all')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/m/a/magento_image.jpg', 'Updated Image Text'); + $this->assertEquals(10, $updatedImage['position_default']); + $this->assertEquals(1, $updatedImage['disabled_default']); + } - $targetProduct = $this->getTargetSimpleProduct(); - $this->assertEquals('/m/a/magento_image.jpg', $targetProduct->getData('thumbnail')); - $this->assertEquals('no_selection', $targetProduct->getData('image')); - $this->assertEquals('no_selection', $targetProduct->getData('small_image')); - $mediaGallery = $targetProduct->getData('media_gallery'); - $this->assertCount(1, $mediaGallery['images']); - $updatedImage = array_shift($mediaGallery['images']); - $this->assertEquals('Updated Image Text', $updatedImage['label']); - $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); - $this->assertEquals(10, $updatedImage['position']); - $this->assertEquals(1, $updatedImage['disabled']); - $this->assertEquals('Updated Image Text', $updatedImage['label_default']); + /** + * Update media gallery entity with new image. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_with_image.php + * @return void + */ + public function testUpdateWithNewImage(): void + { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); + + $requestData = [ + 'sku' => 'simple', + 'entry' => [ + 'id' => $this->getTargetGalleryEntryId(), + 'label' => 'Updated Image Text', + 'position' => 10, + 'types' => ['thumbnail'], + 'disabled' => true, + 'media_type' => 'image', + 'content' => [ + 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' + . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + 'type' => 'image/png', + 'name' => 'testname_updated.png', + ], + ], + ]; + + $this->updateServiceInfo['rest']['resourcePath'] = $this->updateServiceInfo['rest']['resourcePath'] + . '/' . $this->getTargetGalleryEntryId(); + + $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'all')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/t/e/testname_updated.png', 'Updated Image Text'); $this->assertEquals(10, $updatedImage['position_default']); $this->assertEquals(1, $updatedImage['disabled_default']); } @@ -281,6 +319,11 @@ public function testUpdate() */ public function testUpdateWithNotDefaultStoreId() { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); + $requestData = [ 'sku' => 'simple', 'entry' => [ @@ -297,21 +340,36 @@ public function testUpdateWithNotDefaultStoreId() . '/' . $this->getTargetGalleryEntryId(); $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'default')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/m/a/magento_image.jpg', 'Image Alt Text'); + $this->assertEquals(1, $updatedImage['position_default']); + $this->assertEquals(0, $updatedImage['disabled_default']); + } + /** + * Check that Media Gallery data is correct. + * + * @param int $imageId + * @param string $file + * @param string $label + * @return array + */ + private function assertMediaGalleryData(int $imageId, string $file, string $label): array + { $targetProduct = $this->getTargetSimpleProduct(); - $this->assertEquals('/m/a/magento_image.jpg', $targetProduct->getData('thumbnail')); + $this->assertEquals($file, $targetProduct->getData('thumbnail')); + $this->assertEquals('no_selection', $targetProduct->getData('image')); + $this->assertEquals('no_selection', $targetProduct->getData('small_image')); $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); $updatedImage = array_shift($mediaGallery['images']); - // Not default store view values were updated + $this->assertEquals($imageId, $updatedImage['value_id']); $this->assertEquals('Updated Image Text', $updatedImage['label']); - $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); + $this->assertEquals($file, $updatedImage['file']); $this->assertEquals(10, $updatedImage['position']); $this->assertEquals(1, $updatedImage['disabled']); - // Default store view values were not updated - $this->assertEquals('Image Alt Text', $updatedImage['label_default']); - $this->assertEquals(1, $updatedImage['position_default']); - $this->assertEquals(0, $updatedImage['disabled_default']); + $this->assertEquals($label, $updatedImage['label_default']); + + return $updatedImage; } /** @@ -564,9 +622,8 @@ public function testGet() { $productSku = 'simple'; - $objectManager = ObjectManager::getInstance(); /** @var ProductRepository $repository */ - $repository = $objectManager->create(ProductRepository::class); + $repository = $this->objectManager->create(ProductRepository::class); $product = $repository->get($productSku); $image = current($product->getMediaGallery('images')); $imageId = $image['value_id']; From 5da4d3573cf27c9e3d8e98fe03a32b09f0915179 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Fri, 8 Nov 2019 13:44:29 +0200 Subject: [PATCH 1951/2437] MC-20682: Storefront: Add/remove product from other storeviews and websites --- .../Catalog/Block/Product/ViewTest.php | 2 +- .../Catalog/Controller/Product/ViewTest.php | 42 ++++--------------- ...multistore_different_short_description.php | 6 +-- 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php index ca2f915b42c18..59582f313cf55 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php @@ -67,9 +67,9 @@ class ViewTest extends TestCase protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->block = $this->objectManager->create(View::class); $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(View::class); $this->registry = $this->objectManager->get(Registry::class); $this->json = $this->objectManager->get(Json::class); $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php index 495fe4aa710df..5709be55988b9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php @@ -12,16 +12,13 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; -use Magento\Eav\Model\AttributeSetSearchResults; -use Magento\Eav\Model\Entity\Attribute\Set; use Magento\Eav\Model\Entity\Type; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\App\Http; -use Magento\Framework\Data\Collection; use Magento\Framework\Registry; -use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; use Magento\TestFramework\Request; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; @@ -66,15 +63,15 @@ class ViewTest extends AbstractController /** @var StoreManagerInterface */ private $storeManager; - /** @var WebsiteRepositoryInterface */ - private $websiteRepository; - /** @var SortOrderBuilder */ private $sortOrderBuilder; /** @var SearchCriteriaBuilder */ private $searchCriteriaBuilder; + /** @var GetAttributeSetByName */ + private $getAttributeSetByName; + /** * @inheritdoc */ @@ -89,9 +86,9 @@ protected function setUp() ->loadByCode(Product::ENTITY); $this->registry = $this->_objectManager->get(Registry::class); $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); - $this->websiteRepository = $this->_objectManager->get(WebsiteRepositoryInterface::class); $this->sortOrderBuilder = $this->_objectManager->create(SortOrderBuilder::class); $this->searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); } /** @@ -128,7 +125,7 @@ public function testViewActionCustomAttributeSetWithoutCountryOfManufacture(): v /** @var MockObject|LoggerInterface $logger */ $logger = $this->setupLoggerMock(); $product = $this->productRepository->get('simple_with_com'); - $attributeSetCustom = $this->getProductAttributeSetByName('custom_attribute_set_wout_com'); + $attributeSetCustom = $this->getAttributeSetByName->execute('custom_attribute_set_wout_com'); $product->setAttributeSetId($attributeSetCustom->getAttributeSetId()); $this->productRepository->save($product); @@ -226,7 +223,7 @@ public function testProductVisibleOnTwoWebsites(): void */ public function testRemoveProductFromOneWebsiteVisibility(): void { - $websiteId = $this->websiteRepository->get('test')->getId(); + $websiteId = $this->storeManager->getWebsite('test')->getId(); $currentStore = $this->storeManager->getStore(); $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); $product = $this->updateProduct('simple-on-two-websites', ['website_ids' => [$websiteId]]); @@ -355,31 +352,6 @@ private function setupLoggerMock(): MockObject return $logger; } - /** - * Get product attribute set by name. - * - * @param string $attributeSetName - * @return Set|null - */ - private function getProductAttributeSetByName(string $attributeSetName): ?Set - { - $this->searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); - $this->searchCriteriaBuilder->addFilter('entity_type_id', $this->productEntityType->getId()); - $attributeSetIdSortOrder = $this->sortOrderBuilder - ->setField('attribute_set_id') - ->setDirection(Collection::SORT_ORDER_DESC) - ->create(); - $this->searchCriteriaBuilder->addSortOrder($attributeSetIdSortOrder); - $this->searchCriteriaBuilder->setPageSize(1); - $this->searchCriteriaBuilder->setCurrentPage(1); - /** @var AttributeSetSearchResults $searchResult */ - $searchResult = $this->attributeSetRepository->getList($this->searchCriteriaBuilder->create()); - $items = $searchResult->getItems(); - $result = count($items) > 0 ? reset($items) : null; - - return $result; - } - /** * Update product visibility * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php index d710379ef0483..41998799ae104 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php @@ -21,7 +21,7 @@ $productFactory = $objectManager->get(ProductFactory::class); $currentStoreId = $storeManager->getStore()->getId(); -$storeId = $storeManager->getStore('fixturestore')->getId(); +$secondStoreId = $storeManager->getStore('fixturestore')->getId(); $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) @@ -38,8 +38,8 @@ $productRepository->save($product); try { - $storeManager->setCurrentStore($store->getId()); - $product = $productRepository->get('simple-different-short-description', false, $storeId); + $storeManager->setCurrentStore($secondStoreId); + $product = $productRepository->get('simple-different-short-description', false, $secondStoreId); $product->setShortDescription('Second store view short description'); $productRepository->save($product); } finally { From 464d93c0468eaeab065d4ff70fc8db08a2061777 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Fri, 8 Nov 2019 14:25:18 +0200 Subject: [PATCH 1952/2437] MC-20682: Storefront: Add/remove product from other storeviews and websites --- .../Magento/Catalog/_files/product_two_websites.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php index e4ce83f4135d6..92da39ffc6980 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php @@ -16,15 +16,19 @@ require __DIR__ . '/../../Store/_files/second_website_with_two_stores.php'; $objectManager = Bootstrap::getObjectManager(); +/** @var ProductFactory $productFactory */ $productFactory = $objectManager->create(ProductFactory::class); +/** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ $websiteRepository = $objectManager->create(WebsiteRepositoryInterface::class); $websiteId = $websiteRepository->get('test')->getId(); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) ->setAttributeSetId(4) - ->setWebsiteIds([1, $websiteId]) + ->setWebsiteIds([$defaultWebsiteId, $websiteId]) ->setName('Simple Product on two websites') ->setSku('simple-on-two-websites') ->setPrice(10) From 64bdaf3bfd9c834e2ee0b5469cd15210583fe843 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Fri, 8 Nov 2019 15:12:19 +0200 Subject: [PATCH 1953/2437] MC-20682: Storefront: Add/remove product from other storeviews and websites --- .../Magento/Catalog/Controller/Product/ViewTest.php | 5 ----- .../product_multistore_different_short_description.php | 4 ++-- .../Magento/Catalog/_files/product_two_websites.php | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php index 5709be55988b9..d5d222ad608f0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php @@ -13,7 +13,6 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; use Magento\Eav\Model\Entity\Type; -use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\App\Http; use Magento\Framework\Registry; @@ -66,9 +65,6 @@ class ViewTest extends AbstractController /** @var SortOrderBuilder */ private $sortOrderBuilder; - /** @var SearchCriteriaBuilder */ - private $searchCriteriaBuilder; - /** @var GetAttributeSetByName */ private $getAttributeSetByName; @@ -87,7 +83,6 @@ protected function setUp() $this->registry = $this->_objectManager->get(Registry::class); $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); $this->sortOrderBuilder = $this->_objectManager->create(SortOrderBuilder::class); - $this->searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php index 41998799ae104..4f0d1ce6d47ff 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php @@ -25,8 +25,8 @@ $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) - ->setAttributeSetId(4) - ->setWebsiteIds([1]) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$storeManager->getWebsite(true)->getId()]) ->setName('Simple Product One') ->setSku('simple-different-short-description') ->setPrice(10) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php index 92da39ffc6980..d7150d7ec41b1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php @@ -27,7 +27,7 @@ $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([$defaultWebsiteId, $websiteId]) ->setName('Simple Product on two websites') ->setSku('simple-on-two-websites') From 2263d2e9fe2359b86c26dc91597cf50b5a87def4 Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Fri, 8 Nov 2019 14:12:36 +0100 Subject: [PATCH 1954/2437] Watermark added explode on size ([Width]x[Height] to $size['Width','Height']) . #25514 --- .../Catalog/Model/Product/Image/ParamsBuilder.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index 4b7a623b15c19..c0f4e83ef3de4 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -130,10 +130,12 @@ private function getWatermark(string $type, int $scopeId = null): array ); if ($file) { - $size = $this->scopeConfig->getValue( - "design/watermark/{$type}_size", - ScopeInterface::SCOPE_STORE, - $scopeId + $size = explode( + 'x', + $this->scopeConfig->getValue( + "design/watermark/{$type}_size", + ScopeInterface::SCOPE_STORE + ) ); $opacity = $this->scopeConfig->getValue( "design/watermark/{$type}_imageOpacity", @@ -145,8 +147,8 @@ private function getWatermark(string $type, int $scopeId = null): array ScopeInterface::SCOPE_STORE, $scopeId ); - $width = !empty($size['width']) ? $size['width'] : null; - $height = !empty($size['height']) ? $size['height'] : null; + $width = !empty($size['0']) ? $size['0'] : null; + $height = !empty($size['1']) ? $size['1'] : null; return [ 'watermark_file' => $file, From d5249b73d0225eb3694930568c94c36a6c8fe557 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 15:21:24 +0200 Subject: [PATCH 1955/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- .../resources/rulesets/design.xml | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml index 73354c46d76b2..53f2fe4a0084e 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml +++ b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml @@ -10,29 +10,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> - - - - - 1 - - - - - From 040954aac7d472d2f2190617ab920a0262e23e2f Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 15:47:29 +0200 Subject: [PATCH 1956/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- .../static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml index 7a402818eb0b9..35378b0d8c9b2 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml @@ -46,7 +46,6 @@ - From d3da0bba129e7216deee7de2c81ccb2c03bbd63d Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 16:16:08 +0200 Subject: [PATCH 1957/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- app/code/Magento/Backend/Model/Auth/Session.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php index 31709705f2507..809b78b7b98bc 100644 --- a/app/code/Magento/Backend/Model/Auth/Session.php +++ b/app/code/Magento/Backend/Model/Auth/Session.php @@ -20,6 +20,7 @@ * @method \Magento\Backend\Model\Auth\Session setUpdatedAt(int $value) * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @todo implement solution that keeps is_first_visit flag in session during redirects * @api * @since 100.0.2 From 0ffbe25590f893cea39c31a7521c2ca9c68fd324 Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi Date: Fri, 8 Nov 2019 16:31:47 +0200 Subject: [PATCH 1958/2437] Magento#25529: Wrong program code in "Magento/Checkout/view/frontend/web/js/view/summary/shipping.js" - added additional checking of shippingMethod type - need to be an array - fixed condition to output "shipping method title" --- .../view/frontend/web/js/view/summary/shipping.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js index 22e278bea947e..5886dc6a69b8f 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js @@ -5,10 +5,11 @@ define([ 'jquery', + 'underscore', 'Magento_Checkout/js/view/summary/abstract-total', 'Magento_Checkout/js/model/quote', 'Magento_SalesRule/js/view/summary/discount' -], function ($, Component, quote, discountView) { +], function ($, _, Component, quote, discountView) { 'use strict'; return Component.extend({ @@ -22,7 +23,7 @@ define([ * @return {*} */ getShippingMethodTitle: function () { - var shippingMethod = '', + var shippingMethod, shippingMethodTitle = ''; if (!this.isCalculated()) { @@ -30,11 +31,15 @@ define([ } shippingMethod = quote.shippingMethod(); + if (!_.isArray(shippingMethod)) { + return ''; + } + if (typeof shippingMethod['method_title'] !== 'undefined') { shippingMethodTitle = ' - ' + shippingMethod['method_title']; } - return shippingMethod ? + return shippingMethodTitle ? shippingMethod['carrier_title'] + shippingMethodTitle : shippingMethod['carrier_title']; }, From 322709d1572caec44189edb1e8b3ca5f0be628fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= Date: Fri, 8 Nov 2019 15:42:36 +0100 Subject: [PATCH 1959/2437] M2C-22619 Add check if found elements are not nested to prevent from processing panels incorrectly --- lib/web/mage/tabs.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index 7a313bb7ac1b9..d39a3902e82a9 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -102,21 +102,31 @@ define([ * @private */ _processPanels: function () { - this.contents = this.element.children(this.options.content); + var isNotNested = this._isNotNested.bind(this); - this.collapsibles = this.element.children(this.options.collapsibleElement); + this.contents = this.element + .find(this.options.content) + .filter(isNotNested); + + this.collapsibles = this.element + .find(this.options.collapsibleElement) + .filter(isNotNested); this.collapsibles .attr('role', 'presentation') .parent() .attr('role', 'tablist'); - this.headers = this.element.children(this.options.header); + this.headers = this.element + .find(this.options.header) + .filter(isNotNested); if (this.headers.length === 0) { this.headers = this.collapsibles; } - this.triggers = this.element.children(this.options.trigger); + this.triggers = this.element + .find(this.options.trigger) + .filter(isNotNested); if (this.triggers.length === 0) { this.triggers = this.headers; @@ -124,6 +134,17 @@ define([ this._callCollapsible(); }, + /** + * @param {HTMLElement} elem + * @private + * @return {Boolean} + */ + _isNotNested: function (index, element) { + var parentContent = $(element).parents(this.options.content); + + return !parentContent.length || !this.element.find(parentContent).length; + }, + /** * Setting the disabled and active tabs and calling instantiation of collapsible * @private From 5a24a699c6b3ef06908a8f7dce8353df4e992ce0 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 17:33:28 +0200 Subject: [PATCH 1960/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- .../Backend/Model/Locale/ResolverTest.php | 32 +++++++++++++++++++ .../Magento/Test/Php/_files/phpmd/ruleset.xml | 3 -- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php index d080a2360b6eb..2754e055b71fe 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php @@ -76,6 +76,38 @@ public function testSetLocaleWithRequestLocale() $this->_checkSetLocale('de_DE'); } + /** + * Tests setLocale() with parameter + * + * @param string|null $localeParam + * @param string|null $localeRequestParam + * @param string $localeExpected + * @dataProvider setLocaleWithParameterDataProvider + */ + public function testSetLocaleWithParameter( + ?string $localeParam, + ?string $localeRequestParam, + string $localeExpected + ) { + $request = Bootstrap::getObjectManager() + ->get(\Magento\Framework\App\RequestInterface::class); + $request->setPostValue(['locale' => $localeRequestParam]); + $this->_model->setLocale($localeParam); + $this->assertEquals($localeExpected, $this->_model->getLocale()); + } + + /** + * @return array + */ + public function setLocaleWithParameterDataProvider(): array + { + return [ + ['ko_KR', 'ja_JP', 'ja_JP'], + ['ko_KR', null, 'ko_KR'], + [null, 'ja_JP', 'ja_JP'], + ]; + } + /** * Check set locale * diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml index 35378b0d8c9b2..0e3b5fa3d341c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml @@ -29,9 +29,6 @@ - - - From d13cd157c4abc215fea2c23f5be63e10ccead415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= Date: Fri, 8 Nov 2019 16:55:42 +0100 Subject: [PATCH 1961/2437] M2C-22619 Adjust indentation --- lib/web/mage/tabs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index d39a3902e82a9..d55ef6e246d4d 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -143,7 +143,7 @@ define([ var parentContent = $(element).parents(this.options.content); return !parentContent.length || !this.element.find(parentContent).length; - }, + }, /** * Setting the disabled and active tabs and calling instantiation of collapsible From 02db2408258a63c4cac30637dd317c051637dd76 Mon Sep 17 00:00:00 2001 From: Dave Macaulay Date: Fri, 8 Nov 2019 09:58:34 -0600 Subject: [PATCH 1962/2437] PB-225: Page builder JS and CSS files are not loading and going to 404 --- lib/web/mage/requirejs/static.js | 65 ++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/web/mage/requirejs/static.js b/lib/web/mage/requirejs/static.js index 898850cb2948b..6b3b4717c3215 100644 --- a/lib/web/mage/requirejs/static.js +++ b/lib/web/mage/requirejs/static.js @@ -2,13 +2,66 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +var storageShim = { + _data: {}, + + /** + * Sets value of the specified item. + * + * @param {String} key - Key of the property. + * @param {*} value - Properties' value. + */ + setItem: function (key, value) { + 'use strict'; + + this._data[key] = value + ''; + }, + + /** + * Retrieves specified item. + * + * @param {String} key - Key of the property to be retrieved. + */ + getItem: function (key) { + 'use strict'; + + return this._data[key]; + }, + + /** + * Removes specified item. + * + * @param {String} key - Key of the property to be removed. + */ + removeItem: function (key) { + 'use strict'; + + delete this._data[key]; + }, + + /** + * Removes all items. + */ + clear: function () { + 'use strict'; + + this._data = {}; + } +}; + define('buildTools', [ ], function () { 'use strict'; - var storage = window.localStorage, + var storage, storeName = 'buildDisabled'; + try { + storage = window.localStorage; + } catch (e) { + storage = storageShim; + } + return { isEnabled: storage.getItem(storeName) === null, @@ -70,9 +123,15 @@ define('statistician', [ ], function () { 'use strict'; - var storage = window.localStorage, + var storage, stringify = JSON.stringify.bind(JSON); + try { + storage = window.localStorage; + } catch (e) { + storage = storageShim; + } + /** * Removes duplicated entries of array, returning new one. * @@ -326,4 +385,4 @@ define('text', [ }; return text; -}); +}); \ No newline at end of file From 153197f48dbcddbb822025bcdbaaeef5c627d9b4 Mon Sep 17 00:00:00 2001 From: Viktor Sevch Date: Fri, 8 Nov 2019 18:09:39 +0200 Subject: [PATCH 1963/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- app/code/Magento/User/Model/User.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 5e482e1af31d5..3b41d529b542b 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -413,10 +413,6 @@ public function getRoles() */ public function getRole() { - if ($this->getData('extracted_role')) { - $this->_role = $this->getData('extracted_role'); - $this->unsetData('extracted_role'); - } if (null === $this->_role) { $this->_role = $this->_roleFactory->create(); $roles = $this->getRoles(); From 69b70b908ad7cb5b32df53ce1a00328897270487 Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Fri, 8 Nov 2019 11:04:55 -0600 Subject: [PATCH 1964/2437] MC-22838: Required input type values validation does not work correctly - Resolve remaining test issues --- app/code/Magento/CustomerGraphQl/etc/schema.graphqls | 2 +- .../AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php | 2 +- .../GraphQl/Quote/Customer/AddVirtualProductToCartTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 86ab39bbee25c..9aa1fdaa841e4 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -61,7 +61,7 @@ input CustomerInput { middlename: String @doc(description: "The customer's middle name") lastname: String @doc(description: "The customer's family name") suffix: String @doc(description: "A value such as Sr., Jr., or III") - email: String! @doc(description: "The customer's email address. Required") + email: String @doc(description: "The customer's email address. Required for customer creation") dob: String @doc(description: "Deprecated: Use `date_of_birth` instead") date_of_birth: String @doc(description: "The customer's date of birth") taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php index c9bfc0c950142..e9ab4456fae81 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php @@ -136,7 +136,7 @@ public function testSetPaymentInvalidInput(\Closure $getMutationClosure, array $ $setPaymentMutation = $getMutationClosure($maskedQuoteId); - foreach($expectedMessages as $expectedMessage){ + foreach ($expectedMessages as $expectedMessage) { $this->expectExceptionMessage($expectedMessage); } $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php index ae0208ca3101b..64047e2087401 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php @@ -57,7 +57,7 @@ public function testAddVirtualProductToCart() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing + * @expectedExceptionMessage Field AddSimpleProductsToCartInput.cart_id of required type String! was not provided. */ public function testAddVirtualProductToCartIfCartIdIsMissed() { From cbeb99ca2e35a173ad1b67c4a76bddf263ab4197 Mon Sep 17 00:00:00 2001 From: Lena Orobei Date: Fri, 8 Nov 2019 11:14:16 -0600 Subject: [PATCH 1965/2437] magento/graphql-ce#416: graphql-input provides to Customer as many errors as appeared instead of last one like on Magento Storefront --- .../Model/Product/Type/AbstractType.php | 2 +- .../Model/Cart/AddProductsToCart.php | 30 +++++-------------- .../Model/Cart/AddSimpleProductToCart.php | 7 ++++- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 7321a95c7aa68..e6804d9246faa 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -603,7 +603,7 @@ protected function _prepareOptions(\Magento\Framework\DataObject $buyRequest, $p } } if (count($results) > 0) { - throw new LocalizedException(__(implode("\n", array_unique($results)))); + throw new LocalizedException(__(implode("\n", $results))); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index 005cf3a10ca80..91c77a1a3ecc5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -8,7 +8,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\Message\AbstractMessage; +use Magento\Framework\Message\MessageInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; @@ -55,29 +55,15 @@ public function execute(Quote $cart, array $cartItems): void } if ($cart->getData('has_error')) { - throw new GraphQlInputException( - __('Shopping cart error: %message', ['message' => $this->getCartErrors($cart)]) - ); + $e = new GraphQlInputException(__('Shopping cart errors')); + $errors = $cart->getErrors(); + foreach ($errors as $error) { + /** @var MessageInterface $error */ + $e->addError(new GraphQlInputException(__($error->getText()))); + } + throw $e; } $this->cartRepository->save($cart); } - - /** - * Collecting cart errors - * - * @param Quote $cart - * @return string - */ - private function getCartErrors(Quote $cart): string - { - $errorMessages = []; - - /** @var AbstractMessage $error */ - foreach ($cart->getErrors() as $error) { - $errorMessages[] = $error->getText(); - } - - return implode(PHP_EOL, $errorMessages); - } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 11719db2d1b8f..83c1d03f132db 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -72,7 +72,12 @@ public function execute(Quote $cart, array $cartItemData): void } if (is_string($result)) { - throw new GraphQlInputException(__($result)); + $e = new GraphQlInputException(__('Cannot add product to cart')); + $errors = array_unique(explode("\n", $result)); + foreach ($errors as $error) { + $e->addError(new GraphQlInputException(__($error))); + } + throw $e; } } From 68e58e2e0b789488e583a8fa486556d4df0381e6 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Fri, 8 Nov 2019 19:52:26 +0200 Subject: [PATCH 1966/2437] MC-22831: [Function Test] MC-6192: Magento\FunctionalTestingFramework.functional.AdminCheckResultsOfColorAndOtherFiltersTest --- .../Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml | 1 + .../Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml index 33561d7c3b03e..6434d74b28754 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml @@ -25,6 +25,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml index 50d192a27e46d..c36c29ce594d0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml @@ -51,6 +51,9 @@ + + + From a30b550a7a8c06026d5d05addd9a4b695a2e1d1a Mon Sep 17 00:00:00 2001 From: Dave Macaulay Date: Fri, 8 Nov 2019 13:25:32 -0600 Subject: [PATCH 1967/2437] PB-76: PageBuilder Product List Template Is Missing Product Color & Size Options in Admin --- .../AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml index dd33c279dd182..f3e31d23f715b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml @@ -23,9 +23,6 @@ - - - From 891e5a4e91ce5ef261cb22c457210322bf4ddcd9 Mon Sep 17 00:00:00 2001 From: Lena Orobei Date: Fri, 8 Nov 2019 13:34:24 -0600 Subject: [PATCH 1968/2437] magento/graphql-ce#1029: Add Postcode as required depending of the country Mutation createCustomerAddress - added test --- .../Customer/CreateCustomerAddressTest.php | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 408a254f65f2e..5eed8576db0de 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -358,6 +358,67 @@ public function testCreateCustomerAddressWithRedundantStreetLine() $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @magentoConfigFixture default_store general/country/optional_zip_countries UA + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithOptionalZipCode() + { + $newAddress = [ + 'country_code' => 'UA', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<graphQlMutation( + $mutation, + [], + '', + $this->getCustomerAuthHeaders($userName, $password) + ); + $this->assertNotEmpty($response['createCustomerAddress']['id']); + } + /** * Create new address with invalid input * From ccc4301356130c4bac27ab99e2fe4f1fdcba8ec3 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 8 Nov 2019 14:02:50 -0600 Subject: [PATCH 1969/2437] MC-20533: Large amount of Block Cache. --- .../Product/Renderer/Listing/Configurable.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index 2e4980c2fbfd0..5f9492f005ec6 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -9,7 +9,6 @@ use Magento\Catalog\Helper\Product as CatalogProduct; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Layer\Resolver; -use Magento\Catalog\Model\Layer\Category as CategoryLayer; use Magento\ConfigurableProduct\Helper\Data; use Magento\ConfigurableProduct\Model\ConfigurableAttributeData; use Magento\Customer\Helper\Session\CurrentCustomer; @@ -154,7 +153,7 @@ public function getJsonConfig() $this->unsetData('allow_products'); return parent::getJsonConfig(); } - + /** * Composes configuration for js price format * @@ -257,16 +256,4 @@ private function getLayeredAttributesIfExists(Product $configurableProduct, arra return $layeredAttributes; } - - /** - * @inheritdoc - */ - public function getCacheKeyInfo() - { - $cacheKeyInfo = parent::getCacheKeyInfo(); - /** @var CategoryLayer $catalogLayer */ - $catalogLayer = $this->layerResolver->get(); - $cacheKeyInfo[] = $catalogLayer->getStateKey(); - return $cacheKeyInfo; - } } From 0e5c82f7151913eeb9d11943abe05f70589da91e Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Fri, 8 Nov 2019 14:10:04 -0600 Subject: [PATCH 1970/2437] MC-22838: Required input type values validation does not work correctly - Resolve remaining test issues --- .../Magento/GraphQl/Customer/CreateCustomerTest.php | 4 ++-- .../GraphQl/Quote/Customer/AddVirtualProductToCartTest.php | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index 0be968d6d340d..618fe289ad566 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -114,7 +114,7 @@ public function testCreateCustomerAccountWithoutPassword() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage "input" value should be specified */ public function testCreateCustomerIfInputDataIsEmpty() { @@ -140,7 +140,7 @@ public function testCreateCustomerIfInputDataIsEmpty() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage Required parameters are missing: Email */ public function testCreateCustomerIfEmailMissed() { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php index 64047e2087401..a7a3028f2a369 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php @@ -109,8 +109,6 @@ public function testAddVirtualProductToCartIfCartIdIsEmpty() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_items" is missing */ public function testAddVirtualProductToCartIfCartItemsAreMissed() { @@ -129,6 +127,11 @@ public function testAddVirtualProductToCartIfCartItemsAreMissed() } } QUERY; + $this->expectException(\Exception::class); + $this->expectExceptionMessage( + 'Field AddSimpleProductsToCartInput.cart_items of required type [SimpleProductCartItemInput]!' + . ' was not provided.' + ); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } From d6c184d5239502a65c1350d26c505bdd3642b9e1 Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi Date: Fri, 8 Nov 2019 22:40:15 +0200 Subject: [PATCH 1971/2437] Magento#25529: Wrong program code in "Magento/Checkout/view/frontend/web/js/view/summary/shipping.js" - fixed condition for shippingMethod --- .../Checkout/view/frontend/web/js/view/summary/shipping.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js index 5886dc6a69b8f..a0bbc9dd55bff 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js @@ -31,7 +31,7 @@ define([ } shippingMethod = quote.shippingMethod(); - if (!_.isArray(shippingMethod)) { + if (!_.isArray(shippingMethod) && !_.isObject(shippingMethod)) { return ''; } From cb96e87ad9d6285880e2a8b09ace7aa1462294cd Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi Date: Fri, 8 Nov 2019 14:57:12 -0600 Subject: [PATCH 1972/2437] MC-20533: Large amount of Block Cache. --- .../Block/Product/Renderer/Listing/Configurable.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index 5f9492f005ec6..6e0a1e8d01360 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -241,9 +241,12 @@ private function getLayeredAttributesIfExists(Product $configurableProduct, arra $layeredAttributes = []; - $configurableAttributes = array_map(function ($attribute) { - return $attribute->getAttributeCode(); - }, $configurableAttributes); + $configurableAttributes = array_map( + function ($attribute) { + return $attribute->getAttributeCode(); + }, + $configurableAttributes + ); $commonAttributeCodes = array_intersect( $configurableAttributes, From 33c761e0023758a409e662643925ac7570a73dc5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra Date: Fri, 8 Nov 2019 23:28:30 +0200 Subject: [PATCH 1973/2437] MC-22829: Revert ticket MAGETWO-96975 which introduce BIC on 2.3.4 --- .../Test/Unit/Model/Auth/SessionTest.php | 17 ++++++++++------- .../Model/Authorization/RoleLocatorTest.php | 7 +++++-- .../Test/Unit/Model/Locale/ManagerTest.php | 13 ++++++++----- .../System/Config/Fieldset/GroupTest.php | 13 ++++++++----- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php index f1a4bc355b08e..dd8e06307cecc 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php @@ -18,38 +18,41 @@ class SessionTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Backend\App\Config | \PHPUnit_Framework_MockObject_MockObject */ - protected $config; + private $config; /** * @var \Magento\Framework\Session\Config | \PHPUnit_Framework_MockObject_MockObject */ - protected $sessionConfig; + private $sessionConfig; /** * @var \Magento\Framework\Stdlib\CookieManagerInterface | \PHPUnit_Framework_MockObject_MockObject */ - protected $cookieManager; + private $cookieManager; /** * @var \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory | \PHPUnit_Framework_MockObject_MockObject */ - protected $cookieMetadataFactory; + private $cookieMetadataFactory; /** * @var \Magento\Framework\Session\Storage | \PHPUnit_Framework_MockObject_MockObject */ - protected $storage; + private $storage; /** * @var \Magento\Framework\Acl\Builder | \PHPUnit_Framework_MockObject_MockObject */ - protected $aclBuilder; + private $aclBuilder; /** * @var Session */ - protected $session; + private $session; + /** + * @inheritdoc + */ protected function setUp() { $this->cookieMetadataFactory = $this->createPartialMock( diff --git a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php index 5b3910e9445f8..8264c0868eb90 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php @@ -10,13 +10,16 @@ class RoleLocatorTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Backend\Model\Authorization\RoleLocator */ - protected $_model; + private $_model; /** * @var \PHPUnit_Framework_MockObject_MockObject */ - protected $_sessionMock = []; + private $_sessionMock = []; + /** + * @inheritdoc + */ protected function setUp() { $this->_sessionMock = $this->createPartialMock( diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php index cfd153fed2d7e..f3d62b34c46e9 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php @@ -12,28 +12,31 @@ class ManagerTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Backend\Model\Locale\Manager */ - protected $_model; + private $_model; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\TranslateInterface */ - protected $_translator; + private $_translator; /** * @var \Magento\Backend\Model\Session */ - protected $_session; + private $_session; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Backend\Model\Auth\Session */ - protected $_authSession; + private $_authSession; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Backend\App\ConfigInterface */ - protected $_backendConfig; + private $_backendConfig; + /** + * @inheritdoc + */ protected function setUp() { $this->_session = $this->createMock(\Magento\Backend\Model\Session::class); diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php index 1b3f528cd0f56..1bea6b11b966b 100644 --- a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php @@ -11,28 +11,31 @@ class GroupTest extends \PHPUnit\Framework\TestCase /** * @var Group */ - protected $_model; + private $_model; /** * @var \Magento\Framework\Data\Form\Element\AbstractElement */ - protected $_element; + private $_element; /** * @var \Magento\Backend\Model\Auth\Session|\PHPUnit_Framework_MockObject_MockObject */ - protected $_authSession; + private $_authSession; /** * @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject */ - protected $_user; + private $_user; /** * @var \Magento\Config\Model\Config\Structure\Element\Group|\PHPUnit_Framework_MockObject_MockObject */ - protected $_group; + private $_group; + /** + * @inheritdoc + */ protected function setUp() { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); From cfd6d443a23925541f2043e7e1a46f6c684b8931 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra Date: Fri, 8 Nov 2019 23:31:59 +0200 Subject: [PATCH 1974/2437] MC-22636: Add namespaces of patches that were removed to the list of obsolete namespaces --- .../testsuite/Magento/Test/Legacy/_files/obsolete_classes.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index e4adfb58ac672..af785c28db414 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -4242,9 +4242,5 @@ ['Zend_Uri', 'Zend\Uri\Uri'], ['Zend_Mime', 'Magento\Framework\HTTP\Mime'], ['Magento\Framework\Encryption\Crypt', 'Magento\Framework\Encryption\EncryptionAdapterInterface'], - ['Magento\Bundle\Setup\Patch\Schema\ChangeTmpTablesEngine'], - ['Magento\Catalog\Setup\Patch\Schema\ChangeTmpTablesEngine'], - ['Magento\CatalogInventory\Setup\Patch\Schema\ChangeTmpTablesEngine'], - ['Magento\Downloadable\Setup\Patch\Schema\ChangeTmpTablesEngine'], ['Magento\Wishlist\Setup\Patch\Schema\AddProductIdConstraint'], ]; From f19c780fb7e31db89bfd9965e177d52ad03dacd7 Mon Sep 17 00:00:00 2001 From: Buba Suma Date: Fri, 8 Nov 2019 10:30:50 -0600 Subject: [PATCH 1975/2437] MC-22567: 404 page while switching the store from Product detail page - Move HashGenerator to the end of storeswitcher handlers pool to prevent changing the target URL --- app/code/Magento/Store/etc/di.xml | 2 +- .../Model/StoreSwitcher/RewriteUrlTest.php | 55 +++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 62f6f41424025..3fa9c8314fdd1 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -436,7 +436,7 @@ Magento\Store\Model\StoreSwitcher\CleanTargetUrl Magento\Store\Model\StoreSwitcher\ManageStoreCookie Magento\Store\Model\StoreSwitcher\ManagePrivateContent - Magento\Store\Model\StoreSwitcher\HashGenerator + Magento\Store\Model\StoreSwitcher\HashGenerator diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php index d7da1389ac847..317d26abd3370 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php @@ -8,21 +8,28 @@ namespace Magento\UrlRewrite\Model\StoreSwitcher; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\App\ActionInterface; use Magento\Framework\App\Config\ReinitableConfigInterface; use Magento\Framework\App\Config\Value; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface as ObjectManager; +use Magento\Framework\Url\DecoderInterface; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\StoreSwitcher; -use Magento\Framework\ObjectManagerInterface as ObjectManager; use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * Test store switching */ -class RewriteUrlTest extends \PHPUnit\Framework\TestCase +class RewriteUrlTest extends TestCase { /** * @var StoreSwitcher @@ -66,7 +73,7 @@ protected function setUp() * @magentoAppIsolation enabled * @return void * @throws StoreSwitcher\CannotSwitchStoreException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ public function testSwitchToNonExistingPage(): void { @@ -91,7 +98,7 @@ public function testSwitchToNonExistingPage(): void * @magentoDbIsolation disabled * @return void * @throws StoreSwitcher\CannotSwitchStoreException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ public function testSwitchToExistingPage(): void { @@ -120,6 +127,46 @@ public function testSwitchCmsPageToAnotherStore(): void $this->assertEquals($expectedUrl, $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl)); } + /** + * Test store switching with logged in customer on cms page with different url_key + * + * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrite.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @return void + */ + public function testSwitchCmsPageToAnotherStoreAsCustomer(): void + { + /** @var CustomerRepositoryInterface $repository */ + $repository = $this->objectManager->create(CustomerRepositoryInterface::class); + $this->loginAsCustomer($repository->get('customer@example.com')); + $fromStore = $this->getStoreByCode('default'); + $toStore = $this->getStoreByCode('fixture_second_store'); + + $redirectUrl = "http://localhost/index.php/page-c/"; + $expectedUrl = "http://localhost/index.php/page-c-on-2nd-store"; + /** @var DecoderInterface $decoder */ + $decoder = $this->objectManager->create(DecoderInterface::class); + $secureRedirectUrl = $this->storeSwitcher->switch($fromStore, $toStore, $redirectUrl); + parse_str(parse_url($secureRedirectUrl, PHP_URL_QUERY), $secureRedirectUrlQueryParams); + $encodedActualUrl = $secureRedirectUrlQueryParams[ActionInterface::PARAM_NAME_URL_ENCODED]; + $actualUrl = $decoder->decode($encodedActualUrl); + $this->assertEquals($expectedUrl, $actualUrl); + } + + /** + * Login as customer + * + * @param CustomerInterface $customer + */ + private function loginAsCustomer($customer) + { + /** @var Session $session */ + $session = $this->objectManager->get(Session::class); + $session->setCustomerDataAsLoggedIn($customer); + } + /** * Set base url to store. * From d93c113262d78b8e7fb699206316cc173451f2ac Mon Sep 17 00:00:00 2001 From: Dave Macaulay Date: Fri, 8 Nov 2019 16:19:38 -0600 Subject: [PATCH 1976/2437] PB-225: Page builder JS and CSS files are not loading and going to 404 --- lib/web/mage/requirejs/static.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/requirejs/static.js b/lib/web/mage/requirejs/static.js index 6b3b4717c3215..ec66b5f1063e0 100644 --- a/lib/web/mage/requirejs/static.js +++ b/lib/web/mage/requirejs/static.js @@ -385,4 +385,4 @@ define('text', [ }; return text; -}); \ No newline at end of file +}); From d69c4429c05427fc8de1873e38f0461e152dd0bd Mon Sep 17 00:00:00 2001 From: Roman Hanin Date: Fri, 8 Nov 2019 16:57:33 -0600 Subject: [PATCH 1977/2437] MC-22958: [Integration] Failing Tests: Magento.CatalogSearch --- .../Model/ResourceModel/Advanced/CollectionTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php index 87fda534be6d9..3eea0aa117452 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php @@ -5,6 +5,10 @@ */ namespace Magento\CatalogSearch\Model\ResourceModel\Advanced; +use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\TestFramework\Helper\Bootstrap; + /** * Test class for \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection. * @magentoDbIsolation disabled @@ -21,6 +25,9 @@ protected function setUp() $advanced = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\CatalogSearch\Model\Search\ItemCollectionProvider::class); $this->advancedCollection = $advanced->getCollection(); + $indexerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(IndexerRegistry::class); + $indexerRegistry->get(Fulltext::INDEXER_ID)->reindexAll(); } /** From 45d1b6bb83a84a9b42c2c70cb3b41ffaba042fc7 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov Date: Fri, 8 Nov 2019 16:58:57 -0600 Subject: [PATCH 1978/2437] Reorder memory leak test --- .../framework/Magento/TestFramework/Helper/Memory.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php index 0924bf8b7db0b..819d6630e55a8 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php @@ -47,12 +47,12 @@ public function getRealMemoryUsage() { $pid = getmypid(); try { + // fall back to the Unix command line + $result = $this->_getUnixProcessMemoryUsage($pid); + } catch (\Magento\Framework\Exception\LocalizedException $e) { // try to use the Windows command line // some ports of Unix commands on Windows, such as MinGW, have limited capabilities and cannot be used $result = $this->_getWinProcessMemoryUsage($pid); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - // fall back to the Unix command line - $result = $this->_getUnixProcessMemoryUsage($pid); } return $result; } From 0a6bbff27f287e4342be5b24c3d4684cd89d0fa6 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 8 Nov 2019 17:03:54 -0600 Subject: [PATCH 1979/2437] MAGETWO-99311: Asynchronous image resizing --- .../Console/Command/ImagesResizeCommand.php | 141 ++++++++++++++++-- .../Model/ConsumerImageResize.php | 91 +++++++++++ .../Service/ImageResizeScheduler.php | 97 ++++++++++++ app/code/Magento/MediaStorage/composer.json | 4 +- .../MediaStorage/etc/communication.xml | 12 ++ app/code/Magento/MediaStorage/etc/di.xml | 5 + app/code/Magento/MediaStorage/etc/queue.xml | 12 ++ .../MediaStorage/etc/queue_consumer.xml | 10 ++ .../MediaStorage/etc/queue_publisher.xml | 12 ++ .../MediaStorage/etc/queue_topology.xml | 12 ++ 10 files changed, 379 insertions(+), 17 deletions(-) create mode 100644 app/code/Magento/MediaStorage/Model/ConsumerImageResize.php create mode 100644 app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php create mode 100644 app/code/Magento/MediaStorage/etc/communication.xml create mode 100644 app/code/Magento/MediaStorage/etc/queue.xml create mode 100644 app/code/Magento/MediaStorage/etc/queue_consumer.xml create mode 100644 app/code/Magento/MediaStorage/etc/queue_publisher.xml create mode 100644 app/code/Magento/MediaStorage/etc/queue_topology.xml diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index ba12d60cb0bc8..21692b1ae004c 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -8,26 +8,39 @@ namespace Magento\MediaStorage\Console\Command; use Magento\Framework\App\Area; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\State; -use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Console\Cli; use Magento\MediaStorage\Service\ImageResize; +use Magento\MediaStorage\Service\ImageResizeScheduler; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBarFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Command\Command; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; /** * Resizes product images according to theme view definitions. * * @package Magento\MediaStorage\Console\Command */ -class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command +class ImagesResizeCommand extends Command { + /** + * Asynchronous image resize mode + */ + const ASYNC_RESIZE = 'async'; + + /** + * @var ImageResizeScheduler + */ + private $imageResizeScheduler; + /** * @var ImageResize */ - private $resize; + private $imageResize; /** * @var State @@ -39,24 +52,32 @@ class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command */ private $progressBarFactory; + /** + * @var ProductImage + */ + private $productImage; + /** * @param State $appState - * @param ImageResize $resize - * @param ObjectManagerInterface $objectManager + * @param ImageResize $imageResize + * @param ImageResizeScheduler $imageResizeScheduler * @param ProgressBarFactory $progressBarFactory + * @param ProductImage $productImage * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( State $appState, - ImageResize $resize, - ObjectManagerInterface $objectManager, - ProgressBarFactory $progressBarFactory = null + ImageResize $imageResize, + ImageResizeScheduler $imageResizeScheduler, + ProgressBarFactory $progressBarFactory, + ProductImage $productImage ) { parent::__construct(); - $this->resize = $resize; $this->appState = $appState; - $this->progressBarFactory = $progressBarFactory - ?: ObjectManager::getInstance()->get(ProgressBarFactory::class); + $this->imageResize = $imageResize; + $this->imageResizeScheduler = $imageResizeScheduler; + $this->progressBarFactory = $progressBarFactory; + $this->productImage = $productImage; } /** @@ -65,7 +86,25 @@ public function __construct( protected function configure() { $this->setName('catalog:images:resize') - ->setDescription('Creates resized product images'); + ->setDescription('Creates resized product images') + ->setDefinition($this->getOptionsList()); + } + + /** + * Image resize command options list + * + * @return array + */ + private function getOptionsList() : array + { + return [ + new InputOption( + self::ASYNC_RESIZE, + '-a', + InputOption::VALUE_NONE, + 'Resize image in asynchronous mode' + ), + ]; } /** @@ -74,11 +113,25 @@ protected function configure() * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) + { + $result = $input->getOption(self::ASYNC_RESIZE) ? + $this->executeAsync($output) : $this->executeSync($output); + + return $result; + } + + /** + * Run resize in synchronous mode + * + * @param OutputInterface $output + * @return int + */ + private function executeSync(OutputInterface $output): int { try { $errors = []; $this->appState->setAreaCode(Area::AREA_GLOBAL); - $generator = $this->resize->resizeFromThemes(); + $generator = $this->imageResize->resizeFromThemes(); /** @var ProgressBar $progress */ $progress = $this->progressBarFactory->create( @@ -111,7 +164,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } catch (\Exception $e) { $output->writeln("{$e->getMessage()}"); // we must have an exit code higher than zero to indicate something was wrong - return \Magento\Framework\Console\Cli::RETURN_FAILURE; + return Cli::RETURN_FAILURE; } $output->write(PHP_EOL); @@ -124,6 +177,62 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln("Product images resized successfully"); } - return \Magento\Framework\Console\Cli::RETURN_SUCCESS; + return Cli::RETURN_SUCCESS; + } + + /** + * Schedule asynchronous image resizing + * + * @param OutputInterface $output + * @return int + */ + private function executeAsync(OutputInterface $output): int + { + try { + $errors = []; + $this->appState->setAreaCode(Area::AREA_GLOBAL); + + /** @var ProgressBar $progress */ + $progress = $this->progressBarFactory->create( + [ + 'output' => $output, + 'max' => $this->productImage->getCountUsedProductImages() + ] + ); + $progress->setFormat( + "%current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s% \t| %message%" + ); + + if ($output->getVerbosity() !== OutputInterface::VERBOSITY_NORMAL) { + $progress->setOverwrite(false); + } + + $productImages = $this->productImage->getUsedProductImages(); + foreach ($productImages as $image) { + $result = $this->imageResizeScheduler->schedule($image['filepath']); + + if (!$result) { + $errors[$image['filepath']] = 'Error image scheduling: ' . $image['filepath']; + } + $progress->setMessage($image['filepath']); + $progress->advance(); + } + } catch (\Exception $e) { + $output->writeln("{$e->getMessage()}"); + // we must have an exit code higher than zero to indicate something was wrong + return Cli::RETURN_FAILURE; + } + + $output->write(PHP_EOL); + if (count($errors)) { + $output->writeln("Product images resized with errors:"); + foreach ($errors as $error) { + $output->writeln("{$error}"); + } + } else { + $output->writeln("Product images scheduled successfully"); + } + + return Cli::RETURN_SUCCESS; } } diff --git a/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php b/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php new file mode 100644 index 0000000000000..43f3e1d7767ce --- /dev/null +++ b/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php @@ -0,0 +1,91 @@ +resize = $resize; + $this->logger = $logger; + $this->serializer = $serializer; + $this->entityManager = $entityManager; + } + + /** + * Image resize + * + * @param OperationInterface $operation + * @return void + * @throws \Exception + */ + public function process(OperationInterface $operation): void + { + try { + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); + $this->resize->resizeFromImageName($data['filename']); + } catch (NotFoundException $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during image resize. Please see log for details.'); + } + + $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) + ->setErrorCode($errorCode ?? null) + ->setResultMessage($message ?? null); + + $this->entityManager->save($operation); + } +} diff --git a/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php b/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php new file mode 100644 index 0000000000000..900bb026dc5b3 --- /dev/null +++ b/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php @@ -0,0 +1,97 @@ +bulkManagement = $bulkManagement; + $this->operationFactory = $operartionFactory; + $this->identityService = $identityService; + $this->serializer = $serializer; + $this->userContext = $userContext; + } + + /** + * Schedule image resize based on original image. + * + * @param string $imageName + * @return boolean + */ + public function schedule(string $imageName): bool + { + $bulkUuid = $this->identityService->generateId(); + $bulkDescription = __('Image resize: %1', $imageName); + $dataToEncode = ['filename' => $imageName]; + + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => 'media.storage.catalog.image.resize', + 'serialized_data' => $this->serializer->serialize($dataToEncode), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + $operation = $this->operationFactory->create($data); + + return $this->bulkManagement->scheduleBulk( + $bulkUuid, + [$operation], + $bulkDescription, + $this->userContext->getUserId() + ); + } +} diff --git a/app/code/Magento/MediaStorage/composer.json b/app/code/Magento/MediaStorage/composer.json index 95c48f3fdc581..ce97eec97f7c3 100644 --- a/app/code/Magento/MediaStorage/composer.json +++ b/app/code/Magento/MediaStorage/composer.json @@ -11,7 +11,9 @@ "magento/module-config": "*", "magento/module-store": "*", "magento/module-catalog": "*", - "magento/module-theme": "*" + "magento/module-theme": "*", + "magento/module-asynchronous-operations": "*", + "magento/module-authorization": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaStorage/etc/communication.xml b/app/code/Magento/MediaStorage/etc/communication.xml new file mode 100644 index 0000000000000..c9630b24ba336 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/communication.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/MediaStorage/etc/di.xml b/app/code/Magento/MediaStorage/etc/di.xml index 2b9317787463d..5cdcbb3b2b9a9 100644 --- a/app/code/Magento/MediaStorage/etc/di.xml +++ b/app/code/Magento/MediaStorage/etc/di.xml @@ -26,4 +26,9 @@ + + + Magento\MediaStorage\Service\ImageResizeScheduler\Proxy + + diff --git a/app/code/Magento/MediaStorage/etc/queue.xml b/app/code/Magento/MediaStorage/etc/queue.xml new file mode 100644 index 0000000000000..3e4ffbf8ec9e2 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/MediaStorage/etc/queue_consumer.xml b/app/code/Magento/MediaStorage/etc/queue_consumer.xml new file mode 100644 index 0000000000000..e4c06a47f314e --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_consumer.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/code/Magento/MediaStorage/etc/queue_publisher.xml b/app/code/Magento/MediaStorage/etc/queue_publisher.xml new file mode 100644 index 0000000000000..dab99e2c307f8 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_publisher.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/MediaStorage/etc/queue_topology.xml b/app/code/Magento/MediaStorage/etc/queue_topology.xml new file mode 100644 index 0000000000000..9bb1ca5cef90e --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_topology.xml @@ -0,0 +1,12 @@ + + + + + + + From acd78506485af73b0bb1c106c9a9000cc1536471 Mon Sep 17 00:00:00 2001 From: Arnob Saha Date: Wed, 6 Nov 2019 13:03:06 -0600 Subject: [PATCH 1980/2437] MC-22632: Bundle price difference in product listing / detail page and mini cart (post add to cart) - static --- .../CatalogRule/Pricing/Price/CatalogRulePrice.php | 3 ++- .../Unit/Pricing/Price/CatalogRulePriceTest.php | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php index 7cbbc547571ab..ec63d70d55abf 100644 --- a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php @@ -85,7 +85,8 @@ public function getValue() { if (null === $this->value) { if ($this->product->hasData(self::PRICE_CODE)) { - $this->value = (float)$this->product->getData(self::PRICE_CODE) ?: false; + $value = $this->product->getData(self::PRICE_CODE); + $this->value = $value ? (float)$value : false; } else { $this->value = $this->ruleResource->getRulePrice( $this->dateTime->scopeDate($this->storeManager->getStore()->getId()), diff --git a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php index 7514d2bc4b5c5..54cd9e411df5c 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php @@ -176,4 +176,17 @@ public function testGetAmountNoBaseAmount() $result = $this->object->getValue(); $this->assertFalse($result); } + + public function testGetValueWithNullAmount() + { + $catalogRulePrice = null; + $convertedPrice = 0.0; + + $this->saleableItemMock->expects($this->once())->method('hasData') + ->with('catalog_rule_price')->willReturn(true); + $this->saleableItemMock->expects($this->once())->method('getData') + ->with('catalog_rule_price')->willReturn($catalogRulePrice); + + $this->assertEquals($convertedPrice, $this->object->getValue()); + } } From 3a7ae917e0bb789c1320389aac398d18f04850e1 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Sat, 9 Nov 2019 12:46:05 +0200 Subject: [PATCH 1981/2437] magento/graphql-ce# Remove redundant logic in createCustomer mutation --- .../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php index c690e11bd4940..5136b92f170c5 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php @@ -63,10 +63,6 @@ public function resolve( array $value = null, array $args = null ) { - if (empty($args['input']) || !is_array($args['input'])) { - throw new GraphQlInputException(__('"input" value should be specified')); - } - if (!$this->newsLetterConfig->isActive(ScopeInterface::SCOPE_STORE)) { $args['input']['is_subscribed'] = false; } From b4ec7287182fdea8ef89c90e3cf991f7888fff43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= Date: Sat, 9 Nov 2019 12:32:23 +0100 Subject: [PATCH 1982/2437] M2C-22619 Add JSDoc and description --- lib/web/mage/tabs.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index d55ef6e246d4d..f404ad9683d6f 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -135,6 +135,8 @@ define([ }, /** + * Checks if element is not in nested container to keep the correct scope of collapsible + * @param {Number} index * @param {HTMLElement} elem * @private * @return {Boolean} From 19432787ab80d80b0dc23b10e26bbc64b67b9da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= Date: Sat, 9 Nov 2019 20:52:51 +0100 Subject: [PATCH 1983/2437] M2C-22619 Change elem to element --- lib/web/mage/tabs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/tabs.js b/lib/web/mage/tabs.js index f404ad9683d6f..b007d6113eca6 100644 --- a/lib/web/mage/tabs.js +++ b/lib/web/mage/tabs.js @@ -137,7 +137,7 @@ define([ /** * Checks if element is not in nested container to keep the correct scope of collapsible * @param {Number} index - * @param {HTMLElement} elem + * @param {HTMLElement} element * @private * @return {Boolean} */ From e6e6769531c7ff689f56376697fa7e7af5117c4e Mon Sep 17 00:00:00 2001 From: Evgenii Kalashnikov Date: Sun, 10 Nov 2019 12:42:03 +0300 Subject: [PATCH 1984/2437] upload media files fix --- .../CatalogImportExport/Model/Import/Product.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 9ff2edaf2708f..0b13b93ee79f8 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1198,7 +1198,7 @@ protected function _initTypeModels() // phpcs:disable Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $this->_fieldsMap = array_merge($this->_fieldsMap, $model->getCustomFieldsMapping()); $this->_specialAttributes = array_merge($this->_specialAttributes, $model->getParticularAttributes()); - // phpcs:enable + // phpcs:enable } $this->_initErrorTemplates(); // remove doubles @@ -2035,9 +2035,9 @@ protected function _saveProductTierPrices(array $tierPriceData) protected function _getUploader() { if ($this->_fileUploader === null) { - $this->_fileUploader = $this->_uploaderFactory->create(); + $fileUploader = $this->_uploaderFactory->create(); - $this->_fileUploader->init(); + $fileUploader->init(); $dirConfig = DirectoryList::getDefaultConfig(); $dirAddon = $dirConfig[DirectoryList::MEDIA][DirectoryList::PATH]; @@ -2048,7 +2048,7 @@ protected function _getUploader() $tmpPath = $dirAddon . '/' . $this->_mediaDirectory->getRelativePath('import'); } - if (!$this->_fileUploader->setTmpDir($tmpPath)) { + if (!$fileUploader->setTmpDir($tmpPath)) { throw new LocalizedException( __('File directory \'%1\' is not readable.', $tmpPath) ); @@ -2057,11 +2057,13 @@ protected function _getUploader() $destinationPath = $dirAddon . '/' . $this->_mediaDirectory->getRelativePath($destinationDir); $this->_mediaDirectory->create($destinationPath); - if (!$this->_fileUploader->setDestDir($destinationPath)) { + if (!$fileUploader->setDestDir($destinationPath)) { throw new LocalizedException( __('File directory \'%1\' is not writable.', $destinationPath) ); } + + $this->_fileUploader = $fileUploader; } return $this->_fileUploader; } From 5c3424f781c7767cc1d6c581e847a008a59fa3cf Mon Sep 17 00:00:00 2001 From: Alexander Shkurko Date: Sun, 10 Nov 2019 12:19:54 +0200 Subject: [PATCH 1985/2437] 585: cover with unit test the GetById media asset command --- .../Model/Asset/Command/GetById.php | 5 +- .../Unit/Model/Asset/Command/GetByIdTest.php | 119 ++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php index 4519e0f926981..0ffeb206beb54 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php @@ -66,9 +66,10 @@ public function __construct( public function execute(int $mediaAssetId): AssetInterface { try { + $mediaAssetTable = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); $connection = $this->resourceConnection->getConnection(); $select = $connection->select() - ->from(['amg' => $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET)]) + ->from(['amg' => $mediaAssetTable]) ->where('amg.id = ?', $mediaAssetId); $data = $connection->query($select)->fetch(); @@ -80,7 +81,7 @@ public function execute(int $mediaAssetId): AssetInterface return $this->assetFactory->create(['data' => $data]); } catch (\Exception $exception) { $message = __( - 'En error occurred during get media asset with id %id by id: %error', + 'En error occurred during get media asset with id %id: %error', ['id' => $mediaAssetId, 'error' => $exception->getMessage()] ); $this->logger->critical($message); diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php new file mode 100644 index 0000000000000..2e25dc7c4df09 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php @@ -0,0 +1,119 @@ + 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $this->logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test successful get media asset by id command execution. + */ + public function testSuccessfulGetByIdExecution(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willReturn($this->statementMock); + + $mediaAssetStub = $this->getMockBuilder(AssetInterface::class)->getMock(); + $this->assetFactory->expects($this->once())->method('create')->willReturn($mediaAssetStub); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } + + /** + * Test case when there is no found media asset by id. + */ + public function testNotFoundMediaAssetException(): void + { + $this->statementMock->method('fetch')->willReturn([]); + $this->adapter->method('query')->willReturn($this->statementMock); + + $this->expectException(IntegrationException::class); + $this->logger->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} From d08eb4494b8bd9539d4f48cf171865dc924a2030 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko Date: Sun, 10 Nov 2019 20:16:47 +0200 Subject: [PATCH 1986/2437] Change the GetById command execution logic and cover with tests --- .../Model/Asset/Command/GetById.php | 31 ++++++++++------ .../Unit/Model/Asset/Command/GetByIdTest.php | 35 ++++++++++++++++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php index 0ffeb206beb54..cdb0f8587f8bc 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php @@ -7,12 +7,12 @@ namespace Magento\MediaGallery\Model\Asset\Command; -use Magento\MediaGalleryApi\Api\Data\AssetInterface; -use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; -use Magento\MediaGalleryApi\Model\Asset\Command\GetByIdInterface; use Magento\Framework\App\ResourceConnection; use Magento\Framework\Exception\IntegrationException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByIdInterface; use Psr\Log\LoggerInterface; /** @@ -71,20 +71,29 @@ public function execute(int $mediaAssetId): AssetInterface $select = $connection->select() ->from(['amg' => $mediaAssetTable]) ->where('amg.id = ?', $mediaAssetId); - $data = $connection->query($select)->fetch(); + $mediaAssetData = $connection->query($select)->fetch(); + } catch (\Exception $exception) { + $message = __( + 'En error occurred during get media asset data by id %id: %error', + ['id' => $mediaAssetId, 'error' => $exception->getMessage()] + ); + $this->logger->critical($message); + throw new IntegrationException($message, $exception); + } - if (empty($data)) { - $message = __('There is no such media asset with id "%1"', $mediaAssetId); - throw new NoSuchEntityException($message); - } + if (empty($mediaAssetData)) { + $message = __('There is no such media asset with id "%1"', $mediaAssetId); + throw new NoSuchEntityException($message); + } - return $this->assetFactory->create(['data' => $data]); + try { + return $this->assetFactory->create(['data' => $mediaAssetData]); } catch (\Exception $exception) { + $this->logger->critical($exception->getMessage()); $message = __( - 'En error occurred during get media asset with id %id: %error', + 'En error occurred during initialize media asset with id %id: %error', ['id' => $mediaAssetId, 'error' => $exception->getMessage()] ); - $this->logger->critical($message); throw new IntegrationException($message, $exception); } } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php index 2e25dc7c4df09..eb9d3c80039e9 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php @@ -11,6 +11,7 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaGallery\Model\Asset\Command\GetById; use Magento\MediaGalleryApi\Api\Data\AssetInterface; @@ -22,6 +23,7 @@ /** * Test the GetById command model + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class GetByIdTest extends TestCase { @@ -98,6 +100,22 @@ public function testSuccessfulGetByIdExecution(): void $mediaAssetStub = $this->getMockBuilder(AssetInterface::class)->getMock(); $this->assetFactory->expects($this->once())->method('create')->willReturn($mediaAssetStub); + $this->assertEquals( + $mediaAssetStub, + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID) + ); + } + + /** + * Test an exception during the get asset data query. + */ + public function testExceptionDuringGetMediaAssetData(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willThrowException(new \Exception()); + + $this->expectException(IntegrationException::class); + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); } @@ -109,8 +127,23 @@ public function testNotFoundMediaAssetException(): void $this->statementMock->method('fetch')->willReturn([]); $this->adapter->method('query')->willReturn($this->statementMock); + $this->expectException(NoSuchEntityException::class); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } + + /** + * Test case when a problem occurred during asset initialization from received data. + */ + public function testErrorDuringMediaAssetInitializationException(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willReturn($this->statementMock); + + $this->assetFactory->expects($this->once())->method('create')->willThrowException(new \Exception()); + $this->expectException(IntegrationException::class); - $this->logger->expects($this->once()) + $this->logger->expects($this->any()) ->method('critical') ->willReturnSelf(); From fe89ce4bd0e60cbc9fdfe8044602fa0de72ea7ad Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Mon, 11 Nov 2019 00:10:58 +0200 Subject: [PATCH 1987/2437] magento/devdocs#: createCustomer. Test coverage. Case: create new customer with the email of already existent user --- .../GraphQl/Customer/CreateCustomerTest.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index 0be968d6d340d..85e5a0dae11e2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -308,6 +308,39 @@ public function testCreateCustomerSubscribed() $this->assertEquals(false, $response['createCustomer']['customer']['is_subscribed']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage A customer with the same email address already exists in an associated website. + */ + public function testCreateCustomerIfCustomerWithProvidedEmailAlreadyExists() + { + $existedEmail = 'customer@example.com'; + $password = 'test123#'; + $firstname = 'John'; + $lastname = 'Smith'; + + $query = <<graphQlMutation($query); + } + public function tearDown() { $newEmail = 'new_customer@example.com'; From e0a0d508090386a8225ade2cc72c0bc030ee6ee2 Mon Sep 17 00:00:00 2001 From: Jasper Zeinstra Date: Mon, 11 Nov 2019 10:45:18 +0100 Subject: [PATCH 1988/2437] Fix escape less calc --- lib/web/css/source/lib/_navigation.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/css/source/lib/_navigation.less b/lib/web/css/source/lib/_navigation.less index 21cb5fe0a004b..9b13c28227eb9 100644 --- a/lib/web/css/source/lib/_navigation.less +++ b/lib/web/css/source/lib/_navigation.less @@ -338,7 +338,7 @@ top: 0; left: 100%; width: 10px; - height: calc(100% + 3px); + height: calc(~'100% + 3px'); z-index: 1; } } From 39f307283f18702aa365f0f1113b936ca301b6e8 Mon Sep 17 00:00:00 2001 From: Andrii Meysar Date: Mon, 11 Nov 2019 12:14:12 +0200 Subject: [PATCH 1989/2437] MC-22984: Fix CE composer.lock --- composer.lock | 233 +++++++++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 125 deletions(-) diff --git a/composer.lock b/composer.lock index 8bcfbb73d0a3d..acbd5f3036f90 100644 --- a/composer.lock +++ b/composer.lock @@ -73,6 +73,7 @@ "File.php" ] }, + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -83,10 +84,6 @@ ], "description": "The stock Zend_Cache_Backend_File backend has extremely poor performance for cleaning by tags making it become unusable as the number of cached items increases. This backend makes many changes resulting in a huge performance boost, especially for tag cleaning.", "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_File", - "support": { - "source": "https://github.com/colinmollenhour/Cm_Cache_Backend_File/tree/v1.4.5", - "issues": "https://github.com/colinmollenhour/Cm_Cache_Backend_File/issues" - }, "time": "2019-04-18T21:54:31+00:00" }, { @@ -112,6 +109,7 @@ "Cm/Cache/Backend/Redis.php" ] }, + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -122,10 +120,6 @@ ], "description": "Zend_Cache backend using Redis with full support for tags.", "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis", - "support": { - "source": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis/tree/1.10.6", - "issues": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis/issues" - }, "time": "2018-09-24T16:02:07+00:00" }, { @@ -263,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.9.1", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f" + "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f", - "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f", + "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5", + "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5", "shasum": "" }, "require": { @@ -339,7 +333,7 @@ "dependency", "package" ], - "time": "2019-11-01T16:20:17+00:00" + "time": "2019-08-02T18:55:33+00:00" }, { "name": "composer/semver", @@ -1003,12 +997,6 @@ "reference": "8b6c32f53b4944a5d6656e86344cd0f9784709a1", "shasum": "" }, - "archive": { - "exclude": [ - "vendor", - "/tests/FullStackTest/" - ] - }, "require": { "composer-plugin-api": "^1.0" }, @@ -1036,10 +1024,15 @@ "MagentoHackathon\\Composer\\Magento": "src/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "OSL-3.0" ], "authors": [ + { + "name": "Vinai Kopp", + "email": "vinai@netzarbeiter.com" + }, { "name": "Daniel Fahlke aka Flyingmana", "email": "flyingmana@googlemail.com" @@ -1059,10 +1052,6 @@ { "name": "David Fuhr", "email": "fuhr@flagbit.de" - }, - { - "name": "Vinai Kopp", - "email": "vinai@netzarbeiter.com" } ], "description": "Composer installer for Magento modules", @@ -1071,9 +1060,6 @@ "composer-installer", "magento" ], - "support": { - "source": "https://github.com/magento/magento-composer-installer/tree/0.1.13" - }, "time": "2017-12-29T16:45:24+00:00" }, { @@ -1717,16 +1703,16 @@ }, { "name": "psr/log", - "version": "1.1.2", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", "shasum": "" }, "require": { @@ -1760,7 +1746,7 @@ "psr", "psr-3" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2019-10-25T08:06:51+00:00" }, { "name": "ralouphie/getallheaders", @@ -2096,7 +2082,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2149,7 +2135,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2277,7 +2263,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2327,16 +2313,16 @@ }, { "name": "symfony/finder", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" + "reference": "5e575faa95548d0586f6bedaeabec259714e44d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "url": "https://api.github.com/repos/symfony/finder/zipball/5e575faa95548d0586f6bedaeabec259714e44d1", + "reference": "5e575faa95548d0586f6bedaeabec259714e44d1", "shasum": "" }, "require": { @@ -2372,7 +2358,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-10-30T12:53:54+00:00" + "time": "2019-09-16T11:29:48+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2493,16 +2479,16 @@ }, { "name": "symfony/process", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", + "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", + "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", "shasum": "" }, "require": { @@ -2538,7 +2524,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-09-26T21:17:10+00:00" }, { "name": "tedivm/jshrink", @@ -4114,16 +4100,16 @@ }, { "name": "zendframework/zend-modulemanager", - "version": "2.8.4", + "version": "2.8.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" + "reference": "aaba206a955b5f43f29e17d09d19fc342a989b24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", - "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/aaba206a955b5f43f29e17d09d19fc342a989b24", + "reference": "aaba206a955b5f43f29e17d09d19fc342a989b24", "shasum": "" }, "require": { @@ -4169,7 +4155,7 @@ "modulemanager", "zf" ], - "time": "2019-10-28T13:29:38+00:00" + "time": "2019-10-18T20:54:53+00:00" }, { "name": "zendframework/zend-mvc", @@ -4473,16 +4459,16 @@ }, { "name": "zendframework/zend-session", - "version": "2.9.1", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" + "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", - "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/0a0c7ae4d8be608e30ecff714c86164ccca19ca3", + "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3", "shasum": "" }, "require": { @@ -4536,7 +4522,7 @@ "session", "zf" ], - "time": "2019-10-28T19:40:43+00:00" + "time": "2019-09-20T12:50:51+00:00" }, { "name": "zendframework/zend-soap", @@ -4734,16 +4720,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.12.2", + "version": "2.12.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62" + "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/fd24920c2afcf2a70d11f67c3457f8f509453a62", - "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/7b870a7515f3a35afbecc39d63f34a861f40f58b", + "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b", "shasum": "" }, "require": { @@ -4803,7 +4789,7 @@ "validator", "zf" ], - "time": "2019-10-29T08:33:25+00:00" + "time": "2019-10-12T12:17:57+00:00" }, { "name": "zendframework/zend-view", @@ -5737,20 +5723,20 @@ }, { "name": "consolidation/robo", - "version": "1.4.11", + "version": "1.4.10", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" + "reference": "e5a6ca64cf1324151873672e484aceb21f365681" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", - "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/e5a6ca64cf1324151873672e484aceb21f365681", + "reference": "e5a6ca64cf1324151873672e484aceb21f365681", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.11.0", + "consolidation/annotated-command": "^2.10.2", "consolidation/config": "^1.2", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", @@ -5780,7 +5766,6 @@ "pear/archive_tar": "^1.4.4", "php-coveralls/php-coveralls": "^1", "phpunit/php-code-coverage": "~2|~4", - "sebastian/comparator": "^1.2.4", "squizlabs/php_codesniffer": "^2.8" }, "suggest": { @@ -5842,7 +5827,7 @@ } ], "description": "Modern task runner", - "time": "2019-10-29T15:50:02+00:00" + "time": "2019-07-29T15:40:50+00:00" }, { "name": "consolidation/self-update", @@ -6175,16 +6160,16 @@ }, { "name": "doctrine/cache", - "version": "v1.8.1", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d4374ae95b36062d02ef310100ed33d78738d76c" + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d4374ae95b36062d02ef310100ed33d78738d76c", - "reference": "d4374ae95b36062d02ef310100ed33d78738d76c", + "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", "shasum": "" }, "require": { @@ -6219,10 +6204,6 @@ "MIT" ], "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -6231,6 +6212,10 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -6246,7 +6231,7 @@ "cache", "caching" ], - "time": "2019-10-28T09:31:32+00:00" + "time": "2018-08-21T18:01:43+00:00" }, { "name": "doctrine/inflector", @@ -6373,30 +6358,28 @@ }, { "name": "doctrine/lexer", - "version": "1.1.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea" + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea", - "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", "shasum": "" }, "require": { - "php": "^7.2" + "php": ">=5.3.2" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpstan/phpstan": "^0.11.8", - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^4.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -6409,14 +6392,14 @@ "MIT" ], "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -6431,7 +6414,7 @@ "parser", "php" ], - "time": "2019-07-30T19:33:28+00:00" + "time": "2019-06-08T11:03:04+00:00" }, { "name": "facebook/webdriver", @@ -7296,16 +7279,16 @@ }, { "name": "mikey179/vfsstream", - "version": "v1.6.8", + "version": "v1.6.7", "source": { "type": "git", "url": "https://github.com/bovigo/vfsStream.git", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" + "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", - "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", + "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", "shasum": "" }, "require": { @@ -7338,7 +7321,7 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", - "time": "2019-10-30T15:31:00+00:00" + "time": "2019-08-01T01:38:37+00:00" }, { "name": "mustache/mustache", @@ -9252,16 +9235,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" + "reference": "78b7611c45039e8ce81698be319851529bf040b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/78b7611c45039e8ce81698be319851529bf040b1", + "reference": "78b7611c45039e8ce81698be319851529bf040b1", "shasum": "" }, "require": { @@ -9307,20 +9290,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-09-10T11:25:17+00:00" }, { "name": "symfony/config", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "f4ee0ebb91b16ca1ac105aa39f9284f3cac19a15" + "reference": "0acb26407a9e1a64a275142f0ae5e36436342720" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/f4ee0ebb91b16ca1ac105aa39f9284f3cac19a15", - "reference": "f4ee0ebb91b16ca1ac105aa39f9284f3cac19a15", + "url": "https://api.github.com/repos/symfony/config/zipball/0acb26407a9e1a64a275142f0ae5e36436342720", + "reference": "0acb26407a9e1a64a275142f0ae5e36436342720", "shasum": "" }, "require": { @@ -9371,20 +9354,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-10-30T13:18:51+00:00" + "time": "2019-09-19T15:51:53+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "fc036941dfafa037a7485714b62593c7eaf68edd" + "reference": "e1e0762a814b957a1092bff75a550db49724d05b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/fc036941dfafa037a7485714b62593c7eaf68edd", - "reference": "fc036941dfafa037a7485714b62593c7eaf68edd", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e1e0762a814b957a1092bff75a550db49724d05b", + "reference": "e1e0762a814b957a1092bff75a550db49724d05b", "shasum": "" }, "require": { @@ -9444,20 +9427,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-10-02T12:58:58+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" + "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/e9f7b4d19d69b133bd638eeddcdc757723b4211f", + "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f", "shasum": "" }, "require": { @@ -9505,7 +9488,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-09-28T21:25:05+00:00" }, { "name": "symfony/http-foundation", @@ -9564,16 +9547,16 @@ }, { "name": "symfony/options-resolver", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" + "reference": "81c2e120522a42f623233968244baebd6b36cb6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/81c2e120522a42f623233968244baebd6b36cb6a", + "reference": "81c2e120522a42f623233968244baebd6b36cb6a", "shasum": "" }, "require": { @@ -9614,7 +9597,7 @@ "configuration", "options" ], - "time": "2019-10-28T20:59:01+00:00" + "time": "2019-08-08T09:29:19+00:00" }, { "name": "symfony/polyfill-php54", @@ -9904,7 +9887,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -9954,16 +9937,16 @@ }, { "name": "symfony/yaml", - "version": "v4.3.6", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "324cf4b19c345465fad14f3602050519e09e361d" + "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", - "reference": "324cf4b19c345465fad14f3602050519e09e361d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/41e16350a2a1c7383c4735aa2f9fce74cf3d1178", + "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178", "shasum": "" }, "require": { @@ -10009,7 +9992,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-10-30T12:58:49+00:00" + "time": "2019-09-11T15:41:19+00:00" }, { "name": "theseer/fdomdocument", From f22905662aeda6a0f2c073d650326dd6c2a6fd93 Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Mon, 11 Nov 2019 12:36:10 +0200 Subject: [PATCH 1990/2437] Fix issue with WYSIWYG editors magento/adobe-stock-integration/issues/622 --- lib/web/mage/adminhtml/browser.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 137ad5541a87f..894a33d4ab3bc 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -51,13 +51,12 @@ define([ var windowId = this.windowId, content = '', self = this; - - if (this.modalLoaded === true && - options && - self.targetElementId && - self.targetElementId === options.targetElementId - ) { - if (typeof options.closed !== 'undefined') { + + if (this.modalLoaded === true + || (self.targetElementId + && self.targetElementId === options.targetElementId)) { + + if (options && typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } this.modal.modal('openModal'); @@ -89,9 +88,9 @@ define([ }).done(function (data) { self.modal.html(data).trigger('contentUpdated'); self.modalLoaded = true; - self.targetElementId = options.targetElementId; + options ? self.targetElementId = options.targetElementId : ''; }); - }, + }, /** * Close dialog. From f6d2af383f50c0484acf6bab11cebf771e673006 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Mon, 11 Nov 2019 12:47:47 +0200 Subject: [PATCH 1991/2437] MC-22985: Fix integration test \Magento\UrlRewrite\Controller\UrlRewriteTest::testCategoryUrlRewrite --- .../Category/CategoryUrlRewriteTest.php | 90 +++++++++++++++++++ .../UrlRewrite/Controller/UrlRewriteTest.php | 33 ------- 2 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php new file mode 100644 index 0000000000000..6aa0720081ce8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php @@ -0,0 +1,90 @@ +config = $this->_objectManager->get(ScopeConfigInterface::class); + $this->registry = $this->_objectManager->get(Registry::class); + $this->categoryUrlSuffix = $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @dataProvider categoryRewriteProvider + * @param int $categoryId + * @param string $urlPath + * @return void + */ + public function testCategoryUrlRewrite(int $categoryId, string $urlPath): void + { + $this->dispatch(sprintf($urlPath, $this->categoryUrlSuffix)); + $currentCategory = $this->registry->registry('current_category'); + $response = $this->getResponse(); + $this->assertEquals( + Response::STATUS_CODE_200, + $response->getHttpResponseCode(), + 'Response code does not match expected value' + ); + $this->assertNotNull($currentCategory); + $this->assertEquals($categoryId, $currentCategory->getId()); + } + + /** + * @return array + */ + public function categoryRewriteProvider(): array + { + return [ + [ + 'category_id' => 400, + 'url_path' => '/category-1%s', + ], + [ + 'category_id' => 401, + 'url_path' => '/category-1/category-1-1%s', + ], + [ + 'category_id' => 402, + 'url_path' => '/category-1/category-1-1/category-1-1-1%s', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php index e1b28f474672a..5f8adc5d65113 100644 --- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Controller/UrlRewriteTest.php @@ -82,37 +82,4 @@ public function requestDataProvider(): array ], ]; } - - /** - * @magentoDbIsolation enabled - * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 - * @magentoDataFixture Magento/Catalog/_files/category_tree.php - * @dataProvider categoryRewriteProvider - * @param string $request - * @return void - */ - public function testCategoryUrlRewrite(string $request): void - { - $this->dispatch($request); - $response = $this->getResponse(); - $this->assertEquals( - HttpResponse::STATUS_CODE_200, - $response->getHttpResponseCode(), - 'Response code does not match expected value' - ); - } - - /** - * @return array - */ - public function categoryRewriteProvider(): array - { - return [ - [ - 'category-1.html', - 'category-1/category-1-1.html', - 'category-1/category-1-1/category-1-1-1.html', - ], - ]; - } } From 34f870ea09108509cedc1d66f5910f8c232f7b24 Mon Sep 17 00:00:00 2001 From: vpashovski Date: Mon, 11 Nov 2019 12:50:09 +0200 Subject: [PATCH 1992/2437] Remove Prefix name length check; --- lib/internal/Magento/Framework/Lock/Backend/Database.php | 9 --------- .../Framework/Lock/Test/Unit/Backend/DatabaseTest.php | 6 ------ 2 files changed, 15 deletions(-) diff --git a/lib/internal/Magento/Framework/Lock/Backend/Database.php b/lib/internal/Magento/Framework/Lock/Backend/Database.php index bf59167f2b50d..aa872f9166b5c 100644 --- a/lib/internal/Magento/Framework/Lock/Backend/Database.php +++ b/lib/internal/Magento/Framework/Lock/Backend/Database.php @@ -11,7 +11,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\Config\ConfigOptionsListConstants; use Magento\Framework\Exception\AlreadyExistsException; -use Magento\Framework\Exception\InputException; use Magento\Framework\Phrase; use Magento\Framework\DB\ExpressionConverter; @@ -69,7 +68,6 @@ public function __construct( * @param string $name lock name * @param int $timeout How long to wait lock acquisition in seconds, negative value means infinite timeout * @return bool - * @throws InputException * @throws AlreadyExistsException * @throws \Zend_Db_Statement_Exception */ @@ -111,7 +109,6 @@ public function lock(string $name, int $timeout = -1): bool * * @param string $name lock name * @return bool - * @throws InputException * @throws \Zend_Db_Statement_Exception */ public function unlock(string $name): bool @@ -139,7 +136,6 @@ public function unlock(string $name): bool * * @param string $name lock name * @return bool - * @throws InputException * @throws \Zend_Db_Statement_Exception */ public function isLocked(string $name): bool @@ -163,17 +159,12 @@ public function isLocked(string $name): bool * * @param string $name * @return string - * @throws InputException */ private function addPrefix(string $name): string { $prefix = $this->getPrefix() ? $this->getPrefix() . '|' : ''; $name = ExpressionConverter::shortenEntityName($prefix . $name, $prefix); - if (strlen($name) > 64) { - throw new InputException(new Phrase('Lock name too long: %1...', [substr($name, 0, 64)])); - } - return $name; } diff --git a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/DatabaseTest.php b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/DatabaseTest.php index fd6fa44bd01ed..5da420f39a672 100644 --- a/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/DatabaseTest.php +++ b/lib/internal/Magento/Framework/Lock/Test/Unit/Backend/DatabaseTest.php @@ -86,7 +86,6 @@ protected function setUp() /** * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception */ public function testLock() @@ -104,7 +103,6 @@ public function testLock() /** * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception */ public function testlockWithTooLongName() @@ -122,7 +120,6 @@ public function testlockWithTooLongName() /** * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception * @expectedException \Magento\Framework\Exception\AlreadyExistsException */ @@ -142,7 +139,6 @@ public function testlockWithAlreadyAcquiredLockInSameSession() /** * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception */ public function testLockWithUnavailableDeploymentConfig() @@ -156,7 +152,6 @@ public function testLockWithUnavailableDeploymentConfig() } /** - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception */ public function testUnlockWithUnavailableDeploymentConfig() @@ -170,7 +165,6 @@ public function testUnlockWithUnavailableDeploymentConfig() } /** - * @throws \Magento\Framework\Exception\InputException * @throws \Zend_Db_Statement_Exception */ public function testIsLockedWithUnavailableDB() From 3f77466a229536daa0ba884517f40adcd1d8e8b4 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Mon, 11 Nov 2019 15:20:02 +0200 Subject: [PATCH 1993/2437] MC-20682: Storefront: Add/remove product from other storeviews and websites --- .../Magento/Catalog/Controller/Product/ViewTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php index d5d222ad608f0..f45c9934acfc1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php @@ -13,7 +13,6 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; use Magento\Eav\Model\Entity\Type; -use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\App\Http; use Magento\Framework\Registry; use Magento\Store\Model\StoreManagerInterface; @@ -62,9 +61,6 @@ class ViewTest extends AbstractController /** @var StoreManagerInterface */ private $storeManager; - /** @var SortOrderBuilder */ - private $sortOrderBuilder; - /** @var GetAttributeSetByName */ private $getAttributeSetByName; @@ -82,7 +78,6 @@ protected function setUp() ->loadByCode(Product::ENTITY); $this->registry = $this->_objectManager->get(Registry::class); $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); - $this->sortOrderBuilder = $this->_objectManager->create(SortOrderBuilder::class); $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); } From f7f9c5bb193248c4b7762a1378f4f6450e5fc74e Mon Sep 17 00:00:00 2001 From: Yurii Sapiha Date: Mon, 11 Nov 2019 15:22:52 +0200 Subject: [PATCH 1994/2437] MC-22985: Fix integration test \Magento\UrlRewrite\Controller\UrlRewriteTest::testCategoryUrlRewrite --- .../Catalog/Controller/Category/CategoryUrlRewriteTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php index 6aa0720081ce8..1b51c65e1e853 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php @@ -9,10 +9,10 @@ use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Response\Http; use Magento\Framework\Registry; use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\TestCase\AbstractController; -use Zend\Http\Response; /** * Checks category availability on storefront by url rewrite @@ -59,7 +59,7 @@ public function testCategoryUrlRewrite(int $categoryId, string $urlPath): void $currentCategory = $this->registry->registry('current_category'); $response = $this->getResponse(); $this->assertEquals( - Response::STATUS_CODE_200, + Http::STATUS_CODE_200, $response->getHttpResponseCode(), 'Response code does not match expected value' ); From cdbd45249c810591aebedc8a715b74df332a6f0c Mon Sep 17 00:00:00 2001 From: Tomash Khamlai Date: Mon, 11 Nov 2019 17:59:39 +0200 Subject: [PATCH 1995/2437] Refactoring: - removed function for getting `customer_token`. (dublication) - removed assertion from private function revokeCustomerToken that is nod considered as test and already covered with tests Signed-off-by: Tomash Khamlai --- .../Quote/Customer/GetCustomerCartTest.php | 44 +++---------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php index a52938c13e5c0..2e3e460b5932c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php @@ -90,10 +90,8 @@ public function testGetLoggedInCustomerCartWithoutMaskedQuoteId() */ public function testGetNewCustomerCart() { - $customerToken = $this->generateCustomerToken(); $customerCartQuery = $this->getCustomerCartQuery(); - $headers = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($customerCartQuery, [], '', $headers); + $response = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); $this->assertArrayHasKey('customerCart', $response); $this->assertArrayHasKey('id', $response['customerCart']); $this->assertNotNull($response['customerCart']['id']); @@ -118,12 +116,13 @@ public function testGetCustomerCartWithNoCustomerToken() * Query for customer cart after customer token is revoked * * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The request is allowed for logged in customer */ public function testGetCustomerCartAfterTokenRevoked() { - $customerToken = $this->generateCustomerToken(); - $headers = ['Authorization' => 'Bearer ' . $customerToken]; $customerCartQuery = $this->getCustomerCartQuery(); + $headers = $this->getHeaderMap(); $response = $this->graphQlMutation($customerCartQuery, [], '', $headers); $this->assertArrayHasKey('customerCart', $response); $this->assertArrayHasKey('id', $response['customerCart']); @@ -131,9 +130,6 @@ public function testGetCustomerCartAfterTokenRevoked() $this->assertNotEmpty($response['customerCart']['id']); $this->revokeCustomerToken(); $customerCartQuery = $this->getCustomerCartQuery(); - $this->expectExceptionMessage( - 'The request is allowed for logged in customer' - ); $this->graphQlQuery($customerCartQuery, [], '', $headers); } @@ -144,16 +140,14 @@ public function testGetCustomerCartAfterTokenRevoked() */ public function testRequestCustomerCartTwice() { - $customerToken = $this->generateCustomerToken(); - $headers = ['Authorization' => 'Bearer ' . $customerToken]; $customerCartQuery = $this->getCustomerCartQuery(); - $response = $this->graphQlMutation($customerCartQuery, [], '', $headers); + $response = $this->graphQlMutation($customerCartQuery, [], '', $this->getHeaderMap()); $this->assertArrayHasKey('customerCart', $response); $this->assertArrayHasKey('id', $response['customerCart']); $this->assertNotNull($response['customerCart']['id']); $cartId = $response['customerCart']['id']; $customerCartQuery = $this->getCustomerCartQuery(); - $response2 = $this->graphQlQuery($customerCartQuery, [], '', $headers); + $response2 = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); $this->assertEquals($cartId, $response2['customerCart']['id']); } @@ -192,31 +186,6 @@ public function testGetCustomerCartSecondStore() $this->assertEquals($maskedQuoteIdSecondStore, $responseSecondStore['customerCart']['id']); } - /** - * Query to generate customer token - * - * @return string - */ - private function generateCustomerToken(): string - { - $query = <<graphQlMutation($query); - self::assertArrayHasKey('generateCustomerToken', $response); - self::assertArrayHasKey('token', $response['generateCustomerToken']); - self::assertNotEmpty($response['generateCustomerToken']['token']); - - return $response['generateCustomerToken']['token']; - } - /** * Query to revoke customer token * @@ -233,7 +202,6 @@ private function revokeCustomerToken(): void QUERY; $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - $this->assertTrue($response['revokeCustomerToken']['result']); } /** From 94d9de20c8da6de29ad094d725ce3a8bd2080233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Corr=C3=AAa=20Gomes?= Date: Mon, 11 Nov 2019 13:51:16 -0300 Subject: [PATCH 1996/2437] Changing confirm password field class --- .../Magento/Customer/view/frontend/templates/form/edit.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml index 2718f03909be2..e2b6792439576 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml @@ -67,7 +67,7 @@ -
+
isEnabled() ? '\'input[id$="full"]\'' : 'null' ?>; - + dataForm.mage('validation', { isEnabled()) : ?> errorPlacement: function(error, element) { From 3e802409f124157f56b7bf5663daaa29fc4d6c11 Mon Sep 17 00:00:00 2001 From: Tomash Khamlai Date: Mon, 11 Nov 2019 20:01:18 +0200 Subject: [PATCH 1997/2437] Remove redundant variable assignment Signed-off-by: Tomash Khamlai --- .../Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php index 2e3e460b5932c..8100bce4ac718 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php @@ -201,7 +201,7 @@ private function revokeCustomerToken(): void } QUERY; - $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** From 3d223517139c4562c727df737e7af4b8fd27d72d Mon Sep 17 00:00:00 2001 From: Maksym Aposov Date: Mon, 11 Nov 2019 12:27:33 -0600 Subject: [PATCH 1998/2437] MC-22984: Fix CE composer.lock --- composer.lock | 317 ++++++++++++++++++++++++++------------------------ 1 file changed, 162 insertions(+), 155 deletions(-) diff --git a/composer.lock b/composer.lock index acbd5f3036f90..f5e4fe783879e 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], "content-hash": "21394914b3f105a33f583ba59aeba748", @@ -257,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5" + "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5", + "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f", + "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2019-08-02T18:55:33+00:00" + "time": "2019-11-01T16:20:17+00:00" }, { "name": "composer/semver", @@ -459,24 +459,24 @@ }, { "name": "composer/xdebug-handler", - "version": "1.3.3", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f" + "reference": "cbe23383749496fe0f373345208b79568e4bc248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", + "reference": "cbe23383749496fe0f373345208b79568e4bc248", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", + "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" }, "type": "library", "autoload": { @@ -494,12 +494,12 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2019-05-27T17:52:04+00:00" + "time": "2019-11-06T16:40:04+00:00" }, { "name": "container-interop/container-interop", @@ -1234,16 +1234,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.12.0", + "version": "v1.12.1", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "8228b286d6b8fe24825f42ce02403f72656ac826" + "reference": "063cae9b3a7323579063e7037720f5b52b56c178" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/8228b286d6b8fe24825f42ce02403f72656ac826", - "reference": "8228b286d6b8fe24825f42ce02403f72656ac826", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/063cae9b3a7323579063e7037720f5b52b56c178", + "reference": "063cae9b3a7323579063e7037720f5b52b56c178", "shasum": "" }, "require": { @@ -1312,7 +1312,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2019-10-19T15:30:42+00:00" + "time": "2019-11-07T17:07:24+00:00" }, { "name": "pelago/emogrifier", @@ -1390,36 +1390,39 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v2.10.1", + "version": "v2.7.3", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" + "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", - "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", + "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", "shasum": "" }, "require": { "ext-bcmath": "*", - "ext-sockets": "*", - "php": ">=5.6" + "ext-mbstring": "*", + "php": ">=5.3.0" }, "replace": { "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "ext-curl": "*", - "nategood/httpful": "^0.2.20", - "phpunit/phpunit": "^5.7|^6.5|^7.0", + "phpdocumentor/phpdocumentor": "^2.9", + "phpunit/phpunit": "^4.8", + "scrutinizer/ocular": "^1.1", "squizlabs/php_codesniffer": "^2.5" }, + "suggest": { + "ext-sockets": "Use AMQPSocketConnection" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev" + "dev-master": "2.7-dev" } }, "autoload": { @@ -1445,11 +1448,6 @@ "name": "Raúl Araya", "email": "nubeiro@gmail.com", "role": "Maintainer" - }, - { - "name": "Luke Bakken", - "email": "luke@bakken.io", - "role": "Maintainer" } ], "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", @@ -1459,7 +1457,7 @@ "queue", "rabbitmq" ], - "time": "2019-10-10T13:23:40+00:00" + "time": "2018-04-30T03:54:54+00:00" }, { "name": "phpseclib/mcrypt_compat", @@ -1703,16 +1701,16 @@ }, { "name": "psr/log", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2" + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", - "reference": "bf73deb2b3b896a9d9c75f3f0d88185d2faa27e2", + "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { @@ -1746,7 +1744,7 @@ "psr", "psr-3" ], - "time": "2019-10-25T08:06:51+00:00" + "time": "2019-11-01T11:05:21+00:00" }, { "name": "ralouphie/getallheaders", @@ -2082,7 +2080,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2135,16 +2133,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807" + "reference": "0df002fd4f500392eabd243c2947061a50937287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6229f58993e5a157f6096fc7145c0717d0be8807", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", + "reference": "0df002fd4f500392eabd243c2947061a50937287", "shasum": "" }, "require": { @@ -2201,7 +2199,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-10-01T16:40:32+00:00" + "time": "2019-11-03T09:04:05+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2263,7 +2261,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2313,16 +2311,16 @@ }, { "name": "symfony/finder", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5e575faa95548d0586f6bedaeabec259714e44d1" + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5e575faa95548d0586f6bedaeabec259714e44d1", - "reference": "5e575faa95548d0586f6bedaeabec259714e44d1", + "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", "shasum": "" }, "require": { @@ -2358,7 +2356,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-09-16T11:29:48+00:00" + "time": "2019-10-30T12:53:54+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2479,16 +2477,16 @@ }, { "name": "symfony/process", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", - "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", + "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", "shasum": "" }, "require": { @@ -2524,7 +2522,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-09-26T21:17:10+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "tedivm/jshrink", @@ -4100,16 +4098,16 @@ }, { "name": "zendframework/zend-modulemanager", - "version": "2.8.3", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "aaba206a955b5f43f29e17d09d19fc342a989b24" + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/aaba206a955b5f43f29e17d09d19fc342a989b24", - "reference": "aaba206a955b5f43f29e17d09d19fc342a989b24", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", "shasum": "" }, "require": { @@ -4155,7 +4153,7 @@ "modulemanager", "zf" ], - "time": "2019-10-18T20:54:53+00:00" + "time": "2019-10-28T13:29:38+00:00" }, { "name": "zendframework/zend-mvc", @@ -4459,16 +4457,16 @@ }, { "name": "zendframework/zend-session", - "version": "2.9.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3" + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/0a0c7ae4d8be608e30ecff714c86164ccca19ca3", - "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", "shasum": "" }, "require": { @@ -4522,7 +4520,7 @@ "session", "zf" ], - "time": "2019-09-20T12:50:51+00:00" + "time": "2019-10-28T19:40:43+00:00" }, { "name": "zendframework/zend-soap", @@ -4720,16 +4718,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.12.1", + "version": "2.12.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b" + "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/7b870a7515f3a35afbecc39d63f34a861f40f58b", - "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/fd24920c2afcf2a70d11f67c3457f8f509453a62", + "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62", "shasum": "" }, "require": { @@ -4789,7 +4787,7 @@ "validator", "zf" ], - "time": "2019-10-12T12:17:57+00:00" + "time": "2019-10-29T08:33:25+00:00" }, { "name": "zendframework/zend-view", @@ -5723,20 +5721,20 @@ }, { "name": "consolidation/robo", - "version": "1.4.10", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "e5a6ca64cf1324151873672e484aceb21f365681" + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/e5a6ca64cf1324151873672e484aceb21f365681", - "reference": "e5a6ca64cf1324151873672e484aceb21f365681", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.10.2", + "consolidation/annotated-command": "^2.11.0", "consolidation/config": "^1.2", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", @@ -5766,6 +5764,7 @@ "pear/archive_tar": "^1.4.4", "php-coveralls/php-coveralls": "^1", "phpunit/php-code-coverage": "~2|~4", + "sebastian/comparator": "^1.2.4", "squizlabs/php_codesniffer": "^2.8" }, "suggest": { @@ -5827,7 +5826,7 @@ } ], "description": "Modern task runner", - "time": "2019-07-29T15:40:50+00:00" + "time": "2019-10-29T15:50:02+00:00" }, { "name": "consolidation/self-update", @@ -6160,16 +6159,16 @@ }, { "name": "doctrine/cache", - "version": "v1.8.0", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "url": "https://api.github.com/repos/doctrine/cache/zipball/c15dcd24b756f9e52ea7c3ae8227354f3628f11a", + "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a", "shasum": "" }, "require": { @@ -6180,7 +6179,7 @@ }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^4.0", + "doctrine/coding-standard": "^6.0", "mongodb/mongodb": "^1.1", "phpunit/phpunit": "^7.0", "predis/predis": "~1.0" @@ -6191,7 +6190,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -6204,6 +6203,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -6212,10 +6215,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -6225,13 +6224,21 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "https://www.doctrine-project.org", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ + "abstraction", + "apcu", "cache", - "caching" + "caching", + "couchdb", + "memcached", + "php", + "redis", + "riak", + "xcache" ], - "time": "2018-08-21T18:01:43+00:00" + "time": "2019-11-11T10:31:52+00:00" }, { "name": "doctrine/inflector", @@ -6302,16 +6309,16 @@ }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { @@ -6354,7 +6361,7 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "doctrine/lexer", @@ -7279,16 +7286,16 @@ }, { "name": "mikey179/vfsstream", - "version": "v1.6.7", + "version": "v1.6.8", "source": { "type": "git", "url": "https://github.com/bovigo/vfsStream.git", - "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb" + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", - "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", "shasum": "" }, "require": { @@ -7321,7 +7328,7 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", - "time": "2019-08-01T01:38:37+00:00" + "time": "2019-10-30T15:31:00+00:00" }, { "name": "mustache/mustache", @@ -7660,16 +7667,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.2", + "version": "9.3.3", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "bfca2be3992f40e92206e5a7ebe5eaee37280b58" + "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/bfca2be3992f40e92206e5a7ebe5eaee37280b58", - "reference": "bfca2be3992f40e92206e5a7ebe5eaee37280b58", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1af08ca3861048a8bfb39d0405d0ac3e50ba2696", + "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696", "shasum": "" }, "require": { @@ -7714,7 +7721,7 @@ "phpcs", "standards" ], - "time": "2019-10-16T21:24:24+00:00" + "time": "2019-11-11T03:25:23+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -7936,28 +7943,28 @@ }, { "name": "phpoption/phpoption", - "version": "1.5.0", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793", + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "4.7.*" + "phpunit/phpunit": "^4.7|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -7967,7 +7974,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "Apache-2.0" ], "authors": [ { @@ -7982,7 +7989,7 @@ "php", "type" ], - "time": "2015-07-25T16:39:46+00:00" + "time": "2019-11-06T22:27:00+00:00" }, { "name": "phpspec/prophecy", @@ -9235,16 +9242,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "78b7611c45039e8ce81698be319851529bf040b1" + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/78b7611c45039e8ce81698be319851529bf040b1", - "reference": "78b7611c45039e8ce81698be319851529bf040b1", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", "shasum": "" }, "require": { @@ -9290,20 +9297,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-09-10T11:25:17+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/config", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "0acb26407a9e1a64a275142f0ae5e36436342720" + "reference": "8267214841c44d315a55242ea867684eb43c42ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/0acb26407a9e1a64a275142f0ae5e36436342720", - "reference": "0acb26407a9e1a64a275142f0ae5e36436342720", + "url": "https://api.github.com/repos/symfony/config/zipball/8267214841c44d315a55242ea867684eb43c42ce", + "reference": "8267214841c44d315a55242ea867684eb43c42ce", "shasum": "" }, "require": { @@ -9354,20 +9361,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-09-19T15:51:53+00:00" + "time": "2019-11-08T08:31:27+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "e1e0762a814b957a1092bff75a550db49724d05b" + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e1e0762a814b957a1092bff75a550db49724d05b", - "reference": "e1e0762a814b957a1092bff75a550db49724d05b", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/80c6d9e19467dfbba14f830ed478eb592ce51b64", + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64", "shasum": "" }, "require": { @@ -9427,20 +9434,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-10-02T12:58:58+00:00" + "time": "2019-11-08T16:22:27+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f" + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/e9f7b4d19d69b133bd638eeddcdc757723b4211f", - "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", "shasum": "" }, "require": { @@ -9488,7 +9495,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-09-28T21:25:05+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/http-foundation", @@ -9547,16 +9554,16 @@ }, { "name": "symfony/options-resolver", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a" + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/81c2e120522a42f623233968244baebd6b36cb6a", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", "shasum": "" }, "require": { @@ -9597,7 +9604,7 @@ "configuration", "options" ], - "time": "2019-08-08T09:29:19+00:00" + "time": "2019-10-28T20:59:01+00:00" }, { "name": "symfony/polyfill-php54", @@ -9829,16 +9836,16 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.7", + "version": "v1.1.8", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0" + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", "shasum": "" }, "require": { @@ -9883,20 +9890,20 @@ "interoperability", "standards" ], - "time": "2019-09-17T11:12:18+00:00" + "time": "2019-10-14T12:27:06+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71" + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/1e4ff456bd625be5032fac9be4294e60442e9b71", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e96c259de6abcd0cead71f0bf4d730d53ee464d0", + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0", "shasum": "" }, "require": { @@ -9933,20 +9940,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-08-07T11:52:19+00:00" + "time": "2019-11-05T14:48:09+00:00" }, { "name": "symfony/yaml", - "version": "v4.3.5", + "version": "v4.3.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178" + "reference": "324cf4b19c345465fad14f3602050519e09e361d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/41e16350a2a1c7383c4735aa2f9fce74cf3d1178", - "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178", + "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", + "reference": "324cf4b19c345465fad14f3602050519e09e361d", "shasum": "" }, "require": { @@ -9992,7 +9999,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-09-11T15:41:19+00:00" + "time": "2019-10-30T12:58:49+00:00" }, { "name": "theseer/fdomdocument", From 6aa6bd4cb863f39ea31240878fa099790a8cceee Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov Date: Mon, 11 Nov 2019 13:48:29 -0600 Subject: [PATCH 1999/2437] Update Integration Test Memory helper coding style and unit tests --- .../Magento/TestFramework/Helper/Memory.php | 12 +++++++--- .../Magento/Test/Helper/MemoryTest.php | 22 +++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php index 819d6630e55a8..ed105559ad6c4 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php @@ -10,6 +10,9 @@ */ namespace Magento\TestFramework\Helper; +/** + * Integration Test Framework memory management logic. + */ class Memory { /** @@ -38,9 +41,9 @@ public function __construct(\Magento\Framework\Shell $shell) /** * Retrieve the effective memory usage of the current process * - * memory_get_usage() cannot be used because of the bug - * @link https://bugs.php.net/bug.php?id=62467 + * Function memory_get_usage() cannot be used because of the bug * + * @link https://bugs.php.net/bug.php?id=62467 * @return int Memory usage in bytes */ public function getRealMemoryUsage() @@ -100,6 +103,7 @@ protected function _getWinProcessMemoryUsage($pid) * @return int * @throws \InvalidArgumentException * @throws \OutOfBoundsException + * phpcs:ignore Magento2.Functions.StaticFunction */ public static function convertToBytes($number) { @@ -132,9 +136,10 @@ public static function convertToBytes($number) * - but the value has only one delimiter, such as "234,56", then it is impossible to know whether it is decimal * separator or not. Only knowing the right format would allow this. * - * @param $number + * @param string $number * @return string * @throws \InvalidArgumentException + * phpcs:ignore Magento2.Functions.StaticFunction */ protected static function _convertToNumber($number) { @@ -152,6 +157,7 @@ protected static function _convertToNumber($number) * * @link http://php.net/manual/en/function.php-uname.php * @return boolean + * phpcs:ignore Magento2.Functions.StaticFunction */ public static function isMacOs() { diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php index a033aba7e90b6..04a82607668ec 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php @@ -21,16 +21,7 @@ public function testGetRealMemoryUsageUnix() { $object = new \Magento\TestFramework\Helper\Memory($this->_shell); $this->_shell->expects( - $this->at(0) - )->method( - 'execute' - )->with( - $this->stringStartsWith('tasklist.exe ') - )->will( - $this->throwException(new \Magento\Framework\Exception\LocalizedException(__('command not found'))) - ); - $this->_shell->expects( - $this->at(1) + $this->once() )->method( 'execute' )->with( @@ -44,7 +35,16 @@ public function testGetRealMemoryUsageUnix() public function testGetRealMemoryUsageWin() { $this->_shell->expects( - $this->once() + $this->at(0) + )->method( + 'execute' + )->with( + $this->stringStartsWith('ps ') + )->will( + $this->throwException(new \Magento\Framework\Exception\LocalizedException(__('command not found'))) + ); + $this->_shell->expects( + $this->at(1) )->method( 'execute' )->with( From a07627c9b0c8dcb600cc1109a65c5a08ab375a71 Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Mon, 11 Nov 2019 15:34:06 +0200 Subject: [PATCH 2000/2437] WYSIWYG Image-Popup is not working correctly with multipleEditors --- lib/web/mage/adminhtml/browser.js | 24 ++++++++++++------- .../wysiwyg/tiny_mce/tinymce4Adapter.js | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 894a33d4ab3bc..898910e477ad0 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -51,22 +51,26 @@ define([ var windowId = this.windowId, content = '', self = this; - - if (this.modalLoaded === true - || (self.targetElementId - && self.targetElementId === options.targetElementId)) { - - if (options && typeof options.closed !== 'undefined') { + + if (options + && self.targetElementId + && self.targetElementId === options.targetElementId + ) { + if (typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } this.modal.modal('openModal'); return; + } else if (typeof options === 'undefined' + && self.modalLoaded === true) { + this.modal.modal('openModal'); + return; } if (this.modal) { this.modal.html($(content).html()); - + if (options && typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } @@ -88,9 +92,11 @@ define([ }).done(function (data) { self.modal.html(data).trigger('contentUpdated'); self.modalLoaded = true; - options ? self.targetElementId = options.targetElementId : ''; + self.targetElementId = options + ? options.targetElementId + : 'null'; }); - }, + }, /** * Close dialog. diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 4dafc845309cb..df691601eccb9 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -375,7 +375,7 @@ define([ */ openFileBrowser: function (o) { var typeTitle = this.translate('Select Images'), - storeId = this.config['store_id'] !== null ? this.config['store_id'] : 0, + storeId = this.config['store_id'] ? this.config['store_id'] : 0, frameDialog = jQuery('div.mce-container[role="dialog"]'), self = this, wUrl = this.config['files_browser_window_url'] + From 87b626ed141c700dd71abcac5f5958f6982022bf Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov Date: Mon, 11 Nov 2019 14:31:30 -0600 Subject: [PATCH 2001/2437] Update Integration Test Memory helper coding style --- .../framework/Magento/TestFramework/Helper/Memory.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php index ed105559ad6c4..933d86fe6a4f5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php @@ -103,10 +103,11 @@ protected function _getWinProcessMemoryUsage($pid) * @return int * @throws \InvalidArgumentException * @throws \OutOfBoundsException - * phpcs:ignore Magento2.Functions.StaticFunction + * phpcs:disable Magento2.Functions.StaticFunction */ public static function convertToBytes($number) { + // phpcs:enable Magento2.Functions.StaticFunction if (!preg_match('/^(.*\d)\h*(\D)$/', $number, $matches)) { throw new \InvalidArgumentException("Number format '{$number}' is not recognized."); } @@ -139,10 +140,11 @@ public static function convertToBytes($number) * @param string $number * @return string * @throws \InvalidArgumentException - * phpcs:ignore Magento2.Functions.StaticFunction + * phpcs:disable Magento2.Functions.StaticFunction */ protected static function _convertToNumber($number) { + // phpcs:enable Magento2.Functions.StaticFunction preg_match_all('/(\D+)/', $number, $matches); if (count(array_unique($matches[0])) > 1) { throw new \InvalidArgumentException( @@ -157,10 +159,11 @@ protected static function _convertToNumber($number) * * @link http://php.net/manual/en/function.php-uname.php * @return boolean - * phpcs:ignore Magento2.Functions.StaticFunction + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isMacOs() { + // phpcs:enable Magento2.Functions.StaticFunction return strtoupper(PHP_OS) === 'DARWIN'; } } From 2314bd3e02f0449af9e88d440fdc620e609d29fa Mon Sep 17 00:00:00 2001 From: Buba Suma Date: Mon, 11 Nov 2019 10:46:29 -0600 Subject: [PATCH 2002/2437] MC-21807: Implement control over minimum_should_match for elasticsearch queries - Fix Magento-Health-Index-PR build failures --- .../FieldMapper/AddDefaultSearchFieldTest.php | 23 +++++-------------- .../CopySearchableFieldsToSearchFieldTest.php | 23 +++++-------------- 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php index 7da68168f8c97..1a1e7f4e0d643 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php @@ -8,27 +8,14 @@ namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; -use Magento\Framework\TestFramework\Unit\BaseTestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; /** * Test mapping preprocessor AddDefaultSearchField */ -class AddDefaultSearchFieldTest extends BaseTestCase +class AddDefaultSearchFieldTest extends TestCase { - /** - * @var AddDefaultSearchField - */ - private $model; - - /** - * @inheritDoc - */ - protected function setUp() - { - parent::setUp(); - $this->model = $this->objectManager->getObject(AddDefaultSearchField::class); - } - /** * Test default search field "_search" should be prepended and overwrite if exist. * @@ -38,7 +25,9 @@ protected function setUp() */ public function testProcess(array $mappingBefore, array $mappingAfter) { - $this->assertEquals($mappingAfter, $this->model->process($mappingBefore)); + $objectManager = new ObjectManager($this); + $model = $objectManager->getObject(AddDefaultSearchField::class); + $this->assertEquals($mappingAfter, $model->process($mappingBefore)); } /** diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php index c366e55fbbdf7..cfe8b71095d21 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php @@ -8,27 +8,14 @@ namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper; use Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; -use Magento\Framework\TestFramework\Unit\BaseTestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; /** * Test mapping preprocessor CopySearchableFieldsToSearchField */ -class CopySearchableFieldsToSearchFieldTest extends BaseTestCase +class CopySearchableFieldsToSearchFieldTest extends TestCase { - /** - * @var CopySearchableFieldsToSearchField - */ - private $model; - - /** - * @inheritDoc - */ - protected function setUp() - { - parent::setUp(); - $this->model = $this->objectManager->getObject(CopySearchableFieldsToSearchField::class); - } - /** * Test "copy_to" parameter should be added to searchable fields. * @@ -38,7 +25,9 @@ protected function setUp() */ public function testProcess(array $mappingBefore, array $mappingAfter) { - $this->assertEquals($mappingAfter, $this->model->process($mappingBefore)); + $objectManager = new ObjectManager($this); + $model = $objectManager->getObject(CopySearchableFieldsToSearchField::class); + $this->assertEquals($mappingAfter, $model->process($mappingBefore)); } /** From 0102b326155afe17eaa4e6c5c7705481778063c4 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn Date: Tue, 12 Nov 2019 10:36:34 +0200 Subject: [PATCH 2003/2437] MC-20677: Storefront: View product with related, up-sells products --- .../Product/ProductList/AbstractLinksTest.php | 323 ++++++++++++++++++ .../Block/Product/ProductList/RelatedTest.php | 158 ++++++--- .../Block/Product/ProductList/UpsellTest.php | 146 +++++--- .../Catalog/_files/products_upsell.php | 2 + .../_files/products_upsell_rollback.php | 8 + 5 files changed, 548 insertions(+), 89 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php new file mode 100644 index 0000000000000..f8e778498211d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php @@ -0,0 +1,323 @@ + [ + 'position' => 1, + ], + 'simple-249' => [ + 'position' => 2, + ], + 'simple-156' => [ + 'position' => 3, + ], + ]; + + /** @var AbstractProduct */ + protected $block; + + /** @var string */ + protected $linkType; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->productLinkInterfaceFactory = $this->objectManager->get(ProductLinkInterfaceFactory::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * Provide test data to verify the display of linked products. + * + * @return array + */ + public function displayLinkedProductsProvider(): array + { + return [ + 'product_all_displayed' => [ + 'data' => [ + 'updateProducts' => [], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-249', + 'simple-156', + ], + ], + ], + 'product_disabled' => [ + 'data' => [ + 'updateProducts' => [ + 'wrong-simple' => ['status' => Status::STATUS_DISABLED], + ], + 'expectedProductLinks' => [ + 'simple-249', + 'simple-156', + ], + ], + ], + 'product_invisibility' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-249' => ['visibility' => Visibility::VISIBILITY_NOT_VISIBLE], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-156', + ], + ], + ], + 'product_invisible_in_catalog' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-249' => ['visibility' => Visibility::VISIBILITY_IN_SEARCH], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-156', + ], + ], + ], + 'product_out_of_stock' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-156' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ], + ], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-249', + ], + ], + ], + ]; + } + + /** + * Provide test data to verify the display of linked products on different websites. + * + * @return array + */ + public function multipleWebsitesLinkedProductsProvider(): array + { + return [ + 'first_website' => [ + 'data' => [ + 'storeCode' => 'default', + 'productLinks' => [ + 'simple-2' => ['position' => 4], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-2', + ], + ], + ], + 'second_website' => [ + 'data' => [ + 'storeCode' => 'fixture_second_store', + 'productLinks' => [ + 'simple-2' => ['position' => 4], + ], + 'expectedProductLinks' => [ + 'simple-249', + 'simple-2', + ], + ], + ], + ]; + } + + /** + * Get test data to check position of related, up-sells and cross-sells products + * + * @return array + */ + protected function getPositionData(): array + { + return [ + 'productLinks' => array_replace_recursive( + $this->existingProducts, + [ + 'wrong-simple' => ['position' => 2], + 'simple-249' => ['position' => 3], + 'simple-156' => ['position' => 1], + ] + ), + 'expectedProductLinks' => [ + 'simple-156', + 'wrong-simple', + 'simple-249', + ], + ]; + } + + /** + * Prepare a block of linked products + * + * @return void + */ + protected function prepareBlock(): void + { + $this->block->setLayout($this->layout); + $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); + $this->block->setType($this->linkType); + } + + /** + * Set linked products by link type for current product received from array + * + * @param ProductInterface $product + * @param array $productData + * @return void + */ + private function setCustomProductLinks(ProductInterface $product, array $productData): void + { + $productLinks = []; + foreach ($productData as $sku => $data) { + /** @var ProductLinkInterface|Link $productLink */ + $productLink = $this->productLinkInterfaceFactory->create(); + $productLink->setSku($product->getSku()); + $productLink->setLinkedProductSku($sku); + if (isset($data['position'])) { + $productLink->setPosition($data['position']); + } + $productLink->setLinkType($this->linkType); + $productLinks[] = $productLink; + } + $product->setProductLinks($productLinks); + } + + /** + * Update product attributes + * + * @param array $products + * @return void + */ + protected function updateProducts(array $products): void + { + foreach ($products as $sku => $data) { + /** @var ProductInterface|Product $product */ + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } + } + + /** + * Get an array of received linked products + * + * @param array $items + * @return array + */ + protected function getActualLinks(array $items): array + { + $actualLinks = []; + /** @var ProductInterface $productItem */ + foreach ($items as $productItem) { + $actualLinks[] = $productItem->getSku(); + } + + return $actualLinks; + } + + /** + * Link products to an existing product + * + * @param string $sku + * @param array $productLinks + * @return void + */ + protected function linkProducts(string $sku, array $productLinks): void + { + $product = $this->productRepository->get($sku); + $this->setCustomProductLinks($product, $productLinks); + $this->productRepository->save($product); + } + + /** + * Prepare the necessary websites for all products + * + * @return array + */ + protected function prepareWebsiteIdsProducts(): array + { + $websiteId = $this->storeManager->getWebsite('test')->getId(); + $defaultWebsiteId = $this->storeManager->getWebsite('base')->getId(); + + return [ + 'simple-1' => [ + 'website_ids' => [$defaultWebsiteId, $websiteId], + ], + 'simple-2' => [ + 'website_ids' => [$defaultWebsiteId, $websiteId], + ], + 'wrong-simple' => [ + 'website_ids' => [$defaultWebsiteId], + ], + 'simple-249' => [ + 'website_ids' => [$websiteId], + ], + 'simple-156' => [ + 'website_ids' => [], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php index 9f0f04508e468..c71a481a79379 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php @@ -3,84 +3,150 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\ProductList; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection as LinkProductCollection; + /** - * Test class for \Magento\Catalog\Block\Product\List\Related. + * Check the correct behavior of related products on the product view page * - * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @see \Magento\Catalog\Block\Product\ProductList\Related * @magentoDbIsolation disabled + * @magentoAppArea frontend */ -class RelatedTest extends \PHPUnit\Framework\TestCase +class RelatedTest extends AbstractLinksTest { - /** - * @var \Magento\Catalog\Block\Product\ProductList\Related - */ + /** @var Related */ protected $block; /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - protected $product; - - /** - * @var \Magento\Catalog\Api\Data\ProductInterface + * @inheritdoc */ - protected $relatedProduct; - protected function setUp() { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $this->relatedProduct = $productRepository->get('simple'); - $this->product = $productRepository->get('simple_with_cross'); - $objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); + parent::setUp(); - $this->block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Catalog\Block\Product\ProductList\Related::class); - - $this->block->setLayout($objectManager->get(\Magento\Framework\View\LayoutInterface::class)); - $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); - $this->block->setType('related'); - $this->block->addChild('addto', \Magento\Catalog\Block\Product\ProductList\Item\Container::class); - $this->block->getChildBlock( - 'addto' - )->addChild( - 'compare', - \Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare::class, - ['template' => 'Magento_Catalog::product/list/addto/compare.phtml'] - ); + $this->block = $this->layout->createBlock(Related::class); + $this->linkType = 'related'; } /** - * @magentoAppIsolation enabled + * Checks for a related product when block code is generated + * + * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @return void */ - public function testAll() + public function testAll(): void { + /** @var ProductInterface $relatedProduct */ + $relatedProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_cross'); + $this->block->setProduct($this->product); + $this->prepareBlock(); $html = $this->block->toHtml(); $this->assertNotEmpty($html); - $this->assertContains('Simple Related Product', $html); + $this->assertContains($relatedProduct->getName(), $html); /* name */ - $this->assertContains('"product":"' . $this->relatedProduct->getId() . '"', $html); + $this->assertContains('id="related-checkbox' . $relatedProduct->getId() . '"', $html); /* part of url */ $this->assertInstanceOf( - \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::class, + LinkProductCollection::class, $this->block->getItems() ); } /** - * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @return void */ - public function testGetIdentities() + public function testGetIdentities(): void { - $expectedTags = ['cat_p_' . $this->relatedProduct->getId(), 'cat_p']; + /** @var ProductInterface $relatedProduct */ + $relatedProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_cross'); + $this->block->setProduct($this->product); + $this->prepareBlock(); + $expectedTags = ['cat_p_' . $relatedProduct->getId(), 'cat_p']; $tags = $this->block->getIdentities(); $this->assertEquals($expectedTags, $tags); } + + /** + * Test the display of related products in the block + * + * @dataProvider displayLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @param array $data + * @return void + */ + public function testDisplayRelatedProducts(array $data): void + { + $this->updateProducts($data['updateProducts']); + $this->linkProducts('simple', $this->existingProducts); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } + + /** + * Test the position of related products in the block + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @return void + */ + public function testPositionRelatedProducts(): void + { + $data = $this->getPositionData(); + $this->linkProducts('simple', $data['productLinks']); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } + + /** + * Test the display of related products in the block on different websites + * + * @dataProvider multipleWebsitesLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @magentoAppIsolation enabled + * @param array $data + * @return void + */ + public function testMultipleWebsitesRelatedProducts(array $data): void + { + $this->updateProducts($this->prepareWebsiteIdsProducts()); + $productLinks = array_replace_recursive($this->existingProducts, $data['productLinks']); + $this->linkProducts('simple-1', $productLinks); + $this->product = $this->productRepository->get( + 'simple-1', + false, + $this->storeManager->getStore($data['storeCode'])->getId() + ); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php index 57ccb48b8df69..f989393d5da63 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php @@ -3,65 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\ProductList; /** - * Test class for \Magento\Catalog\Block\Product\List\Upsell. + * Check the correct behavior of up-sell products on the product view page * - * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @see \Magento\Catalog\Block\Product\ProductList\Upsell * @magentoDbIsolation disabled + * @magentoAppArea frontend */ -class UpsellTest extends \PHPUnit\Framework\TestCase +class UpsellTest extends AbstractLinksTest { - /** - * @var \Magento\Catalog\Block\Product\ProductList\Upsell - */ + /** @var Upsell */ protected $block; /** - * @var \Magento\Catalog\Api\Data\ProductInterface + * @inheritdoc */ - protected $product; - - /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - protected $upsellProduct; - protected function setUp() { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $this->upsellProduct = $productRepository->get('simple'); - $this->product = $productRepository->get('simple_with_upsell'); - $objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); + parent::setUp(); - $this->block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Catalog\Block\Product\ProductList\Upsell::class); - - $this->block->setLayout($objectManager->get(\Magento\Framework\View\LayoutInterface::class)); - $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); - $this->block->setType('upsell'); - $this->block->addChild('addto', \Magento\Catalog\Block\Product\ProductList\Item\Container::class); - $this->block->getChildBlock( - 'addto' - )->addChild( - 'compare', - \Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare::class, - ['template' => 'Magento_Catalog::product/list/addto/compare.phtml'] - ); + $this->block = $this->layout->createBlock(Upsell::class); + $this->linkType = 'upsell'; } /** - * @magentoAppIsolation enabled + * Checks for a up-sell product when block code is generated + * + * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @return void */ - public function testAll() + public function testAll(): void { + $this->product = $this->productRepository->get('simple_with_upsell'); + $this->block->setProduct($this->product); + $this->prepareBlock(); $html = $this->block->toHtml(); $this->assertNotEmpty($html); $this->assertContains('Simple Up Sell', $html); @@ -69,12 +48,93 @@ public function testAll() } /** - * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @return void */ - public function testGetIdentities() + public function testGetIdentities(): void { - $expectedTags = ['cat_p_' . $this->upsellProduct->getId(), 'cat_p']; + $upsellProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_upsell'); + $this->block->setProduct($this->product); + $this->prepareBlock(); + $expectedTags = ['cat_p_' . $upsellProduct->getId(), 'cat_p']; $tags = $this->block->getIdentities(); $this->assertEquals($expectedTags, $tags); } + + /** + * Test the display of up-sell products in the block + * + * @dataProvider displayLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @param array $data + * @return void + */ + public function testDisplayUpsellProducts(array $data): void + { + $this->updateProducts($data['updateProducts']); + $this->linkProducts('simple', $this->existingProducts); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } + + /** + * Test the position of up-sell products in the block + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @return void + */ + public function testPositionUpsellProducts(): void + { + $data = $this->getPositionData(); + $this->linkProducts('simple', $data['productLinks']); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } + + /** + * Test the display of up-sell products in the block on different websites + * + * @dataProvider multipleWebsitesLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @magentoAppIsolation enabled + * @param array $data + * @return void + */ + public function testMultipleWebsitesUpsellProducts(array $data): void + { + $this->updateProducts($this->prepareWebsiteIdsProducts()); + $productLinks = array_replace_recursive($this->existingProducts, $data['productLinks']); + $this->linkProducts('simple-1', $productLinks); + $this->product = $this->productRepository->get( + 'simple-1', + false, + $this->storeManager->getStore($data['storeCode'])->getId() + ); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php index 8df969a31019c..489666517419f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +require __DIR__ . '/products_upsell_rollback.php'; + /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); $product->setTypeId( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php index a9c25dec58547..633eef3371a21 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\CatalogInventory\Model\StockRegistryStorage; + /** @var \Magento\Framework\Registry $registry */ $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); @@ -25,3 +28,8 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); + +/** @var StockRegistryStorage $stockRegistryStorage */ +$stockRegistryStorage = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(StockRegistryStorage::class); +$stockRegistryStorage->clean(); From f070206da28f41814f6677ecee9ef4d84105338d Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Tue, 12 Nov 2019 11:05:27 +0200 Subject: [PATCH 2004/2437] The image details are not hidden when click on same image in the search result --- .../Ui/view/base/web/js/grid/columns/image-preview.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 1ef2ebf6594fa..8a22b06d53dc5 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -100,6 +100,12 @@ define([ */ show: function (record) { var img; + + if (record._rowIndex === this.lastOpenedImage() + && this.isVisible(record)) { + this.hide(); + return; + } this.hide(); this.displayedRecord(record); From 89179be96152960fff698211c11bce0059da8e33 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr Date: Tue, 12 Nov 2019 13:49:13 +0100 Subject: [PATCH 2005/2437] Remove old logic to make search by operation status possible --- .../AsynchronousOperations/Model/BulkStatus.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php index c37ae0d23dd25..6290ac00ce87f 100644 --- a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php @@ -87,22 +87,6 @@ public function getFailedOperationsByBulkId($bulkUuid, $failureType = null) */ public function getOperationsCountByBulkIdAndStatus($bulkUuid, $status) { - if ($status === OperationInterface::STATUS_TYPE_OPEN) { - /** - * Total number of operations that has been scheduled within the given bulk - */ - $allOperationsQty = $this->getOperationCount($bulkUuid); - - /** - * Number of operations that has been processed (i.e. operations with any status but 'open') - */ - $allProcessedOperationsQty = (int)$this->operationCollectionFactory->create() - ->addFieldToFilter('bulk_uuid', $bulkUuid) - ->getSize(); - - return $allOperationsQty - $allProcessedOperationsQty; - } - /** @var \Magento\AsynchronousOperations\Model\ResourceModel\Operation\Collection $collection */ $collection = $this->operationCollectionFactory->create(); return $collection->addFieldToFilter('bulk_uuid', $bulkUuid) From eb99d8131f525d04becbbe6530a1cb48e4ba0d7f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 12 Nov 2019 08:09:07 -0600 Subject: [PATCH 2006/2437] MAGETWO-99311: Asynchronous image resizing --- .../MediaStorage/Console/Command/ImagesResizeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index 21692b1ae004c..ea8cdef8598f4 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -100,7 +100,7 @@ private function getOptionsList() : array return [ new InputOption( self::ASYNC_RESIZE, - '-a', + 'a', InputOption::VALUE_NONE, 'Resize image in asynchronous mode' ), From b3eafd4d3f1a9cbf98bf6ff0c36334cdb0f75bf5 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola Date: Thu, 7 Nov 2019 11:53:10 +0200 Subject: [PATCH 2007/2437] MC-21003: View attributes/groups of attribute set on product edit page --- .../GetEntityIdByAttributeId.php | 48 +++ .../Model/Product/Attribute/SetTest.php | 17 +- .../Product/Form/Modifier/AbstractEavTest.php | 306 ++++++++++++++++++ .../Modifier/Eav/AttributeSetGroupsTest.php | 92 +----- .../Modifier/Eav/BooleanAttributeTest.php | 86 +++++ .../Form/Modifier/Eav/DateAttributeTest.php | 81 +++++ .../Modifier/Eav/DecimalAttributeTest.php | 67 ++++ .../Modifier/Eav/DefaultAttributesTest.php | 47 +++ .../Form/Modifier/Eav/ImageAttributeTest.php | 41 +++ .../Modifier/Eav/MultiselectAttributeTest.php | 65 ++++ .../Form/Modifier/Eav/SelectAttributeTest.php | 66 ++++ .../Modifier/Eav/VarcharAttributeTest.php | 81 +++++ .../Product/Form/Modifier/EavTest.php | 248 +++++++++----- .../_files/eav_expected_meta_output.php | 6 - .../Catalog/_files/product_date_attribute.php | 51 +++ .../product_date_attribute_rollback.php | 25 ++ .../_files/product_decimal_attribute.php | 52 +++ .../product_decimal_attribute_rollback.php | 25 ++ .../_files/product_image_attribute.php | 51 +++ .../product_image_attribute_rollback.php | 25 ++ .../_files/product_varchar_attribute.php | 51 +++ .../product_varchar_attribute_rollback.php | 25 ++ .../Modifier/Eav/TextSwatchAttributeTest.php | 65 ++++ .../Eav/VisualSwatchAttributeTest.php | 71 ++++ .../_files/product_swatch_attribute.php | 31 ++ .../product_swatch_attribute_rollback.php | 8 + .../_files/product_text_swatch_attribute.php | 67 ++++ ...product_text_swatch_attribute_rollback.php | 25 ++ .../Form/Modifier/Eav/FixedAttributeTest.php | 77 +++++ 29 files changed, 1722 insertions(+), 178 deletions(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/TextSwatchAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/VisualSwatchAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Weee/Ui/DataProvider/Product/Form/Modifier/Eav/FixedAttributeTest.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php new file mode 100644 index 0000000000000..edb75a5d8d1bd --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php @@ -0,0 +1,48 @@ +attributeSetResource = $setResource; + } + + /** + * Returns entity attribute by id. + * + * @param int $setId + * @param int $attributeId + * @return int|null + */ + public function execute(int $setId, int $attributeId): ?int + { + $select = $this->attributeSetResource->getConnection()->select() + ->from($this->attributeSetResource->getTable('eav_entity_attribute')) + ->where('attribute_set_id = ?', $setId) + ->where('attribute_id = ?', $attributeId); + + $result = $this->attributeSetResource->getConnection()->fetchOne($select); + return $result ? (int)$result : null; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php index ada865ad83a4d..d5a8b694b7718 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php @@ -16,13 +16,15 @@ use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set as AttributeSetResource; use Magento\Framework\Api\AttributeInterface; use Magento\Framework\ObjectManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Eav\Model\GetAttributeGroupByName; +use Magento\TestFramework\Eav\Model\ResourceModel\GetEntityIdByAttributeId; +use Magento\TestFramework\Helper\Bootstrap; /** * Provides tests for attribute set model saving. * * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SetTest extends \PHPUnit\Framework\TestCase { @@ -66,6 +68,11 @@ class SetTest extends \PHPUnit\Framework\TestCase */ private $attributeGroupByName; + /** + * @var GetEntityIdByAttributeId + */ + private $getEntityIdByAttributeId; + /** * @inheritdoc */ @@ -80,6 +87,7 @@ protected function setUp() $this->attributeSetResource = $this->objectManager->get(AttributeSetResource::class); $this->attributeCollectionFactory = $this->objectManager->get(CollectionFactory ::class); $this->attributeGroupByName = $this->objectManager->get(GetAttributeGroupByName::class); + $this->getEntityIdByAttributeId = $this->objectManager->get(GetEntityIdByAttributeId::class); } /** @@ -263,11 +271,6 @@ private function getSetExcludedAttributes(int $setId): array */ private function getEntityAttributeId(int $setId, int $attributeId): int { - $select = $this->attributeSetResource->getConnection()->select() - ->from($this->attributeSetResource->getTable('eav_entity_attribute'), ['entity_attribute_id']) - ->where('attribute_set_id = ?', $setId) - ->where('attribute_id = ?', $attributeId); - - return (int)$this->attributeSetResource->getConnection()->fetchOne($select); + return $this->getEntityIdByAttributeId->execute($setId, $attributeId); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php new file mode 100644 index 0000000000000..3375a4e8e4094 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php @@ -0,0 +1,306 @@ + 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + 'media_image' => 'image', + 'price' => 'input', + 'weight' => 'input', + 'gallery' => 'image' + ]; + $this->objectManager = Bootstrap::getObjectManager(); + $this->locatorMock = $this->createMock(LocatorInterface::class); + $this->locatorMock->expects($this->any())->method('getStore')->willReturn( + $this->objectManager->get(StoreInterface::class) + ); + $this->metaPropertiesMapper = $this->objectManager->create(MetaProperties::class, ['mappings' => []]); + $this->compositeConfigProcessor = $this->objectManager->create( + CompositeConfigProcessor::class, + ['eavWysiwygDataProcessors' => []] + ); + $this->eavModifier = $this->objectManager->create( + Eav::class, + [ + 'locator' => $this->locatorMock, + 'formElementMapper' => $this->objectManager->create(FormElement::class, ['mappings' => $mappings]), + 'metaPropertiesMapper' => $this->metaPropertiesMapper, + 'wysiwygConfigProcessor' => $this->compositeConfigProcessor, + ] + ); + $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productFactory = $this->objectManager->get(ProductInterfaceFactory::class); + $this->defaultSetId = (int)$this->objectManager->create(Type::class) + ->loadByCode(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * @param ProductInterface $product + * @param array $expectedMeta + * @return void + */ + protected function callModifyMetaAndAssert(ProductInterface $product, array $expectedMeta): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->prepareDataForComparison($actualMeta, $expectedMeta); + $this->assertEquals($expectedMeta, $actualMeta); + } + + /** + * @param ProductInterface $product + * @param array $expectedData + * @return void + */ + protected function callModifyDataAndAssert(ProductInterface $product, array $expectedData): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); + $actualData = $this->eavModifier->modifyData([]); + $this->prepareDataForComparison($actualData, $expectedData); + $this->assertEquals($expectedData, $actualData); + } + + /** + * Prepare data for comparison to avoid false positive failures. + * + * Make sure that $data contains all the data contained in $expectedData, + * ignore all fields not declared in $expectedData + * + * @param array &$data + * @param array $expectedData + * @return void + */ + protected function prepareDataForComparison(array &$data, array $expectedData): void + { + foreach ($data as $key => &$item) { + if (!isset($expectedData[$key])) { + unset($data[$key]); + continue; + } + if ($item instanceof Phrase) { + $item = (string)$item; + } elseif (is_array($item)) { + $this->prepareDataForComparison($item, $expectedData[$key]); + } elseif ($key === 'price_id' || $key === 'sortOrder') { + $data[$key] = '__placeholder__'; + } + } + } + + /** + * Updates attribute default value. + * + * @param string $attributeCode + * @param string $defaultValue + * @return void + */ + protected function setAttributeDefaultValue(string $attributeCode, string $defaultValue): void + { + $attribute = $this->attributeRepository->get($attributeCode); + $attribute->setDefaultValue($defaultValue); + $this->attributeRepository->save($attribute); + } + + /** + * Returns attribute options list. + * + * @param string $attributeCode + * @return array + */ + protected function getAttributeOptions(string $attributeCode): array + { + $attribute = $this->attributeRepository->get($attributeCode); + + return $attribute->usesSource() ? $attribute->getSource()->getAllOptions() : []; + } + + /** + * Returns attribute option value by id. + * + * @param string $attributeCode + * @param string $label + * @return int|null + */ + protected function getOptionValueByLabel(string $attributeCode, string $label): ?int + { + $result = null; + foreach ($this->getAttributeOptions($attributeCode) as $option) { + if ($option['label'] == $label) { + $result = (int)$option['value']; + } + } + + return $result; + } + + /** + * Returns product for testing. + * + * @return ProductInterface + */ + protected function getProduct(): ProductInterface + { + return $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID); + } + + /** + * Returns new product object. + * + * @return ProductInterface + */ + protected function getNewProduct(): ProductInterface + { + $product = $this->productFactory->create(); + $product->setAttributeSetId($this->defaultSetId); + + return $product; + } + + /** + * Updates product. + * + * @param ProductInterface $product + * @param array $attributeData + * @return void + */ + protected function saveProduct(ProductInterface $product, array $attributeData): void + { + $product->addData($attributeData); + $this->productRepository->save($product); + } + + /** + * Adds additional array nesting to expected meta. + * + * @param array $attributeMeta + * @param string $groupCode + * @param string $attributeCode + * @return array + */ + protected function addMetaNesting(array $attributeMeta, string $groupCode, string $attributeCode): array + { + return [ + $groupCode => [ + 'arguments' => ['data' => ['config' => ['dataScope' => 'data.product']]], + 'children' => [ + 'container_' . $attributeCode => [ + 'children' => [$attributeCode => ['arguments' => ['data' => ['config' => $attributeMeta]]]], + ], + ], + ], + ]; + } + + /** + * Adds additional array nesting to expected data. + * + * @param array $data + * @return array + */ + protected function addDataNesting(array $data): array + { + return [1 => ['product' => $data]]; + } + + /** + * Returns attribute codes from product meta data array. + * + * @param array $actualMeta + * @return array + */ + protected function getUsedAttributes(array $actualMeta): array + { + $usedAttributes = []; + foreach ($actualMeta as $group) { + foreach (array_keys($group['children']) as $field) { + $usedAttributes[] = str_replace('container_', '', $field); + } + } + + return $usedAttributes; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php index 71030adff29da..4890d179c7d95 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php @@ -7,96 +7,13 @@ namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Locator\LocatorInterface; -use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav; -use Magento\Store\Api\Data\StoreInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\Ui\DataProvider\Mapper\FormElement; -use Magento\Ui\DataProvider\Mapper\MetaProperties; -use PHPUnit\Framework\TestCase; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractEavTest; /** * Tests for eav product form modifier for attribute set groups. */ -class AttributeSetGroupsTest extends TestCase +class AttributeSetGroupsTest extends AbstractEavTest { - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var LocatorInterface - */ - private $locatorMock; - - /** - * @var FormElement - */ - private $formElement; - - /** - * @var MetaProperties - */ - private $metaPropertiesMapper; - - /** - * @var Eav - */ - private $productFormModifier; - - /** - * @var CompositeConfigProcessor - */ - private $compositeConfigProcessor; - - /** - * @inheritdoc - */ - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $store = $this->objectManager->get(StoreInterface::class); - $this->locatorMock = $this->createMock(LocatorInterface::class); - $this->locatorMock->expects($this->any())->method('getStore')->willReturn($store); - $this->formElement = $this->objectManager->create( - FormElement::class, - [ - 'mappings' => [], - ] - ); - $this->metaPropertiesMapper = $this->objectManager->create( - MetaProperties::class, - [ - 'mappings' => [], - ] - ); - $this->compositeConfigProcessor = $this->objectManager->create( - CompositeConfigProcessor::class, - [ - 'eavWysiwygDataProcessors' => [], - ] - ); - $this->productFormModifier = $this->objectManager->create( - Eav::class, - [ - 'locator' => $this->locatorMock, - 'formElementMapper' => $this->formElement, - 'metaPropertiesMapper' => $this->metaPropertiesMapper, - 'wysiwygConfigProcessor' => $this->compositeConfigProcessor, - ] - ); - parent::setUp(); - } - /** * Check that custom group for custom attribute set not added to product form modifier meta data. * @@ -108,9 +25,8 @@ protected function setUp() */ public function testGroupDoesNotAddToProductFormMeta(): void { - $product = $this->productRepository->get('simple'); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $meta = $this->productFormModifier->modifyMeta([]); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $meta = $this->eavModifier->modifyMeta([]); $this->assertArrayNotHasKey( 'test-attribute-group-name', $meta, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php new file mode 100644 index 0000000000000..16ad3d3ad4564 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php @@ -0,0 +1,86 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'boolean_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['boolean_attribute' => 1]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('boolean_attribute', '0'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => '0']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'boolean_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'boolean', + 'formElement' => 'checkbox', + 'visible' => '1', + 'required' => '0', + 'label' => 'Boolean Attribute', + 'code' => 'boolean_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => '1', + 'false' => '0', + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php new file mode 100644 index 0000000000000..feac956ddf549 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php @@ -0,0 +1,81 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'date_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['date_attribute' => '01/01/2010']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('date_attribute', '01/01/2000'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => '2000-01-01 00:00:00']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'date_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'date', + 'formElement' => 'date', + 'visible' => '1', + 'required' => '0', + 'label' => 'Date Attribute', + 'code' => 'date_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php new file mode 100644 index 0000000000000..901613498e53a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php @@ -0,0 +1,67 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'decimal_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['decimal_attribute' => '10.00']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'price', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'Decimal Attribute', + 'code' => 'decimal_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + 'validation' => [ + 'validate-zero-or-greater' => true, + ], + 'addbefore' => '$' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php new file mode 100644 index 0000000000000..fbf752cc9e239 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php @@ -0,0 +1,47 @@ +callModifyMetaAndAssert($this->getProduct(), $expectedMeta); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_admin_store.php + * @return void + */ + public function testModifyData(): void + { + $expectedData = include __DIR__ . '/../_files/eav_expected_data_output.php'; + $this->callModifyDataAndAssert($this->getProduct(), $expectedData); + } + + /** + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $expectedMeta = include __DIR__ . '/../_files/eav_expected_meta_output_w_default.php'; + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php new file mode 100644 index 0000000000000..75a20145fdcda --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php @@ -0,0 +1,41 @@ +locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('image_attribute', $this->getUsedAttributes($actualMeta)); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getNewProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('image_attribute', $this->getUsedAttributes($actualMeta)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php new file mode 100644 index 0000000000000..eefb49dcf6239 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php @@ -0,0 +1,65 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'multiselect_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $optionValue = $this->getOptionValueByLabel('multiselect_attribute', 'Option 3'); + $attributeData = ['multiselect_attribute' => $optionValue]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'multiselect', + 'formElement' => 'multiselect', + 'visible' => '1', + 'required' => '0', + 'label' => 'Multiselect Attribute', + 'code' => 'multiselect_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('multiselect_attribute'), + 'componentType' => 'field', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php new file mode 100644 index 0000000000000..493608a43b77b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php @@ -0,0 +1,66 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'dropdown_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = [ + 'dropdown_attribute' => $this->getOptionValueByLabel('dropdown_attribute', 'Option 3') + ]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'select', + 'formElement' => 'select', + 'visible' => '1', + 'required' => '0', + 'label' => 'Drop-Down Attribute', + 'code' => 'dropdown_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('dropdown_attribute'), + 'componentType' => 'field', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php new file mode 100644 index 0000000000000..dfa40d138d640 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php @@ -0,0 +1,81 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'varchar_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('varchar_attribute', 'test'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => 'test']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'varchar_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['varchar_attribute' => 'Test message']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'text', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'Varchar Attribute', + 'code' => 'varchar_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 6dd3436041b33..83c6e99df629e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -3,124 +3,218 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; +use Magento\Eav\Api\AttributeSetRepositoryInterface; +use Magento\Eav\Model\AttributeSetRepository; +use Magento\TestFramework\Eav\Model\GetAttributeGroupByName; +use Magento\TestFramework\Eav\Model\ResourceModel\GetEntityIdByAttributeId; + /** + * Provides tests for eav modifier used in products admin form data provider. + * * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - * @magentoAppArea adminhtml */ -class EavTest extends \PHPUnit\Framework\TestCase +class EavTest extends AbstractEavTest { /** - * @var \Magento\Framework\ObjectManagerInterface + * @var GetAttributeGroupByName */ - protected $objectManager; + private $attributeGroupByName; /** - * @var \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav + * @var GetEntityIdByAttributeId */ - protected $eavModifier; + private $getEntityIdByAttributeId; /** - * @var \Magento\Catalog\Model\Locator\LocatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeSetRepository */ - protected $locatorMock; + private $setRepository; + /** + * @inheritdoc + */ protected function setUp() { - $mappings = [ - "text" => "input", - "hidden" => "input", - "boolean" => "checkbox", - "media_image" => "image", - "price" => "input", - "weight" => "input", - "gallery" => "image" - ]; - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->locatorMock = $this->createMock(\Magento\Catalog\Model\Locator\LocatorInterface::class); - $store = $this->objectManager->get(\Magento\Store\Api\Data\StoreInterface::class); - $this->locatorMock->expects($this->any())->method('getStore')->willReturn($store); - $this->eavModifier = $this->objectManager->create( - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::class, - [ - 'locator' => $this->locatorMock, - 'formElementMapper' => $this->objectManager->create( - \Magento\Ui\DataProvider\Mapper\FormElement::class, - ['mappings' => $mappings] - ) - ] - ); parent::setUp(); + $this->attributeGroupByName = $this->objectManager->get(GetAttributeGroupByName::class); + $this->getEntityIdByAttributeId = $this->objectManager->get(GetEntityIdByAttributeId::class); + $this->setRepository = $this->objectManager->get(AttributeSetRepositoryInterface::class); } /** + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @dataProvider modifyMetaWithAttributeProvider + * @param string $groupName + * @param string $groupCode + * @param string $attributeCode + * @param array $attributeMeta + * @return void */ - public function testModifyMeta() - { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->load(1); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedMeta = include __DIR__ . '/_files/eav_expected_meta_output.php'; + public function testModifyMetaWithAttributeInGroups( + string $groupName, + string $groupCode, + string $attributeCode, + array $attributeMeta + ): void { + $attributeGroup = $this->attributeGroupByName->execute($this->defaultSetId, $groupName); + $groupId = $attributeGroup ? $attributeGroup->getAttributeGroupId() : 'ynode-1'; + $data = [ + 'attributes' => [ + [$this->attributeRepository->get($attributeCode)->getAttributeId(), $groupId, 1], + ], + 'groups' => [ + [$groupId, $groupName, 1], + ], + ]; + $this->prepareAttributeSet($data); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); $actualMeta = $this->eavModifier->modifyMeta([]); + $expectedMeta = $this->addMetaNesting($attributeMeta, $groupCode, $attributeCode); $this->prepareDataForComparison($actualMeta, $expectedMeta); $this->assertEquals($expectedMeta, $actualMeta); } - public function testModifyMetaNewProduct() + /** + * @return array + */ + public function modifyMetaWithAttributeProvider(): array + { + $textAttributeMeta = [ + 'dataType' => 'textarea', + 'formElement' => 'textarea', + 'visible' => '1', + 'required' => '0', + 'label' => 'Text Attribute', + 'code' => 'text_attribute', + 'source' => 'content', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + ]; + $urlKeyAttributeMeta = [ + 'dataType' => 'text', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'URL Key', + 'code' => 'url_key', + 'source' => 'image-management', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + ]; + + return [ + 'new_attribute_in_existing_group' => [ + 'group_name' => 'Content', + 'group_code' => 'content', + 'attribute_code' => 'text_attribute', + 'attribute_meta' => $textAttributeMeta, + ], + 'new_attribute_in_new_group' => [ + 'group_name' => 'Test', + 'group_code' => 'test', + 'attribute_code' => 'text_attribute', + 'attribute_meta' => array_merge($textAttributeMeta, ['source' => 'test']), + ], + 'old_attribute_moved_to_existing_group' => [ + 'group_name' => 'Images', + 'group_code' => 'image-management', + 'attribute_code' => 'url_key', + 'attribute_meta' => $urlKeyAttributeMeta, + ], + 'old_attribute_moved_to_new_group' => [ + 'group_name' => 'Test', + 'group_code' => 'test', + 'attribute_code' => 'url_key', + 'attribute_meta' => array_merge($urlKeyAttributeMeta, ['source' => 'test']), + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyMetaWithChangedGroupSorting(): void + { + $contentGroupId = $this->attributeGroupByName->execute($this->defaultSetId, 'Content') + ->getAttributeGroupId(); + $imagesGroupId = $this->attributeGroupByName->execute($this->defaultSetId, 'Images') + ->getAttributeGroupId(); + $additional = ['groups' => [[$contentGroupId, 'Content', 2], [$imagesGroupId, 'Images', 1]]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $groupCodes = ['image-management', 'content']; + $groups = array_filter( + array_keys($actualMeta), + function ($group) use ($groupCodes) { + return in_array($group, $groupCodes); + } + ); + $this->assertEquals($groupCodes, $groups); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyMetaWithRemovedGroup(): void { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->setAttributeSetId(4); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedMeta = include __DIR__ . '/_files/eav_expected_meta_output_w_default.php'; + $designAttributes = ['page_layout', 'options_container', 'custom_layout_update']; + $designGroupId =$this->attributeGroupByName->execute($this->defaultSetId, 'Design') + ->getAttributeGroupId(); + $additional = ['removeGroups' => [$designGroupId]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); $actualMeta = $this->eavModifier->modifyMeta([]); - $this->prepareDataForComparison($actualMeta, $expectedMeta); - $this->assertEquals($expectedMeta, $actualMeta); + $this->assertArrayNotHasKey('design', $actualMeta, 'Group "Design" still visible.'); + $this->assertEmpty( + array_intersect($designAttributes, $this->getUsedAttributes($actualMeta)), + 'Attributes from "Design" group still visible.' + ); } /** - * @magentoDataFixture Magento/Catalog/_files/product_simple_with_admin_store.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void */ - public function testModifyData() + public function testModifyMetaWithRemovedAttribute(): void { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->load(1); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedData = include __DIR__ . '/_files/eav_expected_data_output.php'; - $actualData = $this->eavModifier->modifyData([]); - $this->prepareDataForComparison($actualData, $expectedData); - $this->assertEquals($expectedData, $actualData); + $attributeId = (int)$this->attributeRepository->get('meta_description')->getAttributeId(); + $entityAttributeId = $this->getEntityIdByAttributeId->execute($this->defaultSetId, $attributeId); + $additional = ['not_attributes' => [$entityAttributeId]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('meta_description', $this->getUsedAttributes($actualMeta)); } /** - * Prepare data for comparison to avoid false positive failures. - * - * Make sure that $data contains all the data contained in $expectedData, - * ignore all fields not declared in $expectedData + * Updates default attribute set. * - * @param array &$data - * @param array $expectedData + * @param array $additional * @return void */ - private function prepareDataForComparison(array &$data, array $expectedData) + private function prepareAttributeSet(array $additional): void { - foreach ($data as $key => &$item) { - if (!isset($expectedData[$key])) { - unset($data[$key]); - continue; - } - if ($item instanceof \Magento\Framework\Phrase) { - $item = (string)$item; - } elseif (is_array($item)) { - $this->prepareDataForComparison($item, $expectedData[$key]); - } elseif ($key === 'price_id' || $key === 'sortOrder') { - $data[$key] = '__placeholder__'; - } - } + $set = $this->setRepository->get($this->defaultSetId); + $data = [ + 'attributes' => [], + 'groups' => [], + 'not_attributes' => [], + 'removeGroups' => [], + 'attribute_set_name' => 'Default', + ]; + $set->organizeData(array_merge($data, $additional)); + $this->setRepository->save($set); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php index fa0a664738c1d..f6f4cc0e15159 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php @@ -40,9 +40,6 @@ "globalScope" => false, "code" => "status", "sortOrder" => "__placeholder__", - "service" => [ - "template" => "ui/form/element/helper/service" - ], "componentType" => "field" ], ], @@ -66,9 +63,6 @@ "globalScope" => false, "code" => "name", "sortOrder" => "__placeholder__", - "service" => [ - "template" => "ui/form/element/helper/service" - ], "componentType" => "field", "validation" => [ "required-entry" => true diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php new file mode 100644 index 0000000000000..43ea543a5909e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'date_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'date_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'date', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Date Attribute'], + 'backend_type' => 'datetime', + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php new file mode 100644 index 0000000000000..b20da89be0136 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('date_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php new file mode 100644 index 0000000000000..949475607d773 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php @@ -0,0 +1,52 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'decimal_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'decimal_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'price', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Decimal Attribute'], + 'backend_type' => 'decimal', + 'backend_model' => Price::class, + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php new file mode 100644 index 0000000000000..de187379bc99a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('decimal_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php new file mode 100644 index 0000000000000..d8f0299f2a876 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'image_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'image_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'media_image', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Image Attribute'], + 'backend_type' => 'varchar', + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php new file mode 100644 index 0000000000000..c9d28686741b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('image_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php new file mode 100644 index 0000000000000..d23acfcdd196e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'varchar_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'varchar_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'text', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Varchar Attribute'], + 'backend_type' => 'varchar', + ] + ); + $attribute->save(); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php new file mode 100644 index 0000000000000..c238803a77108 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('varchar_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/TextSwatchAttributeTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/TextSwatchAttributeTest.php new file mode 100644 index 0000000000000..4ee01d8541f4a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/TextSwatchAttributeTest.php @@ -0,0 +1,65 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'text_swatch_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $optionValue = $this->getOptionValueByLabel('text_swatch_attribute', 'Option 1'); + $attributeData = ['text_swatch_attribute' => $optionValue]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'select', + 'formElement' => 'select', + 'visible' => '1', + 'required' => '0', + 'label' => 'Text swatch attribute', + 'code' => 'text_swatch_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('text_swatch_attribute'), + 'componentType' => 'field', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/VisualSwatchAttributeTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/VisualSwatchAttributeTest.php new file mode 100644 index 0000000000000..34a263081c92f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/Ui/DataProvider/Product/Form/Modifier/Eav/VisualSwatchAttributeTest.php @@ -0,0 +1,71 @@ +addMetaNesting( + $this->getAttributeMeta(), + 'product-details', + 'color_swatch' + ); + $this->callModifyMetaAndAssert($this->getProduct(), $expectedMeta); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = [ + 'color_swatch' => $this->getOptionValueByLabel('color_swatch', 'option 1') + ]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'swatch_visual', + 'formElement' => 'swatch_visual', + 'visible' => '1', + 'required' => '1', + 'label' => 'Attribute ', + 'code' => 'color_swatch', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('color_swatch'), + 'componentType' => 'field', + 'validation' => [ + 'required-entry' => true, + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute.php new file mode 100644 index 0000000000000..7dd3c9fb36e3d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute.php @@ -0,0 +1,31 @@ +create(CategorySetup::class); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +$attribute = $objectManager->create(AttributeFactory::class)->create() + ->loadByCode($entityType, 'color_swatch'); + +if ($attribute->getAttributeId()) { + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getAttributeId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..14aecdbc0c729 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_swatch_attribute_rollback.php @@ -0,0 +1,8 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'text_swatch_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'frontend_label' => ['Text swatch attribute'], + 'entity_type_id' => $entityType, + 'frontend_input' => 'select', + 'backend_type' => 'int', + 'is_required' => '0', + 'attribute_code' => 'text_swatch_attribute', + 'is_global' => '1', + 'is_unique' => '0', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_filterable' => '1', + 'is_filterable_in_search' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'used_in_product_listing' => '1', + 'used_for_sort_by' => '0', + 'swatch_input_type' => 'text', + 'optiontext' => [ + 'order' => [ + 'option_0' => 1, + 'option_1' => 2, + 'option_3' => 3, + ], + 'value' => [ + 'option_0' => ['Option 1'], + 'option_1' => ['Option 2'], + 'option_2' => ['Option 3'], + ], + ], + 'swatchtext' => [ + 'value' => [ + 'option_0' => ['Swatch 1'], + 'option_1' => ['Swatch 2'], + 'option_2' => ['Swatch 3'], + ], + ], + ] + ); + $attribute->save(); + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..67157532bdb98 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('text_swatch_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Weee/Ui/DataProvider/Product/Form/Modifier/Eav/FixedAttributeTest.php b/dev/tests/integration/testsuite/Magento/Weee/Ui/DataProvider/Product/Form/Modifier/Eav/FixedAttributeTest.php new file mode 100644 index 0000000000000..0987d8fbe57dd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Weee/Ui/DataProvider/Product/Form/Modifier/Eav/FixedAttributeTest.php @@ -0,0 +1,77 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'fixed_product_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = [ + 'fixed_product_attribute' => [ + ['website_id' => 0, 'country' => 'US', 'state' => 0, 'price' => 12.70, 'delete' => ''] + ] + ]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting( + [ + 'fixed_product_attribute' => [ + [ + 'website_id' => '0', + 'country' => 'US', + 'state' => '0', + 'value' => '12.7000', + 'website_value' => 12.7, + ] + ] + ] + ); + $this->callModifyDataAndAssert($this->getProduct(), $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'visible' => '1', + 'required' => '0', + 'label' => '', + 'code' => 'fixed_product_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + ]; + } +} From c23393d20e9e3e6493f6326a2cc80c4e36cdf86d Mon Sep 17 00:00:00 2001 From: Nazarn96 Date: Tue, 12 Nov 2019 16:34:02 +0200 Subject: [PATCH 2008/2437] Fix error when open grid with opened preview --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 8a22b06d53dc5..cc637d8a3c4d1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -101,7 +101,7 @@ define([ show: function (record) { var img; - if (record._rowIndex === this.lastOpenedImage() + if (record._rowIndex === this.visibleRecord() && this.isVisible(record)) { this.hide(); return; From b0fb36394eaca8f656c61d3c2e24f27ac5c35e33 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko Date: Tue, 12 Nov 2019 15:51:48 +0200 Subject: [PATCH 2009/2437] Change the way the GetById command is tested --- ...ionDuringMediaAssetInitializationTest.php} | 51 +-------- .../GetByIdExceptionNoSuchEntityTest.php | 100 ++++++++++++++++++ .../Command/GetByIdExceptionOnGetDataTest.php | 89 ++++++++++++++++ .../Asset/Command/GetByIdSuccessfulTest.php | 99 +++++++++++++++++ 4 files changed, 290 insertions(+), 49 deletions(-) rename app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/{GetByIdTest.php => GetByIdExceptionDuringMediaAssetInitializationTest.php} (63%) create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php similarity index 63% rename from app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php rename to app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php index eb9d3c80039e9..bb586db60dad8 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php @@ -11,21 +11,17 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; use Magento\Framework\Exception\IntegrationException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\MediaGallery\Model\Asset\Command\GetById; -use Magento\MediaGalleryApi\Api\Data\AssetInterface; use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Zend\Db\Adapter\Driver\Pdo\Statement; /** - * Test the GetById command model - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * Test the GetById command model with exception during media asset initialization */ -class GetByIdTest extends TestCase +class GetByIdExceptionDuringMediaAssetInitializationTest extends \PHPUnit\Framework\TestCase { private const MEDIA_ASSET_STUB_ID = 1; @@ -89,49 +85,6 @@ protected function setUp(): void $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); } - /** - * Test successful get media asset by id command execution. - */ - public function testSuccessfulGetByIdExecution(): void - { - $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); - $this->adapter->method('query')->willReturn($this->statementMock); - - $mediaAssetStub = $this->getMockBuilder(AssetInterface::class)->getMock(); - $this->assetFactory->expects($this->once())->method('create')->willReturn($mediaAssetStub); - - $this->assertEquals( - $mediaAssetStub, - $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID) - ); - } - - /** - * Test an exception during the get asset data query. - */ - public function testExceptionDuringGetMediaAssetData(): void - { - $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); - $this->adapter->method('query')->willThrowException(new \Exception()); - - $this->expectException(IntegrationException::class); - - $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); - } - - /** - * Test case when there is no found media asset by id. - */ - public function testNotFoundMediaAssetException(): void - { - $this->statementMock->method('fetch')->willReturn([]); - $this->adapter->method('query')->willReturn($this->statementMock); - - $this->expectException(NoSuchEntityException::class); - - $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); - } - /** * Test case when a problem occurred during asset initialization from received data. */ diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php new file mode 100644 index 0000000000000..d6df9ca422e06 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -0,0 +1,100 @@ + 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $this->logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test case when there is no found media asset by id. + */ + public function testNotFoundMediaAssetException(): void + { + $this->statementMock->method('fetch')->willReturn([]); + $this->adapter->method('query')->willReturn($this->statementMock); + + $this->expectException(NoSuchEntityException::class); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php new file mode 100644 index 0000000000000..958a5f3ccb609 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php @@ -0,0 +1,89 @@ + 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $assetFactory = $this->createMock(AssetInterfaceFactory::class); + $logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $assetFactory, + 'logger' => $logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test an exception during the get asset data query. + */ + public function testExceptionDuringGetMediaAssetData(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willThrowException(new \Exception()); + + $this->expectException(IntegrationException::class); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php new file mode 100644 index 0000000000000..72e44027469cd --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php @@ -0,0 +1,99 @@ + 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test successful get media asset by id command execution. + */ + public function testSuccessfulGetByIdExecution(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willReturn($this->statementMock); + + $mediaAssetStub = $this->getMockBuilder(AssetInterface::class)->getMock(); + $this->assetFactory->expects($this->once())->method('create')->willReturn($mediaAssetStub); + + $this->assertEquals( + $mediaAssetStub, + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID) + ); + } +} From 549a491739cbf07cd720337d12792e07b16dd0ab Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets Date: Tue, 12 Nov 2019 12:25:12 -0600 Subject: [PATCH 2010/2437] Fix unit test after merge. --- .../Test/Unit/Model/Template/FilterTest.php | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php index 3e23578ec1be4..ebc9130297823 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php @@ -111,6 +111,16 @@ class FilterTest extends \PHPUnit\Framework\TestCase */ private $pubDirectoryRead; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Filter\VariableResolver\StrategyResolver + */ + private $variableResolver; + + /** + * @var array + */ + private $directiveProcessors; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -178,6 +188,30 @@ protected function setUp() $this->pubDirectoryRead = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\Read::class) ->disableOriginalConstructor() ->getMock(); + $this->variableResolver = + $this->getMockBuilder(\Magento\Framework\Filter\VariableResolver\StrategyResolver::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directiveProcessors = [ + 'depend' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\DependDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'if' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\IfDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'template' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\TemplateDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'legacy' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\LegacyDirective::class) + ->disableOriginalConstructor() + ->getMock(), + ]; + } /** @@ -204,6 +238,8 @@ protected function getModel($mockedMethods = null) $this->configVariables, [], $this->cssInliner, + $this->directiveProcessors, + $this->variableResolver, $this->cssProcessor, $this->pubDirectory ] @@ -351,43 +387,6 @@ public function testApplyInlineCssThrowsExceptionWhenDesignParamsNotSet() $filter->applyInlineCss('test'); } - /** - * Ensure that after filter callbacks are reset after exception is thrown during filtering - */ - public function testAfterFilterCallbackGetsResetWhenExceptionTriggered() - { - $value = '{{var random_var}}'; - $exception = new \Exception('Test exception'); - $exceptionResult = sprintf(__('Error filtering template: %s'), $exception->getMessage()); - - $this->appState->expects($this->once()) - ->method('getMode') - ->will($this->returnValue(\Magento\Framework\App\State::MODE_DEVELOPER)); - $this->logger->expects($this->once()) - ->method('critical') - ->with($exception); - - $filter = $this->getModel(['varDirective', 'resetAfterFilterCallbacks']); - $filter->expects($this->once()) - ->method('varDirective') - ->will($this->throwException($exception)); - - // Callbacks must be reset after exception is thrown - $filter->expects($this->once()) - ->method('resetAfterFilterCallbacks'); - - // Build arbitrary object to pass into the addAfterFilterCallback method - $callbackObject = $this->getMockBuilder('stdObject') - ->setMethods(['afterFilterCallbackMethod']) - ->getMock(); - // Callback should never run due to exception happening during filtering - $callbackObject->expects($this->never()) - ->method('afterFilterCallbackMethod'); - $filter->addAfterFilterCallback([$callbackObject, 'afterFilterCallbackMethod']); - - $this->assertEquals($exceptionResult, $filter->filter($value)); - } - public function testConfigDirectiveAvailable() { $path = "web/unsecure/base_url"; From 8e39f49b48692f2e820066815244e8532c247fde Mon Sep 17 00:00:00 2001 From: Nazar Klovanych Date: Tue, 12 Nov 2019 20:27:25 +0200 Subject: [PATCH 2011/2437] Refactor to use _.isUndefined --- lib/web/mage/adminhtml/browser.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 898910e477ad0..4cbb8fb025b78 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -62,8 +62,7 @@ define([ this.modal.modal('openModal'); return; - } else if (typeof options === 'undefined' - && self.modalLoaded === true) { + } else if (_.isUndefined(options) && self.modalLoaded === true) { this.modal.modal('openModal'); return; } From c8bc4769ac70d3a759adf6d3aeec51f31f2be62f Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets Date: Tue, 12 Nov 2019 13:16:57 -0600 Subject: [PATCH 2012/2437] Fix static. --- app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php index ebc9130297823..778573396b011 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php @@ -211,7 +211,6 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(), ]; - } /** From 47a18106269a46a91158beaed06faf3d73f31880 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk Date: Tue, 12 Nov 2019 14:09:52 -0600 Subject: [PATCH 2013/2437] Community MTF to MFTF test conversion code delivery --- ...DeleteCategoryUrlRewriteHypenAsRequestPathTest.xml} | 10 ++++++---- ...minDeleteCategoryUrlRewriteWithRequestPathTest.xml} | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) rename app/code/Magento/UrlRewrite/Test/Mftf/Test/{AdminDeleteCategoryUrlRewriteEntityFirstVariationTest.xml => AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml} (83%) rename app/code/Magento/UrlRewrite/Test/Mftf/Test/{AdminDeleteCategoryUrlRewriteEntitySecondVariationTest.xml => AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml} (84%) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityFirstVariationTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml similarity index 83% rename from app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityFirstVariationTest.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml index c653bf2e910b1..ca292c384979f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntityFirstVariationTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml @@ -7,11 +7,13 @@ --> - + - - - <description value="Login as admin and delete category Url Rewrite"/> + <features value="UrlRewrite" /> + <stories value="Delete custom URL rewrite"/> + <title value="Delete category URL rewrite, hyphen as request path"/> + <description value="Delete category URL rewrite, hyphen as request path"/> + <testCaseId value="MC-5348" /> <group value="urlRewrite"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntitySecondVariationTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml similarity index 84% rename from app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntitySecondVariationTest.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml index f613ee57296e9..3c0dc8dc2be10 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteEntitySecondVariationTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml @@ -7,11 +7,13 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDeleteCategoryUrlRewriteEntitySecondVariationTest"> + <test name="AdminDeleteCategoryUrlRewriteWithRequestPathTest"> <annotations> - <stories value="Delete category URL rewrite second scenario"/> - <title value="Delete category URL rewrite second scenario"/> - <description value="Login as admin and delete category Url Rewrite"/> + <features value="UrlRewrite" /> + <stories value="Delete custom URL rewrite"/> + <title value="Delete category URL rewrite, with request path"/> + <description value="Delete category URL rewrite, with request path"/> + <testCaseId value="MC-5349" /> <group value="urlRewrite"/> <group value="mtf_migrated"/> </annotations> From 9e12ac28a4449b92cdcb6130655c7e5a7fcd8f58 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Tue, 12 Nov 2019 16:16:48 -0600 Subject: [PATCH 2014/2437] MC-23029: The type of the State input field for the multistore is incorrect when restricted by country - Fix wrong scope for order address edit form on backend --- .../Sales/Block/Adminhtml/Order/Create/Form/Address.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index 3fe943c1b194c..bcdeb4e7d67de 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -14,6 +14,7 @@ /** * Order create address form + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Address extends \Magento\Sales\Block\Adminhtml\Order\Create\Form\AbstractForm @@ -216,15 +217,12 @@ public function getAddressCollectionJson() * Prepare Form and add elements to form * * @return $this - * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _prepareForm() { - $storeId = $this->getCreateOrderModel() - ->getSession() - ->getStoreId(); + $storeId = $this->getAddressStoreId(); $this->_storeManager->setCurrentStore($storeId); $fieldset = $this->_form->addFieldset('main', ['no_container' => true]); From 01621ca29e0b0008271fc7f099b782341b2a551d Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com> Date: Wed, 13 Nov 2019 11:10:30 +0200 Subject: [PATCH 2015/2437] magento/magento2#25354: Static tests fix. --- .../Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php index a44f45cdbe805..a95b1812e68f9 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/MySQL/DbSchemaReader.php @@ -147,9 +147,10 @@ public function readIndexes($tableName, $resource) } /** + * Read references (foreign keys) from Magento tables. + * * As MySQL has bug and do not show foreign keys during DESCRIBE and other directives required - * to take it from SHOW CREATE TABLE ... - * command + * to take it from "SHOW CREATE TABLE ..." command. * * @inheritdoc */ @@ -177,6 +178,7 @@ public function getCreateTableSql($tableName, $resource) /** * Reading DB constraints. + * * Primary and unique constraints are always non_unique=0. * * @inheritdoc From 3e513d0fd839bb1d98f4ad991ba946c780310458 Mon Sep 17 00:00:00 2001 From: mahesh <mahesh721@webkul.com> Date: Wed, 13 Nov 2019 16:11:04 +0530 Subject: [PATCH 2016/2437] unitTest covered for changes --- .../Model/Import/Product/Type/OptionTest.php | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php index 9f63decac5ff7..55382ab284638 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php @@ -776,6 +776,75 @@ public function testValidateAmbiguousData( $this->assertEquals($errors, $resultErrors); } + /** + * Test for row without store view code field + * @param array $rowData + * @param array $responseData + * + * @covers \Magento\CatalogImportExport\Model\Import\Product\Option::_parseCustomOptions + * @dataProvider validateRowStoreViewCodeFieldDataProvider + */ + public function testValidateRowDataForStoreViewCodeField($rowData, $responseData) + { + $reflection = new \ReflectionClass(\Magento\CatalogImportExport\Model\Import\Product\Option::class); + $reflectionMethod = $reflection->getMethod('_parseCustomOptions'); + $reflectionMethod->setAccessible(true); + $result = $reflectionMethod->invoke($this->model, $rowData); + $this->assertEquals($responseData, $result); + } + + /** + * Data provider for test of method _parseCustomOptions + * + * @return array + */ + public function validateRowStoreViewCodeFieldDataProvider() + { + return [ + 'with_store_view_code' => [ + '$rowData' => [ + 'store_view_code' => '', + 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + ], + '$responseData' => [ + 'store_view_code' => '', + 'custom_options' => [ + 'Test Field Title' => [ + [ + 'name' => 'Test Field Title', + 'type' => 'field', + 'required' => '1', + 'sku' => '1-text', + 'price' => '0', + 'price_type' => 'fixed', + '_custom_option_store' => '' + ] + ] + ] + ], + ], + 'without_store_view_code' => [ + '$rowData' => [ + 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + ], + '$responseData' => [ + 'custom_options' => [ + 'Test Field Title' => [ + [ + 'name' => 'Test Field Title', + 'type' => 'field', + 'required' => '1', + 'sku' => '1-text', + 'price' => '0', + 'price_type' => 'fixed' + ] + ] + ] + ], + ] + ]; + } + /** * Data provider of row data and errors * From a7f7d0ae1660e13f08d9e5fc5246429f40299a92 Mon Sep 17 00:00:00 2001 From: mahesh <mahesh721@webkul.com> Date: Wed, 13 Nov 2019 16:49:37 +0530 Subject: [PATCH 2017/2437] static test fixed --- .../Test/Unit/Model/Import/Product/Type/OptionTest.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php index 55382ab284638..892b7f18fdc0f 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php @@ -780,7 +780,7 @@ public function testValidateAmbiguousData( * Test for row without store view code field * @param array $rowData * @param array $responseData - * + * * @covers \Magento\CatalogImportExport\Model\Import\Product\Option::_parseCustomOptions * @dataProvider validateRowStoreViewCodeFieldDataProvider */ @@ -804,7 +804,8 @@ public function validateRowStoreViewCodeFieldDataProvider() 'with_store_view_code' => [ '$rowData' => [ 'store_view_code' => '', - 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + 'custom_options' => + 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' ], '$responseData' => [ 'store_view_code' => '', @@ -825,7 +826,8 @@ public function validateRowStoreViewCodeFieldDataProvider() ], 'without_store_view_code' => [ '$rowData' => [ - 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + 'custom_options' => + 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' ], '$responseData' => [ 'custom_options' => [ From bf8b1e4e880d227c052641faec2973b853d8cb1a Mon Sep 17 00:00:00 2001 From: mahesh <mahesh721@webkul.com> Date: Wed, 13 Nov 2019 16:55:45 +0530 Subject: [PATCH 2018/2437] static test fixed removed spaces --- .../Test/Unit/Model/Import/Product/Type/OptionTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php index 892b7f18fdc0f..f8b14a471fd9c 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php @@ -804,7 +804,7 @@ public function validateRowStoreViewCodeFieldDataProvider() 'with_store_view_code' => [ '$rowData' => [ 'store_view_code' => '', - 'custom_options' => + 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' ], '$responseData' => [ @@ -826,7 +826,7 @@ public function validateRowStoreViewCodeFieldDataProvider() ], 'without_store_view_code' => [ '$rowData' => [ - 'custom_options' => + 'custom_options' => 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' ], '$responseData' => [ From e94edeaaac7947deed4835667f0f15320443d292 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 13 Nov 2019 14:13:09 +0200 Subject: [PATCH 2019/2437] magento/graphql-ce#975: [Test coverage] deleteCustomerAddress mutation. --- .../Customer/DeleteCustomerAddressTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php index 443b9d7ec53e5..e61333509ccbb 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php @@ -224,6 +224,28 @@ public function testDeleteAnotherCustomerAddress() $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * + * @expectedException Exception + * @expectedExceptionMessage Address "id" value should be specified + */ + public function testDeleteCustomerAddressWithZeroAddressEntityId() + { + $userName = 'customer@example.com'; + $password = 'password'; + $addressId = 0; + + $mutation + = <<<MUTATION +mutation { + deleteCustomerAddress(id: {$addressId}) +} +MUTATION; + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php From 3be5ac8e2e83415b8cafc340486b298038ed0421 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Wed, 13 Nov 2019 13:56:04 +0200 Subject: [PATCH 2020/2437] Fic static test, resolve issue by 2 insert image widget --- lib/web/mage/adminhtml/browser.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 4cbb8fb025b78..38a06d97634c8 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -11,11 +11,12 @@ define([ 'Magento_Ui/js/modal/prompt', 'Magento_Ui/js/modal/confirm', 'Magento_Ui/js/modal/alert', + 'underscore', 'Magento_Ui/js/modal/modal', 'jquery/ui', 'jquery/jstree/jquery.jstree', - 'mage/mage' -], function ($, wysiwyg, prompt, confirm, alert) { + 'mage/mage', +], function ($, wysiwyg, prompt, confirm, alert, _) { window.MediabrowserUtility = { windowId: 'modal_dialog_message', modalLoaded: false, @@ -63,13 +64,15 @@ define([ return; } else if (_.isUndefined(options) && self.modalLoaded === true) { - this.modal.modal('openModal'); - return; + if (self.targetElementId === url) { + this.modal.modal('openModal'); + return; + } } if (this.modal) { this.modal.html($(content).html()); - + if (options && typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } @@ -93,7 +96,7 @@ define([ self.modalLoaded = true; self.targetElementId = options ? options.targetElementId - : 'null'; + : url; }); }, From dee53ef8d5caa9edb9f13dda7abfd67af8a2f598 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 13 Nov 2019 15:48:34 +0200 Subject: [PATCH 2021/2437] Covering the AllowedQuantity by Unit Tests --- .../Unit/ViewModel/AllowedQuantityTest.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php diff --git a/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php new file mode 100644 index 0000000000000..7d59e7b5cc31a --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Store\Model\Store; +use Magento\Wishlist\ViewModel\AllowedQuantity; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class AllowedQuantityTest + */ +class AllowedQuantityTest extends TestCase +{ + /** + * @var AllowedQuantity + */ + private $sut; + + /** + * @var StockRegistry|MockObject + */ + private $stockRegistryMock; + + /** + * @var ItemInterface|MockObject + */ + private $itemMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->stockRegistryMock = $this->createMock(StockRegistry::class); + $this->itemMock = $this->getMockForAbstractClass( + ItemInterface::class, + [], + '', + false, + true, + true, + ['getMinSaleQty', 'getMaxSaleQty'] + ); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->sut = new AllowedQuantity( + $this->stockRegistryMock + ); + $this->sut->setItem($this->itemMock); + } + + /** + * Getting min and max qty test. + * + * @dataProvider saleQuantityDataProvider + * + * @param int $minSaleQty + * @param int $maxSaleQty + * @param array $expectedResult + */ + public function testGettingMinMaxQty(int $minSaleQty, int $maxSaleQty, array $expectedResult) + { + $this->storeMock->expects($this->atLeastOnce()) + ->method('getWebsiteId') + ->willReturn(1); + $this->productMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(1); + $this->productMock->expects($this->atLeastOnce()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->itemMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->itemMock->expects($this->any()) + ->method('getMinSaleQty') + ->willReturn($minSaleQty); + $this->itemMock->expects($this->any()) + ->method('getMaxSaleQty') + ->willReturn($maxSaleQty); + $this->stockRegistryMock->expects($this->any()) + ->method('getStockItem') + ->will($this->returnValue($this->itemMock)); + + $result = $this->sut->getMinMaxQty(); + + $this->assertEquals($result, $expectedResult); + } + + /** + * Sales quantity provider + * + * @return array + */ + public function saleQuantityDataProvider(): array + { + return [ + [ + 1, + 10, + [ + 'minAllowed' => 1, + 'maxAllowed' => 10 + ] + ], [ + 1, + 0, + [ + 'minAllowed' => 1, + 'maxAllowed' => 99999999 + ] + ] + ]; + } +} From 76a866ec70987dfb0e1c778db472a06c6d738781 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Wed, 13 Nov 2019 16:05:50 +0200 Subject: [PATCH 2022/2437] Fix static test --- lib/web/mage/adminhtml/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 38a06d97634c8..09ba2573d0725 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -494,7 +494,7 @@ define([ var nodeData = $(element).data('node'); if (index > 0) { - breadcrumbs.append($('<li>\/</li>')); + breadcrumbs.append($('<li>\/</li>')); //eslint-disable-line } breadcrumbs.append($('<li />') .data('node', nodeData).attr('data-row', 'breadcrumb').text(nodeData.text)); From 3ef9c536a33a0835b20558d6bffbf6dfaf9cba1f Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 13 Nov 2019 16:10:22 +0200 Subject: [PATCH 2023/2437] Fixing the namespace --- .../Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php index 7d59e7b5cc31a..23e28abf20959 100644 --- a/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\Wishlist\Test\Unit\Observer; +namespace Magento\Wishlist\Test\Unit\ViewModel; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; From 061d5a31391088b058f1735872c0895b9c5098b9 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Wed, 13 Nov 2019 16:29:14 +0200 Subject: [PATCH 2024/2437] Change the way the setup method is called for GetById command test --- ...IdExceptionDuringMediaAssetInitializationTest.php | 2 +- .../Command/GetByIdExceptionNoSuchEntityTest.php | 9 ++------- .../Asset/Command/GetByIdExceptionOnGetDataTest.php | 12 ++++++++++-- .../Model/Asset/Command/GetByIdSuccessfulTest.php | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php index bb586db60dad8..0d0bfc510b518 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php @@ -19,7 +19,7 @@ use Zend\Db\Adapter\Driver\Pdo\Statement; /** - * Test the GetById command model with exception during media asset initialization + * Test the GetById command with exception during media asset initialization */ class GetByIdExceptionDuringMediaAssetInitializationTest extends \PHPUnit\Framework\TestCase { diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php index d6df9ca422e06..52d1c3f82f8f3 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -52,11 +52,6 @@ class GetByIdExceptionNoSuchEntityTest extends \PHPUnit\Framework\TestCase */ private $statementMock; - /** - * @var LoggerInterface|MockObject - */ - private $logger; - /** * Initialize basic test class mocks */ @@ -64,14 +59,14 @@ protected function setUp(): void { $resourceConnection = $this->createMock(ResourceConnection::class); $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); - $this->logger = $this->createMock(LoggerInterface::class); + $logger = $this->createMock(LoggerInterface::class); $this->getMediaAssetById = (new ObjectManager($this))->getObject( GetById::class, [ 'resourceConnection' => $resourceConnection, 'assetFactory' => $this->assetFactory, - 'logger' => $this->logger, + 'logger' => $logger, ] ); $this->adapter = $this->createMock(AdapterInterface::class); diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php index 958a5f3ccb609..a709c2d214bda 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php @@ -41,6 +41,11 @@ class GetByIdExceptionOnGetDataTest extends \PHPUnit\Framework\TestCase */ private $selectStub; + /** + * @var LoggerInterface|MockObject + */ + private $logger; + /** * @var Statement|MockObject */ @@ -53,14 +58,14 @@ protected function setUp(): void { $resourceConnection = $this->createMock(ResourceConnection::class); $assetFactory = $this->createMock(AssetInterfaceFactory::class); - $logger = $this->createMock(LoggerInterface::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->getMediaAssetById = (new ObjectManager($this))->getObject( GetById::class, [ 'resourceConnection' => $resourceConnection, 'assetFactory' => $assetFactory, - 'logger' => $logger, + 'logger' => $this->logger, ] ); $this->adapter = $this->createMock(AdapterInterface::class); @@ -83,6 +88,9 @@ public function testExceptionDuringGetMediaAssetData(): void $this->adapter->method('query')->willThrowException(new \Exception()); $this->expectException(IntegrationException::class); + $this->logger->expects($this->any()) + ->method('critical') + ->willReturnSelf(); $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php index 72e44027469cd..c300d4f121bd2 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php @@ -19,9 +19,9 @@ use Zend\Db\Adapter\Driver\Pdo\Statement; /** - * Test the GetById command model + * Test the GetById command successful scenario */ -class GetByIdTest extends \PHPUnit\Framework\TestCase +class GetByIdSuccessfulTest extends \PHPUnit\Framework\TestCase { private const MEDIA_ASSET_STUB_ID = 1; From c2209fb509134062507130d47154394ef786403d Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Wed, 13 Nov 2019 16:33:01 +0200 Subject: [PATCH 2025/2437] Remove not used const --- .../Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php index 52d1c3f82f8f3..0ca9b3a3ffc8a 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -25,8 +25,6 @@ class GetByIdExceptionNoSuchEntityTest extends \PHPUnit\Framework\TestCase { private const MEDIA_ASSET_STUB_ID = 1; - private const MEDIA_ASSET_DATA = ['id' => 1]; - /** * @var GetById|MockObject */ From b180e8a8f11797db929b3f0cbfdf32f4a951c985 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 13 Nov 2019 17:23:10 +0200 Subject: [PATCH 2026/2437] Making system configs dependent by Active field --- app/code/Magento/Signifyd/etc/adminhtml/system.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml index 2dd75d2d91e5b..bd799d032184a 100644 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ b/app/code/Magento/Signifyd/etc/adminhtml/system.xml @@ -41,22 +41,34 @@ <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console]]></comment> <config_path>fraud_protection/signifyd/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="active">1</field> + </depends> </field> <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>API URL</label> <config_path>fraud_protection/signifyd/api_url</config_path> <comment>Don’t change unless asked to do so.</comment> + <depends> + <field id="active">1</field> + </depends> </field> <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/debug</config_path> + <depends> + <field id="active">1</field> + </depends> </field> <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook URL</label> <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> <attribute type="handler_url">signifyd/webhooks/handler</attribute> <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Field\WebhookUrl</frontend_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> </group> From 9ea1d3fcce6043a660ffbb2157a0d8a137ed1965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= <pawel.tylek@creativestyle.pl> Date: Wed, 13 Nov 2019 16:26:50 +0100 Subject: [PATCH 2027/2437] M2C-21765 Output N/A if there is no value --- .../Catalog/view/frontend/templates/product/compare/list.phtml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml index 55772388d44bf..f80788d2af510 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml @@ -130,6 +130,8 @@ default :?> <?php if (is_string($block->getProductAttributeValue($item, $attribute))) :?> <?= /* @noEscape */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> + <?php else: ?> + <?= $block->escapeHtml($helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode())) ?> <?php endif; ?> <?php break; } ?> From da4f4320c44e664f30089bd5cfef30803cc6c7a6 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@adobe.com> Date: Wed, 13 Nov 2019 09:27:54 -0600 Subject: [PATCH 2028/2437] MQE-1889: [MTF-To-MFTF] Process PR #717 - Reverted BIC actiongroup change - Changed actionGroup to extend existing - Moved test materials from incorrect modules back into correct module - added cleanup steps --- .../AdminCreateUserActionGroup.xml | 26 ++------------- ...eateUserWithRoleAndIsActiveActionGroup.xml | 14 ++++++++ .../Magento/User/Test/Mftf/Data/UserData.xml | 32 +++---------------- .../User/Test/Mftf/Data/UserRoleData.xml | 4 +++ .../Mftf/Section/AdminDeleteUserSection.xml | 17 ++++++++++ .../Mftf/Section/AdminNewUserFormSection.xml | 2 +- .../Test/Mftf/Section/AdminStoreSection.xml | 13 ++++++++ .../Test/AdminCreateActiveUserEntityTest.xml | 7 ++++ .../AdminCreateInactiveUserEntityTest.xml | 7 ++++ 9 files changed, 69 insertions(+), 53 deletions(-) create mode 100644 app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml create mode 100644 app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml index bcc7ec78e87a5..d550d855fcdd0 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml @@ -36,6 +36,7 @@ <!--Create new user with role--> <actionGroup name="AdminCreateUserWithRoleActionGroup"> <arguments> + <argument name="role"/> <argument name="user" defaultValue="newAdmin"/> </arguments> <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> @@ -49,32 +50,9 @@ <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> <scrollToTopOfPage stepKey="scrollToTopOfPage" /> <click stepKey="clickUserRole" selector="{{AdminCreateUserSection.userRoleTab}}"/> - <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(user.role)}}" stepKey="assignRole"/> + <click stepKey="chooseRole" selector="{{AdminStoreSection.createdRoleInUserPage(role.name)}}"/> <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser" /> <waitForPageLoad stepKey="waitForSaveTheUser" /> <see userInput="You saved the user." stepKey="seeSuccessMessage" /> </actionGroup> - - <!--Create new user with role and active/inactive setting--> - <actionGroup name="AdminCreateUserWithRoleAndIsActiveActionGroup"> - <arguments> - <argument name="user" defaultValue="newAdmin"/> - </arguments> - <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> - <waitForPageLoad stepKey="waitForUsersPage" /> - <fillField selector="{{AdminNewUserFormSection.username}}" userInput="{{user.username}}" stepKey="enterUserName" /> - <fillField selector="{{AdminNewUserFormSection.firstname}}" userInput="{{user.firstName}}" stepKey="enterFirstName" /> - <fillField selector="{{AdminNewUserFormSection.lastname}}" userInput="{{user.lastName}}" stepKey="enterLastName" /> - <fillField selector="{{AdminNewUserFormSection.email}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail" /> - <fillField selector="{{AdminNewUserFormSection.password}}" userInput="{{user.password}}" stepKey="enterPassword" /> - <fillField selector="{{AdminNewUserFormSection.passwordConfirmation}}" userInput="{{user.password}}" stepKey="confirmPassword" /> - <checkOption selector="{{AdminNewUserFormSection.userIsActive(user.is_active)}}" stepKey="checkIsActive" /> - <fillField selector="{{AdminNewUserFormSection.currentPassword}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword" /> - <scrollToTopOfPage stepKey="scrollToTopOfPage" /> - <click stepKey="clickUserRole" selector="{{AdminNewUserFormSection.userRoleTab}}"/> - <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(user.role)}}" stepKey="assignRole"/> - <click selector="{{AdminNewUserFormSection.save}}" stepKey="clickSaveUser" /> - <waitForPageLoad stepKey="waitForSaveTheUser" /> - <see userInput="You saved the user." stepKey="seeSuccessMessage" /> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml new file mode 100644 index 0000000000000..779dcbc512fd2 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Create new user with role and active setting--> + <actionGroup name="AdminCreateUserWithRoleAndIsActiveActionGroup" extends="AdminCreateUserWithRoleActionGroup"> + <checkOption selector="{{AdminNewUserFormSection.userIsActive(user.is_active)}}" stepKey="checkIsActive" after="confirmPassword"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index 636dd877bb639..bb31cc412f3a5 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -74,36 +74,12 @@ <item>1</item> </array> </entity> - <entity name="activeAdmin" type="user"> - <data key="username" unique="suffix">AdminUser</data> - <data key="firstname" unique="suffix">FirstName</data> - <data key="lastname" unique="suffix">LastName</data> - <data key="email" unique="prefix">admin@example.com</data> - <data key="password">123123q</data> - <data key="password_confirmation">123123q</data> - <data key="interface_local">en_US</data> - <data key="interface_local_label">English (United States)</data> + <entity name="activeAdmin" type="user" extends="NewAdminUser"> + <data key="name" unique="suffix">admin</data> <data key="is_active">1</data> - <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> - <data key="role">Administrators</data> - <array key="roles"> - <item>1</item> - </array> </entity> - <entity name="inactiveAdmin" type="user"> - <data key="username" unique="suffix">AdminUser</data> - <data key="firstname" unique="suffix">FirstName</data> - <data key="lastname" unique="suffix">LastName</data> - <data key="email" unique="prefix">admin@example.com</data> - <data key="password">123123q</data> - <data key="password_confirmation">123123q</data> - <data key="interface_local">en_US</data> - <data key="interface_local_label">English (United States)</data> + <entity name="inactiveAdmin" type="user" extends="NewAdminUser"> + <data key="name" unique="suffix">admin</data> <data key="is_active">0</data> - <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> - <data key="role">Administrators</data> - <array key="roles"> - <item>1</item> - </array> </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 1c18ca13b9731..f58cab7256b14 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -24,4 +24,8 @@ <data key="resourceAccess">Custom</data> <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> + + <entity name="genericAdminRole" type="role"> + <data key="name">Administrators</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml new file mode 100644 index 0000000000000..21ca1cb36f988 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminDeleteUserSection"> + <element name="role" parameterized="true" selector="//td[contains(text(), '{{roleName}}')]" type="button"/> + <element name="password" selector="#user_current_password" type="input"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete User')]" type="button"/> + <element name="confirm" selector=".action-primary.action-accept" type="button"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml index 79195067315db..8774261ea739d 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -19,7 +19,7 @@ <element name="password" type="input" selector="#page_tabs_main_section_content input[name='password']"/> <element name="passwordConfirmation" type="input" selector="#page_tabs_main_section_content input[name='password_confirmation']"/> <element name="interfaceLocale" type="select" selector="#page_tabs_main_section_content select[name='interface_locale']"/> - <element name="userIsActive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='{{var}}']" parameterized="true"/> + <element name="userIsActive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='{{isActive}}']" parameterized="true"/> <element name="currentPassword" type="input" selector="#page_tabs_main_section_content input[name='current_password']"/> <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml new file mode 100644 index 0000000000000..9e47351e68701 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminStoreSection"> + <element name="createdRoleInUserPage" type="text" selector="//tr//td[contains(text(), '{{roleName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml index 1cc2294a3d33e..753ab02f84053 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -22,8 +22,15 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> + <after> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="activeAdmin"/> + </actionGroup> + </after> <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="role" value="genericAdminRole"/> <argument name="user" value="activeAdmin"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml index 0768f3c6e240e..d0fdd72ebbdcc 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -22,8 +22,15 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> + <after> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + </after> <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="role" value="genericAdminRole"/> <argument name="user" value="inactiveAdmin"/> </actionGroup> <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> From bfb6c8f387399433066749133854f48da82be013 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Wed, 13 Nov 2019 10:39:45 -0600 Subject: [PATCH 2029/2437] MC-22947: Hotfix integration test to stabilize cicd results --- .../Controller/Adminhtml/Product/Action/AttributeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index 3ec8c806dcbb1..e3a47c2f23cc6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -124,7 +124,7 @@ public function testSaveActionChangeVisibility($attributes) $this->publisherConsumerController->waitForAsynchronousResult( function () use ($repository) { - sleep(3); + sleep(10); // Should be refactored in the scope of MC-22947 return $repository->get( 'simple', false, From ced306cf4ab1723481bf1a55ec1472beef831f6e Mon Sep 17 00:00:00 2001 From: Stanislav Idolov <sidolov@magento.com> Date: Wed, 13 Nov 2019 11:59:29 -0600 Subject: [PATCH 2030/2437] magento/magento2ce#5017: Fixed code style issue --- .../Adminhtml/Product/Action/AttributeTest.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index e3a47c2f23cc6..2f35a5fdafc3a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -22,12 +22,15 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr protected function setUp() { - $this->publisherConsumerController = Bootstrap::getObjectManager()->create(PublisherConsumerController::class, [ - 'consumers' => $this->consumers, - 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", - 'maxMessages' => null, - 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() - ]); + $this->publisherConsumerController = Bootstrap::getObjectManager()->create( + PublisherConsumerController::class, + [ + 'consumers' => $this->consumers, + 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", + 'maxMessages' => null, + 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() + ] + ); try { $this->publisherConsumerController->startConsumers(); From 5fea47b97507754a1fa95052aeb8c1ef2b61d501 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 13 Nov 2019 18:17:37 +0000 Subject: [PATCH 2031/2437] magento/magento2#25553: Fixed masonry grid rendering and styles --- .../base/web/js/grid/columns/image-preview.js | 25 ++++ .../Ui/view/base/web/js/grid/columns/image.js | 1 + .../view/base/web/js/grid/columns/overlay.js | 4 + .../Ui/view/base/web/js/grid/masonry.js | 2 +- .../Magento_Ui/web/css/source/_module.less | 1 + .../web/css/source/module/_masonry-grid.less | 108 ++++++++++++++++++ 6 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 1ef2ebf6594fa..4fba02117666f 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -10,11 +10,16 @@ define([ return Column.extend({ defaults: { + bodyTmpl: 'ui/grid/columns/image-preview', previewImageSelector: '[data-image-preview]', visibleRecord: null, height: 0, displayedRecord: {}, lastOpenedImage: null, + fields: { + previewUrl: 'preview_url', + title: 'title' + }, modules: { masonry: '${ $.parentName }', thumbnailComponent: '${ $.parentName }.thumbnail_url' @@ -154,6 +159,26 @@ define([ return this.visibleRecord() === record._rowIndex || false; }, + /** + * Returns preview image url for a given record. + * + * @param {Object} record + * @return {*|bool} + */ + getUrl: function (record) { + return record[this.fields.previewUrl]; + }, + + /** + * Returns image title for a given record. + * + * @param {Object} record + * @return {*|bool} + */ + getTitle: function (record) { + return record[this.fields.title]; + }, + /** * Get styles for preview * diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js index d2a417db683ca..cec0955b835e0 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js @@ -9,6 +9,7 @@ define([ return Column.extend({ defaults: { + bodyTmpl: 'ui/grid/columns/image', modules: { previewComponent: '${ $.parentName }.preview' }, diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js index 1a823b8db019e..420b318e0b440 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js @@ -8,6 +8,10 @@ define([ 'use strict'; return Column.extend({ + defaults: { + bodyTmpl: 'ui/grid/columns/overlay' + }, + /** * If overlay should be visible * diff --git a/app/code/Magento/Ui/view/base/web/js/grid/masonry.js b/app/code/Magento/Ui/view/base/web/js/grid/masonry.js index 4d171e394af0a..e4c72ee950c26 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/masonry.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/masonry.js @@ -13,7 +13,7 @@ define([ return Listing.extend({ defaults: { - template: 'Magento_Ui/grid/masonry', + template: 'ui/grid/masonry', imports: { rows: '${ $.provider }:data.items', errorMessage: '${ $.provider }:data.errorMessage' diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less index e05e81d737f14..9255accff6950 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less @@ -4,3 +4,4 @@ // */ @import 'module/_data-grid.less'; +@import 'module/_masonry-grid.less'; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less new file mode 100644 index 0000000000000..9afdd76d03b86 --- /dev/null +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -0,0 +1,108 @@ +// /** +// * Copyright © Magento, Inc. All rights reserved. +// * See COPYING.txt for license details. +// */ +@admin__masonry_grid_image__space: 20px; +@admin__masonry_grid_background_color: #fff; +@admin__masonry_overlay_background_color: #507dc8; + +& when (@media-common = true) { + .masonry-image { + &-grid { + margin: @admin__masonry_grid_image__space/2 -(@admin__masonry_grid_image__space/2); + overflow: hidden; + position: relative; + + .no-data-message-container, + .error-message-container { + font-size: @data-grid__no-records__font-size; + padding: @data-grid__no-records__padding; + text-align: center; + } + } + + &-column { + background-color: @admin__masonry_grid_background_color; + float: left; + margin: @admin__masonry_grid_image__space/2; + overflow: hidden; + + img { + cursor: pointer; + height: 100%; + width: 100%; + } + } + + &-overlay { + position: absolute; + color: #fff; + z-index: 10; + padding: .5rem; + opacity: 1; + width: 80px; + background-color: @admin__masonry_overlay_background_color; + text-align: center; + } + + &-preview { + background-color: @admin__masonry_grid_background_color; + display: table; + left: 0; + position: absolute; + right: 0; + width: 100%; + + .container { + margin: auto; + max-width: 880px; + padding-top: 10px; + + .action-buttons { + text-align: right; + + .action { + &-close { + padding: 30px; + position: static; + } + + &-previous, + &-next { + background: transparent; + border: none; + margin: 0; + white-space: nowrap; + } + + &-close, + &-previous, + &-next { + font-size: 2rem; + } + } + } + + .preview-row-content { + display: flex; + + &:after { + display: table; + clear: both; + content: ''; + } + + img.preview { + display: block; + flex-basis: 300px; + float: left; + margin-bottom: 20px; + max-height: 500px; + max-width: 60%; + width: auto; + } + } + } + } + } +} From cdf8e4ebd41ffab8bc44940886224a0edf07e060 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 13 Nov 2019 18:37:10 +0000 Subject: [PATCH 2032/2437] magento/magento2#25553: Fixed static tests --- .../web/css/source/module/_masonry-grid.less | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less index 9afdd76d03b86..c17c19e259478 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -35,14 +35,14 @@ } &-overlay { - position: absolute; - color: #fff; - z-index: 10; - padding: .5rem; - opacity: 1; - width: 80px; background-color: @admin__masonry_overlay_background_color; + color: @admin__masonry_grid_background_color; + opacity: 1; + padding: .5rem; + position: absolute; text-align: center; + width: 80px; + z-index: 10; } &-preview { @@ -87,9 +87,9 @@ display: flex; &:after { - display: table; clear: both; content: ''; + display: table; } img.preview { From 4ce646dc58710b38655aa3017ae6cf28fdb8f3a0 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 13 Nov 2019 18:39:11 +0000 Subject: [PATCH 2033/2437] magento/magento2#25553: Fixed return types annotation --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 4fba02117666f..4f632df025ac8 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -163,7 +163,7 @@ define([ * Returns preview image url for a given record. * * @param {Object} record - * @return {*|bool} + * @return {String} */ getUrl: function (record) { return record[this.fields.previewUrl]; @@ -173,7 +173,7 @@ define([ * Returns image title for a given record. * * @param {Object} record - * @return {*|bool} + * @return {String} */ getTitle: function (record) { return record[this.fields.title]; From baec752b9be85c3fd6838880235872aa8f6f9c47 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Wed, 13 Nov 2019 13:21:07 -0600 Subject: [PATCH 2034/2437] Removed redundant check --- .../Model/Resolver/DeleteCustomerAddress.php | 4 ---- .../Customer/DeleteCustomerAddressTest.php | 22 ------------------- 2 files changed, 26 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php index da588b98b5dbc..c26b3710ef561 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php @@ -58,10 +58,6 @@ public function resolve( throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - if (empty($args['id'])) { - throw new GraphQlInputException(__('Address "id" value should be specified')); - } - $address = $this->getCustomerAddress->execute((int)$args['id'], $context->getUserId()); $this->deleteCustomerAddress->execute($address); return true; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php index e61333509ccbb..443b9d7ec53e5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php @@ -224,28 +224,6 @@ public function testDeleteAnotherCustomerAddress() $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php - * - * @expectedException Exception - * @expectedExceptionMessage Address "id" value should be specified - */ - public function testDeleteCustomerAddressWithZeroAddressEntityId() - { - $userName = 'customer@example.com'; - $password = 'password'; - $addressId = 0; - - $mutation - = <<<MUTATION -mutation { - deleteCustomerAddress(id: {$addressId}) -} -MUTATION; - $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php From 24dbfba49de3328c1c073bd65e7a25d8d6494bca Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Wed, 13 Nov 2019 14:41:52 -0600 Subject: [PATCH 2035/2437] magento/graphql-ce#1051: Add missed annotation blocks to Magento\GraphQl\Customer\CreateCustomerTest --- .../testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index 49b56c784e467..a6455a9728fec 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -114,7 +114,7 @@ public function testCreateCustomerAccountWithoutPassword() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage "input" value should be specified */ public function testCreateCustomerIfInputDataIsEmpty() { @@ -140,7 +140,7 @@ public function testCreateCustomerIfInputDataIsEmpty() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage Required parameters are missing: Email */ public function testCreateCustomerIfEmailMissed() { From 4f747b8033717c7d57b0c3a9ee2935a0a3ca2dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Wed, 13 Nov 2019 22:02:14 +0100 Subject: [PATCH 2036/2437] Add Poland regions --- .../Setup/Patch/Data/AddDataForPoland.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php new file mode 100644 index 0000000000000..c5ab4cd186286 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See PLPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Directory\Setup\DataInstallerFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add Poland States + */ +class AddDataForPoland implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForPoland() + ); + } + + /** + * Poland states data. + * + * @return array + */ + private function getDataForPoland() + { + return [ + ['PL', 'PL-02', 'dolnośląskie'], + ['PL', 'PL-04', 'kujawsko-pomorskie'], + ['PL', 'PL-06', 'lubelskie'], + ['PL', 'PL-08', 'lubuskie'], + ['PL', 'PL-10', 'łódzkie'], + ['PL', 'PL-12', 'małopolskie'], + ['PL', 'PL-14', 'mazowieckie'], + ['PL', 'PL-16', 'opolskie'], + ['PL', 'PL-18', 'podkarpackie'], + ['PL', 'PL-20', 'podlaskie'], + ['PL', 'PL-22', 'pomorskie'], + ['PL', 'PL-24', 'śląskie'], + ['PL', 'PL-26', 'świętokrzyskie'], + ['PL', 'PL-28', 'warmińsko-mazurskie'], + ['PL', 'PL-30', 'wielkopolskie'], + ['PL', 'PL-32', 'zachodniopomorskie'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} From 544cb02dd74de996bbcc933c4f9e7d73a1b5c4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Wed, 13 Nov 2019 23:29:36 +0100 Subject: [PATCH 2037/2437] Functional tests fix --- app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml | 2 +- .../Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 74365ef02fe87..08e13885d10d4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -292,7 +292,7 @@ <item>Piwowarska 6</item> </array> <data key="city">Bielsko-Biała</data> - <data key="state"> Bielsko</data> + <data key="state">śląskie</data> <data key="country_id">PL</data> <data key="country">Poland</data> <data key="postcode">43-310</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml index cbc8b89d3f242..d2435a093046a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml @@ -34,7 +34,7 @@ <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickOnEditButton"/> <waitForPageLoad stepKey="waitForCustomerEditPageToLoad"/> - <!--Add the Address --> + <!-- Add the Address --> <click selector="{{AdminEditCustomerAddressesSection.addresses}}" stepKey="selectAddress"/> <waitForPageLoad stepKey="waitForAddressPageToLoad"/> <click selector="{{AdminEditCustomerAddressesSection.addNewAddress}}" stepKey="ClickOnAddNewAddressButton"/> @@ -44,6 +44,7 @@ <fillField selector="{{AdminEditCustomerAddressesSection.city}}" userInput="{{PolandAddress.city}}" stepKey="fillCity"/> <scrollTo selector="{{AdminEditCustomerAddressesSection.phone}}" x="0" y="-80" stepKey="scrollToPhone"/> <selectOption selector="{{AdminEditCustomerAddressesSection.country}}" userInput="{{PolandAddress.country}}" stepKey="fillCountry"/> + <selectOption selector="{{AdminEditCustomerAddressesSection.state}}" userInput="{{PolandAddress.state}}" stepKey="fillState"/> <fillField selector="{{AdminEditCustomerAddressesSection.zipCode}}" userInput="{{PolandAddress.postcode}}" stepKey="fillPostCode"/> <fillField selector="{{AdminEditCustomerAddressesSection.phone}}" userInput="{{PolandAddress.telephone}}" stepKey="fillPhoneNumber"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> @@ -60,6 +61,7 @@ <see userInput="$$createCustomer.firstname$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertFirstName"/> <see userInput="$$createCustomer.lastname$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertLastName"/> <see userInput="$$createCustomer.email$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertEmail"/> + <see userInput="{{PolandAddress.state}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertState"/> <see userInput="{{PolandAddress.country}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertCountry"/> <see userInput="{{PolandAddress.postcode}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertPostCode"/> <see userInput="{{PolandAddress.telephone}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertPhoneNumber"/> @@ -86,6 +88,7 @@ <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.street}}" stepKey="seeStreetAddress"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.city}}" stepKey="seeCity"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.country}}" stepKey="seeCountry"/> + <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.state}}" stepKey="seeState"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.postcode}}" stepKey="seePostCode"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.telephone}}" stepKey="seePhoneNumber"/> </test> From 3ea13a870198ad823dc72935e0ae3d40fad0035a Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Wed, 13 Nov 2019 16:55:33 -0600 Subject: [PATCH 2038/2437] magento/graphql-ce#1052: Remove redundant logic in createCustomer mutation - revert changes, since framework validates `non-nullable` but not empty input --- .../Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php index 5136b92f170c5..c690e11bd4940 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/CreateCustomer.php @@ -63,6 +63,10 @@ public function resolve( array $value = null, array $args = null ) { + if (empty($args['input']) || !is_array($args['input'])) { + throw new GraphQlInputException(__('"input" value should be specified')); + } + if (!$this->newsLetterConfig->isActive(ScopeInterface::SCOPE_STORE)) { $args['input']['is_subscribed'] = false; } From f29afc8e9f8b118bc3c8918f1018228020747b8b Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun <olga.vasyltsun@gmail.com> Date: Thu, 14 Nov 2019 08:06:51 +0200 Subject: [PATCH 2039/2437] MC-17259: Update blank gender it does not saved in direct edits from customer grid --- .../StorefrontCategoryHighlightedAndProductDisplayedTest.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml index ba30c7d0e26e4..3e72df9133898 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCategoryHighlightedAndProductDisplayedTest.xml @@ -37,10 +37,6 @@ <createData entity="SimpleProduct" stepKey="product4"> <requiredEntity createDataKey="category2"/> </createData> - - <!-- TODO: REMOVE AFTER FIX MC-21717 --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="product1" stepKey="deleteProduct1"/> From 1f010f148cdce4e17baaf02235b325b79b3bb8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= <pawel.tylek@creativestyle.pl> Date: Thu, 14 Nov 2019 08:33:24 +0100 Subject: [PATCH 2040/2437] M2C-21765 Add space after else --- .../Catalog/view/frontend/templates/product/compare/list.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml index f80788d2af510..0bea3ca03dee8 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml @@ -130,7 +130,7 @@ default :?> <?php if (is_string($block->getProductAttributeValue($item, $attribute))) :?> <?= /* @noEscape */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> - <?php else: ?> + <?php else : ?> <?= $block->escapeHtml($helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode())) ?> <?php endif; ?> <?php break; From 26dca1007e2458e29419ae5ecf7b77e0c1560658 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 13 Nov 2019 21:58:49 +0200 Subject: [PATCH 2041/2437] Refactor per review comment --- lib/web/mage/adminhtml/browser.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/web/mage/adminhtml/browser.js b/lib/web/mage/adminhtml/browser.js index 09ba2573d0725..7780ac524fa49 100644 --- a/lib/web/mage/adminhtml/browser.js +++ b/lib/web/mage/adminhtml/browser.js @@ -15,7 +15,7 @@ define([ 'Magento_Ui/js/modal/modal', 'jquery/ui', 'jquery/jstree/jquery.jstree', - 'mage/mage', + 'mage/mage' ], function ($, wysiwyg, prompt, confirm, alert, _) { window.MediabrowserUtility = { windowId: 'modal_dialog_message', @@ -53,21 +53,22 @@ define([ content = '<div class="popup-window" id="' + windowId + '"></div>', self = this; - if (options - && self.targetElementId - && self.targetElementId === options.targetElementId - ) { + if (options && + self.targetElementId && + self.targetElementId === options.targetElementId) { if (typeof options.closed !== 'undefined') { this.modal.modal('option', 'closed', options.closed); } this.modal.modal('openModal'); return; - } else if (_.isUndefined(options) && self.modalLoaded === true) { - if (self.targetElementId === url) { - this.modal.modal('openModal'); - return; - } + } else if (_.isUndefined(options) && + self.modalLoaded === true && + self.targetElementId === url + ) { + this.modal.modal('openModal'); + + return; } if (this.modal) { @@ -94,10 +95,11 @@ define([ }).done(function (data) { self.modal.html(data).trigger('contentUpdated'); self.modalLoaded = true; - self.targetElementId = options - ? options.targetElementId - : url; + self.targetElementId = options ? + options.targetElementId + : url; }); + }, /** From aa7195afb7fcab4a4984d9eb703643cc9d5a6be2 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Thu, 14 Nov 2019 10:34:46 +0200 Subject: [PATCH 2042/2437] Fix static tests --- .../Ui/view/base/web/js/grid/columns/image-preview.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index cc637d8a3c4d1..1da19a3645e4a 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -100,10 +100,11 @@ define([ */ show: function (record) { var img; - - if (record._rowIndex === this.visibleRecord() - && this.isVisible(record)) { + + if (record._rowIndex === this.visibleRecord() && + this.isVisible(record)) { this.hide(); + return; } From 9f5fece4a8c330dd44e664eee8d137e162608945 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Thu, 14 Nov 2019 16:32:05 +0700 Subject: [PATCH 2043/2437] Resolve No margin between checkbox and icon when choose category in condition in Cart/Catalog Rule issue25596 --- .../adminhtml/Magento/backend/web/css/styles-old.less | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index c9ecf2e6670c1..1703e87691788 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -1827,6 +1827,10 @@ background-image: url(../images/fam_application_form_delete.png); } + .x-tree-node .x-tree-node-el input[type=checkbox] { + margin-left: 3px; + } + // // Styles for "js" tooltip with positionings // -------------------------------------- @@ -3958,15 +3962,15 @@ .grid tr.headings th > span { white-space: normal; } - + .field { &.field-subscription { .admin__field-label { margin-left: 10px; float: none; cursor: pointer; - } - + } + .admin__field-control { float: left; width: auto; From e29914e4581d5f14c6a4dcd5fdf4f4799c8df64c Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Thu, 14 Nov 2019 12:56:04 +0200 Subject: [PATCH 2044/2437] Update image-preview.js --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 1da19a3645e4a..cf597f8d3a543 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -101,8 +101,7 @@ define([ show: function (record) { var img; - if (record._rowIndex === this.visibleRecord() && - this.isVisible(record)) { + if (record._rowIndex === this.visibleRecord()) { this.hide(); return; From 49bf35df0e24c28b930b01d7611d30664055a467 Mon Sep 17 00:00:00 2001 From: Marcus Pettersen Irgens <marcus.irgens@visma.com> Date: Thu, 14 Nov 2019 12:27:31 +0100 Subject: [PATCH 2045/2437] Add exception to debug log from Frontcontroller validation failures --- lib/internal/Magento/Framework/App/FrontController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index ba23e010e3e3f..29c9452d33b32 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -141,7 +141,8 @@ private function processRequest( //Validation failed - processing validation results. $this->logger->debug( 'Request validation failed for action "' - .get_class($actionInstance) .'"' + . get_class($actionInstance) . '"', + ["exception" => $exception] ); $result = $exception->getReplaceResult(); if ($messages = $exception->getMessages()) { From 339cc2654a91b246aaf004828b9e875557969a78 Mon Sep 17 00:00:00 2001 From: Edward Simpson <edward@skywire.co.uk> Date: Thu, 14 Nov 2019 13:20:57 +0000 Subject: [PATCH 2046/2437] Moving Ui message.js hide speed and timeout into variables for easier overriding --- app/code/Magento/Ui/view/frontend/web/js/view/messages.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js index b2fb3f216199b..735c3219c8136 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js @@ -20,6 +20,8 @@ define([ template: 'Magento_Ui/messages', selector: '[data-role=checkout-messages]', isHidden: false, + hideTimeout: 5000, + hideSpeed: 500, listens: { isHidden: 'onHiddenChange' } @@ -68,8 +70,8 @@ define([ // Hide message block if needed if (isHidden) { setTimeout(function () { - $(self.selector).hide('blind', {}, 500); - }, 5000); + $(self.selector).hide('blind', {}, self.hideSpeed); + }, self.hideTimeout); } } }); From d26f8dad182b6f2cb8608758989fff3edb53a09f Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Thu, 14 Nov 2019 15:58:34 +0200 Subject: [PATCH 2047/2437] Fix JS error on cart configure page for configurable products --- app/code/Magento/Msrp/view/base/web/js/msrp.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Msrp/view/base/web/js/msrp.js b/app/code/Magento/Msrp/view/base/web/js/msrp.js index 2789491137bc1..65af87d85de51 100644 --- a/app/code/Magento/Msrp/view/base/web/js/msrp.js +++ b/app/code/Magento/Msrp/view/base/web/js/msrp.js @@ -352,8 +352,11 @@ define([ $(this.options.mapInfoLinks).show(); if (useDefaultPrice || !this.wasOpened) { - this.$popup.find(this.options.msrpLabelId).html(options.msrpPrice); - this.$popup.find(this.options.priceLabelId).html(options.realPrice); + if (this.$popup) { + this.$popup.find(this.options.msrpLabelId).html(options.msrpPrice); + this.$popup.find(this.options.priceLabelId).html(options.realPrice); + } + $(this.options.displayPriceElement).html(msrpPrice); this.wasOpened = true; } From f661205a5c5d27e519ad57219cdc1f8d12afe3de Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 14 Nov 2019 16:57:36 +0200 Subject: [PATCH 2048/2437] MC-22732: Configurable products dont show the 2nd attribute options --- ...igurableProductVariationQtyActionGroup.xml | 21 ++++ ...nConfigurableAttributesGridActionGroup.xml | 25 +++++ ...nConfigurableAttributesGridActionGroup.xml | 17 +++ ...reateProductConfigurationsPanelSection.xml | 2 + ...orefrontConfigurableProductDetailsTest.xml | 104 ++++++++++++++++++ .../view/frontend/web/js/configurable.js | 4 + 6 files changed, 173 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml new file mode 100644 index 0000000000000..3441b979226dd --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminChangeConfigurableProductVariationQty"> + <annotations> + <description>Change quantity value for configurable product generated variation</description> + </annotations> + <arguments> + <argument name="rowIndex" type="string" defaultValue="0"/> + <argument name="quantity" type="string" defaultValue="0"/> + </arguments> + <fillField selector="{{AdminCreateProductConfigurationsPanel.variationQty(rowIndex)}}" userInput="{{quantity}}" stepKey="fillVariationQuantity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml new file mode 100644 index 0000000000000..56a70a0ba29f9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterAttributeInConfigurableAttributesGrid"> + <annotations> + <description>Filter attribute in configurable attributes grid by attribute code value</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="{{newProductAttribute.attribute_code}}"/> + </arguments> + <conditionalClick selector="{{AdminDataGridFilterSection.clear}}" visible="true" dependentSelector="{{AdminDataGridFilterSection.clear}}" stepKey="clearFilters"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="waitForFiltersAppear"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="expandFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{attributeCode}}" stepKey="fillFilterValue"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml new file mode 100644 index 0000000000000..72ab16df87dac --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectAttributeInConfigurableAttributesGrid" extends="AdminFilterAttributeInConfigurableAttributesGrid"> + <annotations> + <description>EXTENDS: AdminFilterAttributeInConfigurableAttributesGrid. Select first filtered attribute.</description> + </annotations> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="checkAttributeInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index 34feeb3b5bf3e..92e2450ef4f3d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -17,6 +17,8 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="firstCheckbox" type="input" selector="tr[data-repeat-index='0'] .admin__control-checkbox"/> <element name="id" type="text" selector="//tr[contains(@data-repeat-index, '0')]/td[2]/div"/> + <element name="variationsGrid" type="block" selector=".admin__field[data-index='configurable-matrix']"/> + <element name="variationQty" type="input" selector=".admin__field[data-index='configurable-matrix'] input[name='configurable-matrix[{{rowIndex}}][qty]']" parameterized="true"/> <element name="attributeCheckbox" type="checkbox" selector="//div[contains(text(), '{{arg}}')]/ancestor::tr//input[@data-action='select-row']" parameterized="true"/> <element name="defaultLabel" type="text" selector="//div[contains(text(), '{{arg}}')]/ancestor::tr//td[3]/div[@class='data-grid-cell-content']" parameterized="true"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml index 48014be8ac092..2f5ee036b1420 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml @@ -178,4 +178,108 @@ <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> </test> + + <test name="StorefrontConfigurableProductVariationsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Configurable Product"/> + <title value="Customer should get the right options list"/> + <description value="Customer should get the right options list for each variation of configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-23027"/> + <useCaseId value="MC-22732"/> + <group value="configurable_product"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Add first attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <!-- Add second attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> + <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="goToCreateProductPage" stepKey="navigateToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="SetCategoryByName" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyByString" stepKey="fillUrlKey"> + <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> + </actionGroup> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> + <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> + </actionGroup> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> + <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> + </actionGroup> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForPageLoad stepKey="waitForStepLoad"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> + + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> + <argument name="rowIndex" value="0"/> + <argument name="quantity" value="0"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> + <argument name="rowIndex" value="1"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> + <argument name="rowIndex" value="2"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> + <argument name="rowIndex" value="3"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="saveConfigurableProduct" stepKey="saveConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> + <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> + <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> + <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> + </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index ae564610e4b0b..faacbd03f20b0 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -443,6 +443,10 @@ define([ } for (i = 0; i < options.length; i++) { + if (prevConfig && typeof allowedProductsByOption[i] === 'undefined') { + continue; //jscs:ignore disallowKeywords + } + allowedProducts = prevConfig ? allowedProductsByOption[i] : options[i].products.slice(0); optionPriceDiff = 0; From e287963e0444f009098c7dcd1206f49d2b52cb1a Mon Sep 17 00:00:00 2001 From: Kate Kyzyma <kate@atwix.com> Date: Thu, 14 Nov 2019 17:58:46 +0200 Subject: [PATCH 2049/2437] Code refactoring --- ...eateNewUrlRewriteForCmsPageActionGroup.xml | 23 +++++ ...llNewCmsPageUrlRewriteFormActionGroup.xml} | 14 +-- ...minGoToAddNewUrlRewritePageActionGroup.xml | 19 ++++ ...ageFromGridForNewUrlRewriteActionGroup.xml | 22 +++++ ...AdminDeleteCmsPageUrlRewriteEntityTest.xml | 99 ------------------- ...teCmsPageUrlRewriteWithNoRedirectsTest.xml | 59 +++++++++++ ...ageUrlRewriteWithPermanentRedirectTest.xml | 55 +++++++++++ ...ageUrlRewriteWithTemporaryRedirectTest.xml | 55 +++++++++++ 8 files changed, 235 insertions(+), 111 deletions(-) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml rename app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/{AdminAddUrlRewriteForCmsPageActionGroup.xml => AdminFillNewCmsPageUrlRewriteFormActionGroup.xml} (56%) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml delete mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml new file mode 100644 index 0000000000000..143198e4a1faa --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateNewUrlRewriteForCmsPageActionGroup"> + <annotations> + <description>Select "For Csm Page" URL Rewrite type</description> + </annotations> + <arguments> + <argument name="customUrlRewriteValue" type="string"/> + </arguments> + + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustomUrlRewrite"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCsmPage"/> + <waitForPageLoad stepKey="waitForCategoryEditSectionToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml similarity index 56% rename from app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml rename to app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml index f46bd7f5e0386..0c540d0698dd8 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForCmsPageActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml @@ -8,26 +8,17 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAddUrlRewriteForCmsPageActionGroup"> + <actionGroup name="AdminFillNewCmsPageUrlRewriteFormActionGroup"> <annotations> - <description>Goes to the Admin Add URL Rewrite edit page. Fills in the provided URL details. Clicks on Save. Validates that the Success Message is present.</description> + <description>Fills in the provided URL details. Clicks on Save.</description> </annotations> <arguments> - <argument name="cmsPageUrlKey" type="string"/> - <argument name="customUrlRewriteValue" type="string"/> <argument name="storeValue" type="string"/> <argument name="requestPath" type="string"/> <argument name="redirectTypeValue" type="string"/> <argument name="description" type="string"/> </arguments> - <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> - <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> - <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustomUrlRewrite"/> - <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCsmPage"/> - <waitForPageLoad stepKey="waitForCategoryEditSectionToLoad"/> - <click selector="{{AdminUrlRewriteEditSection.cmsPage('cmsPageUrlKey')}}" stepKey="selectCmsPage"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> <click selector="{{AdminUrlRewriteEditSection.store}}" stepKey="clickOnStore"/> <click selector="{{AdminUrlRewriteEditSection.storeValue('storeValue')}}" stepKey="clickOnStoreValue"/> <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> @@ -35,6 +26,5 @@ <click selector="{{AdminUrlRewriteEditSection.redirectTypeValue('redirectTypeValue')}}" stepKey="clickOnRedirectTypeValue"/> <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml new file mode 100644 index 0000000000000..e0e8df47852d6 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGoToAddNewUrlRewritePageActionGroup"> + <annotations> + <description>Goes to the Admin Add URL Rewrite edit page</description> + </annotations> + + <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..2713e3937469b --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup"> + <annotations> + <description>Select Cms Page for URL Rewrite from the grid</description> + </annotations> + <arguments> + <argument name="cmsPageUrlKey" type="string"/> + </arguments> + + <click selector="{{AdminUrlRewriteEditSection.cmsPage('cmsPageUrlKey')}}" stepKey="selectCmsPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml deleted file mode 100644 index 705786afa83bb..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteEntityTest.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminDeleteCmsPageUrlRewriteEntityTest"> - <annotations> - <stories value="Delete CMS Page URL rewrite"/> - <title value="Delete CMS Page URL rewrite"/> - <description value="Log in to admin and delete CMS Page URL rewrite"/> - <group value="cMSContent"/> - <group value="mtf_migrated"/> - </annotations> - - <before> - <createData entity="simpleCmsPage" stepKey="createCMSPage"/> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - </before> - <after> - <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!-- Create URL Rewrite for CMS Page with No redirects --> - <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewrite"> - <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> - <argument name="customUrlRewriteValue" value="For CMS page'"/> - <argument name="storeValue" value="Default Store View"/> - <argument name="requestPath" value="newrequestpath"/> - <argument name="redirectTypeValue" value="No"/> - <argument name="description" value="cms_default_no_redirect"/> - </actionGroup> - - <!-- Create URL Rewrite for CMS Page with temporary redirect --> - <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewriteTemporary"> - <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> - <argument name="customUrlRewriteValue" value="For CMS page'"/> - <argument name="storeValue" value="Default Store View"/> - <argument name="requestPath" value="temporaryrequestpath.html"/> - <argument name="redirectTypeValue" value="Temporary (302)"/> - <argument name="description" value="cms_default_temporary_redirect"/> - </actionGroup> - - <!-- Create URL Rewrite for CMS Page with permanent redirect --> - <actionGroup ref="AdminAddUrlRewriteForCmsPageActionGroup" stepKey="addUrlRewritePermanent"> - <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> - <argument name="customUrlRewriteValue" value="For CMS page'"/> - <argument name="storeValue" value="Default Store View"/> - <argument name="requestPath" value="permanentrequestpath.html"/> - <argument name="redirectTypeValue" value="Permanent (301)"/> - <argument name="description" value="cms_default_permanent_redirect"/> - </actionGroup> - - <!--Delete the URL Rewrite for CMS Page with No redirects--> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> - <argument name="requestPath" value="newrequestpath"/> - </actionGroup> - <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> - <argument name="message" value="You deleted the URL rewrite."/> - </actionGroup> - <!--Search and verify AssertUrlRewriteNotInGrid--> - <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedUrlRewriteInGrid"> - <argument name="requestPath" value="newrequestpath"/> - </actionGroup> - <!--Verify AssertPageByUrlRewriteIsNotFound--> - <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> - <argument name="requestPath" value="newrequestpath"/> - </actionGroup> - - <!--Delete the URL Rewrite for CMS Page with with temporary redirect--> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteTemporaryUrlRewrite"> - <argument name="requestPath" value="temporaryrequestpath.html"/> - </actionGroup> - <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> - <argument name="message" value="You deleted the URL rewrite."/> - </actionGroup> - <!--Verify AssertPageByUrlRewriteIsNotFound--> - <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFoundSecondTime"> - <argument name="requestPath" value="temporaryrequestpath.html"/> - </actionGroup> - - <!--Delete the URL Rewrite for CMS Page with permanent redirect--> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deletePermanentUrlRewrite"> - <argument name="requestPath" value="permanentrequestpath.html"/> - </actionGroup> - <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageThirdTime"> - <argument name="message" value="You deleted the URL rewrite."/> - </actionGroup> - <!--Verify AssertPageByUrlRewriteIsNotFound--> - <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFoundThirdTime"> - <argument name="requestPath" value="permanentrequestpath.html"/> - </actionGroup> - - </test> -</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml new file mode 100644 index 0000000000000..4c3bd8179578c --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with No Redirects"/> + <title value="Delete CMS Page URL rewrite with No Redirects"/> + <description value="Log in to admin and delete CMS Page URL rewrite with No Redirects"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="newrequestpath"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="cms_default_no_redirect"/> + </actionGroup> + + <!--Delete the URL Rewrite for CMS Page with No redirects--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml new file mode 100644 index 0000000000000..89f663b54e017 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with Permanent Redirect"/> + <title value="Delete CMS Page URL rewrite with Permanent Redirect"/> + <description value="Log in to admin and delete CMS Page URL rewrite with Permanent Redirect"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="permanentrequestpath.html"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="cms_default_permanent_redirect"/> + </actionGroup> + + <!-- Delete the URL Rewrite for CMS Page with permanent redirect--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deletePermanentUrlRewrite"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!-- Verify AssertPageByUrlRewriteIsNotFound --> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..ad7aec335b7a3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with Temporary Redirect"/> + <title value="Delete CMS Page URL rewrite with Temporary Redirect"/> + <description value="Log in to admin and delete CMS Page URL rewrite with Temporary Redirect"/> + <group value="cMSContent"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="temporaryrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="cms_default_temporary_redirect"/> + </actionGroup> + + <!-- Delete the URL Rewrite for CMS Page with with temporary redirect--> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteTemporaryUrlRewrite"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + + </test> +</tests> From cdaeaa4dbb4efc2bcb280b883bd3e97daab5b2bd Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Thu, 14 Nov 2019 10:26:56 -0600 Subject: [PATCH 2050/2437] MC-19642: Change single pipes to double pipes in composer.json files - Added double pipes --- app/code/Magento/Elasticsearch/composer.json | 2 +- app/code/Magento/Elasticsearch6/composer.json | 2 +- composer.json | 10 +++++----- .../Composer/_files/testFromClone/composer.json | 2 +- lib/internal/Magento/Framework/Amqp/composer.json | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index 05d3ce94f02f9..3cf5444d86223 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "*", "magento/module-catalog-inventory": "*", "magento/framework": "*", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index 9107f5e900415..b2411f6740fae 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -9,7 +9,7 @@ "magento/module-search": "*", "magento/module-store": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/composer.json b/composer.json index 62366037bc18b..1a73e551168b0 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", "magento/composer": "~1.5.0", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", @@ -42,13 +42,13 @@ "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", - "php-amqplib/php-amqplib": "~2.7.0|~2.10.0", + "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", "phpseclib/mcrypt_compat": "1.0.8", "phpseclib/phpseclib": "2.0.*", "ramsey/uuid": "~3.8.0", - "symfony/console": "~4.1.0|~4.2.0|~4.3.0", - "symfony/event-dispatcher": "~4.1.0|~4.2.0|~4.3.0", - "symfony/process": "~4.1.0|~4.2.0|~4.3.0", + "symfony/console": "~4.1.0||~4.2.0||~4.3.0", + "symfony/event-dispatcher": "~4.1.0||~4.2.0||~4.3.0", + "symfony/process": "~4.1.0||~4.2.0||~4.3.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json index 404db202c6e72..ed9965622dc40 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json @@ -7,7 +7,7 @@ "AFL-3.0" ], "require": { - "php": "~5.5.22|~5.6.0|~7.0.0", + "php": "~5.5.22||~5.6.0||~7.0.0", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", diff --git a/lib/internal/Magento/Framework/Amqp/composer.json b/lib/internal/Magento/Framework/Amqp/composer.json index 8c0a3e3070aaf..aacb6d05ca374 100644 --- a/lib/internal/Magento/Framework/Amqp/composer.json +++ b/lib/internal/Magento/Framework/Amqp/composer.json @@ -12,7 +12,7 @@ "require": { "magento/framework": "*", "php": "~7.1.3||~7.2.0||~7.3.0", - "php-amqplib/php-amqplib": "~2.7.0|~2.10.0" + "php-amqplib/php-amqplib": "~2.7.0||~2.10.0" }, "autoload": { "psr-4": { From 510310b9f98da9453b6d3bf33d51a6167f7e5926 Mon Sep 17 00:00:00 2001 From: Marcus Pettersen Irgens <marcus.irgens@visma.com> Date: Thu, 14 Nov 2019 17:55:30 +0100 Subject: [PATCH 2051/2437] Update other docs in file, move file docblock comment to class --- lib/internal/Magento/Framework/App/FrontController.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 29c9452d33b32..2e6ce65970b97 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -1,7 +1,5 @@ <?php /** - * Front controller responsible for dispatching application requests - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -17,6 +15,8 @@ use Magento\Framework\App\Request\Http as HttpRequest; /** + * Front controller responsible for dispatching application requests + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FrontController implements FrontControllerInterface @@ -116,6 +116,8 @@ public function dispatch(RequestInterface $request) } /** + * Process (validate and dispatch) the incoming request + * * @param HttpRequest $request * @param ActionInterface $actionInstance * @throws NotFoundException From a3ac3646a5fd63976d858821b7bea53d41e19f2d Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Thu, 14 Nov 2019 11:21:31 -0600 Subject: [PATCH 2052/2437] MC-23055: Frontend cookies are not set with secure flag on https - fixed --- .../Magento/PageCache/view/frontend/web/js/page-cache.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index 735fe9a6cb236..41a32ab8a49c8 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -112,11 +112,14 @@ define([ * @private */ _create: function () { - var formKey = $.mage.cookies.get('form_key'); + var formKey = $.mage.cookies.get('form_key'), + options = { + secure: window.cookiesConfig ? window.cookiesConfig.secure : false + }; if (!formKey) { formKey = generateRandomString(this.options.allowedCharacters, this.options.length); - $.mage.cookies.set('form_key', formKey); + $.mage.cookies.set('form_key', formKey, options); } $(this.options.inputSelector).val(formKey); } From 305e52da22dde7f6a9632a4a62e0d8be6af4924b Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Thu, 14 Nov 2019 11:23:20 -0600 Subject: [PATCH 2053/2437] MC-23113: [Magento cloud] - Critical error in log: strpos() expects parameter 1 to be string, array given --- .../Magento/CatalogSearch/Model/Layer/Filter/Attribute.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php index 85a32dc60b119..5b3d324179448 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -63,10 +63,11 @@ public function apply(\Magento\Framework\App\RequestInterface $request) $productCollection = $this->getLayer() ->getProductCollection(); $productCollection->addFieldToFilter($attribute->getAttributeCode(), $attributeValue); - $label = $this->getOptionText($attributeValue); - if (is_array($label)) { - $label = implode(',', $label); + $labels = []; + foreach ((array) $attributeValue as $value) { + $labels[] = $this->getOptionText($value); } + $label = implode(',', $labels); $this->getLayer() ->getState() ->addFilter($this->_createItem($label, $attributeValue)); From 617d5039effa915844fb275994c1e0521806a008 Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Thu, 14 Nov 2019 12:29:19 -0600 Subject: [PATCH 2054/2437] MC-19642: Change single pipes to double pipes in composer.json files - Fixed test.composer.json --- .../Magento/Framework/App/Test/Unit/_files/test.composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/_files/test.composer.json b/lib/internal/Magento/Framework/App/Test/Unit/_files/test.composer.json index ae4eb6880df65..fd3b9089e8345 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/_files/test.composer.json +++ b/lib/internal/Magento/Framework/App/Test/Unit/_files/test.composer.json @@ -8,7 +8,7 @@ "AFL-3.0" ], "require": { - "php": "~5.6.0|7.0.2|^7.0.6", + "php": "~5.6.0||7.0.2||^7.0.6", "ext-openssl": "*" }, "require-dev": { From 0e303660dbe08418c6653059dca7fa85cdf8f770 Mon Sep 17 00:00:00 2001 From: Adarsh Manickam <amanickam@ztech.io> Date: Fri, 15 Nov 2019 00:10:37 +0530 Subject: [PATCH 2055/2437] Resolved Adobe issue 691 --- .../base/web/js/grid/columns/image-preview.js | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 1ef2ebf6594fa..7cbbfdab28ba1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -4,8 +4,9 @@ */ define([ 'jquery', - 'Magento_Ui/js/grid/columns/column' -], function ($, Column) { + 'Magento_Ui/js/grid/columns/column', + 'Magento_Ui/js/lib/key-codes' +], function ($, Column, keyCodes) { 'use strict'; return Column.extend({ @@ -32,6 +33,18 @@ define([ } }, + /** + * Initialize image preview component + * + * @returns {Object} + */ + initialize: function () { + this._super(); + this.setNavigationListener(); + + return this; + }, + /** * Init observable variables * @return {Object} @@ -174,6 +187,36 @@ define([ block: 'center', inline: 'nearest' }); - } + }, + + /** + * Set image preview keyboard navigation listener + */ + setNavigationListener: function () { + var imageIndex, endIndex, key, + startIndex = 0, + imageColumnSelector = '.masonry-image-column', + adobeModalSelector = '.adobe-stock-modal', + imageGridSelector = '.masonry-image-grid'; + + $(document).on('keydown', function(e) { + key = keyCodes[e.keyCode]; + endIndex = $(imageGridSelector)[0].children.length - 1; + + if($(this.previewImageSelector).length > 0) { + imageIndex = $(this.previewImageSelector) + .parents(imageColumnSelector) + .data('repeatIndex'); + } + + if($(adobeModalSelector).hasClass('_show')) { + if(key === 'pageLeftKey' && imageIndex !== startIndex) { + $(this.previewImageSelector + ' .action-previous').click(); + } else if (key === 'pageRightKey' && imageIndex !== endIndex) { + $(this.previewImageSelector + ' .action-next').click(); + } + } + }); + }, }); }); From 4d8aacae47e080d9aff26f5bb1aea4e707707912 Mon Sep 17 00:00:00 2001 From: Raoul Rego <rrego@adobe.com> Date: Thu, 14 Nov 2019 12:55:50 -0600 Subject: [PATCH 2056/2437] MC-19642: Change single pipes to double pipes in composer.json files - Fixed composer.lock --- composer.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.lock b/composer.lock index f5e4fe783879e..65e5fd8e5a42c 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "21394914b3f105a33f583ba59aeba748", + "content-hash": "e75fa994f056960e832018efd6af5a40", "packages": [ { "name": "braintree/braintree_php", From 841872ca4313ca6b4a3587a65598ce8206ad90a9 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 14 Nov 2019 13:14:53 -0600 Subject: [PATCH 2057/2437] MC-23068: Change in-product doc url to devdocs - Move "Minimum Terms to Match" down to search configuration section - Change documentation URL for "Minimum Terms to Match" --- .../Elasticsearch/etc/adminhtml/system.xml | 32 +++++++++---------- .../Elasticsearch6/etc/adminhtml/system.xml | 17 +++++----- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml index 1f61a48db9bfa..41c6710412db9 100644 --- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml @@ -55,21 +55,21 @@ <field id="engine">elasticsearch</field> </depends> </field> - <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Minimum Terms to Match</label> + <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection</frontend_model> <depends> <field id="engine">elasticsearch</field> </depends> - <comment><![CDATA[See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-minimum-should-match.html">here</a> for possible values.]]></comment> - <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> - <field id="elasticsearch_test_connect_wizard" translate="button_label" sortOrder="69" showInDefault="1" showInWebsite="0" showInStore="0"> - <label/> - <button_label>Test Connection</button_label> - <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch</field> </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> <!-- Elasticsearch 5.0+ --> <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> @@ -117,21 +117,21 @@ <field id="engine">elasticsearch5</field> </depends> </field> - <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Minimum Terms to Match</label> + <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="68" showInDefault="1" showInWebsite="0" showInStore="0"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\Elasticsearch5\TestConnection</frontend_model> <depends> <field id="engine">elasticsearch5</field> </depends> - <comment><![CDATA[See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-minimum-should-match.html">here</a> for possible values.]]></comment> - <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> - <field id="elasticsearch5_test_connect_wizard" translate="button_label" sortOrder="69" showInDefault="1" showInWebsite="0" showInStore="0"> - <label/> - <button_label>Test Connection</button_label> - <frontend_model>Magento\Elasticsearch\Block\Adminhtml\System\Config\Elasticsearch5\TestConnection</frontend_model> + <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch5</field> </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> </group> </section> diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml index 8d22fcbc5f8f4..beccb0c1ceb81 100644 --- a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -70,24 +70,23 @@ </depends> </field> - <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="78" showInDefault="1" + <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="78" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Minimum Terms to Match</label> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> <depends> <field id="engine">elasticsearch6</field> </depends> - <comment><![CDATA[See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-minimum-should-match.html">here</a> for possible values.]]></comment> - <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> - - <field id="elasticsearch6_test_connect_wizard" translate="button_label" sortOrder="79" showInDefault="1" + <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> - <label/> - <button_label>Test Connection</button_label> - <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <label>Minimum Terms to Match</label> <depends> <field id="engine">elasticsearch6</field> </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> </field> </group> </section> From 769c48d239648fd700f5b18157f2a5ef8d726b77 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 14 Nov 2019 15:53:52 -0600 Subject: [PATCH 2058/2437] MC-21727: It is impossible to remove Related, Up-Sells and Cross-Sells products via the import procedure --- .../Model/Import/Product.php | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 9ff2edaf2708f..ed8599a182b23 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1198,7 +1198,7 @@ protected function _initTypeModels() // phpcs:disable Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $this->_fieldsMap = array_merge($this->_fieldsMap, $model->getCustomFieldsMapping()); $this->_specialAttributes = array_merge($this->_specialAttributes, $model->getParticularAttributes()); - // phpcs:enable + // phpcs:enable } $this->_initErrorTemplates(); // remove doubles @@ -3060,6 +3060,7 @@ private function getValidationErrorLevel($sku): string * @param int $nextLinkId * @param array $positionAttrId * @return void + * @throws LocalizedException */ private function processLinkBunches( array $bunch, @@ -3070,6 +3071,7 @@ private function processLinkBunches( $productIds = []; $linkRows = []; $positionRows = []; + $linksToDelete = []; $bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); foreach ($bunch as $rowData) { @@ -3086,10 +3088,15 @@ function ($linkName) use ($rowData) { ); foreach ($linkNameToId as $linkName => $linkId) { $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + //process empty value + if (!empty($linkSkus[0]) && $linkSkus[0] === Import::DEFAULT_EMPTY_ATTRIBUTE_VALUE_CONSTANT) { + $linksToDelete[$linkId][] = $productId; + continue; + } + $linkPositions = !empty($rowData[$linkName . 'position']) ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) : []; - $linkSkus = array_filter( $linkSkus, function ($linkedSku) use ($sku) { @@ -3098,6 +3105,7 @@ function ($linkedSku) use ($sku) { && strcasecmp($linkedSku, $sku) !== 0; } ); + foreach ($linkSkus as $linkedKey => $linkedSku) { $linkedId = $this->getProductLinkedId($linkedSku); if ($linkedId == null) { @@ -3129,9 +3137,33 @@ function ($linkedSku) use ($sku) { } } } + $this->deleteProductsLinks($resource, $linksToDelete); $this->saveLinksData($resource, $productIds, $linkRows, $positionRows); } + /** + * Delete links + * + * @param Link $resource + * @param array $linksToDelete + * @return void + * @throws LocalizedException + */ + private function deleteProductsLinks(Link $resource, array $linksToDelete) { + if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $this->getBehavior()) { + foreach ($linksToDelete as $linkTypeId => $productIds) { + if (!empty($productIds)) { + $whereLinkId = $this->_connection->quoteInto('link_type_id', $linkTypeId); + $whereProductId = $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)); + $this->_connection->delete( + $resource->getMainTable(), + $whereLinkId . ' AND ' . $whereProductId + ); + } + } + } + } + /** * Fetches Product Links * From 2ce96d1c0ebb4dc4a3e7b89a9074b1db1d0b2c92 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 14 Nov 2019 16:21:59 -0600 Subject: [PATCH 2059/2437] MC-19926: Implement CSP --- .../Magento/Csp/Api/CspRendererInterface.php | 24 ++ .../Csp/Api/Data/ModeConfiguredInterface.php | 32 ++ .../Magento/Csp/Api/Data/PolicyInterface.php | 30 ++ .../Csp/Api/ModeConfigManagerInterface.php | 26 ++ .../Csp/Api/PolicyCollectorInterface.php | 28 ++ .../Csp/Api/PolicyRendererInterface.php | 36 ++ app/code/Magento/Csp/LICENSE.txt | 48 +++ app/code/Magento/Csp/LICENSE_AFL.txt | 48 +++ .../Collector/Config/FetchPolicyReader.php | 44 +++ .../Collector/Config/FlagPolicyReader.php | 33 ++ .../Config/PluginTypesPolicyReader.php | 33 ++ .../Config/PolicyReaderInterface.php | 33 ++ .../Collector/Config/PolicyReaderPool.php | 45 +++ .../Collector/Config/SandboxPolicyReader.php | 45 +++ .../Csp/Model/Collector/ConfigCollector.php | 92 +++++ .../Collector/CspWhitelistXml/Converter.php | 51 +++ .../Collector/CspWhitelistXml/Reader.php | 27 ++ .../CspWhitelistXml/SchemaLocator.php | 51 +++ .../Collector/CspWhitelistXmlCollector.php | 57 +++ .../Csp/Model/CompositePolicyCollector.php | 42 +++ app/code/Magento/Csp/Model/CspRenderer.php | 49 +++ .../Magento/Csp/Model/Mode/ConfigManager.php | 79 ++++ .../Csp/Model/Mode/Data/ModeConfigured.php | 52 +++ .../Magento/Csp/Model/Policy/FetchPolicy.php | 259 +++++++++++++ .../Magento/Csp/Model/Policy/FlagPolicy.php | 48 +++ .../Csp/Model/Policy/PluginTypesPolicy.php | 59 +++ .../Renderer/SimplePolicyHeaderRenderer.php | 75 ++++ .../Csp/Model/Policy/SandboxPolicy.php | 275 ++++++++++++++ .../Model/Policy/SimplePolicyInterface.php | 18 + .../Magento/Csp/Model/PolicyRendererPool.php | 48 +++ app/code/Magento/Csp/Observer/Render.php | 43 +++ app/code/Magento/Csp/README.md | 2 + app/code/Magento/Csp/composer.json | 24 ++ app/code/Magento/Csp/etc/adminhtml/events.xml | 12 + app/code/Magento/Csp/etc/csp_whitelist.xsd | 52 +++ app/code/Magento/Csp/etc/di.xml | 44 +++ app/code/Magento/Csp/etc/frontend/events.xml | 12 + app/code/Magento/Csp/etc/module.xml | 10 + app/code/Magento/Csp/registration.php | 9 + composer.json | 3 +- .../Magento/TestModuleCspConfig/composer.json | 21 ++ .../TestModuleCspConfig/etc/csp_whitelist.xml | 23 ++ .../TestModuleCspConfig/etc/module.xml | 10 + .../TestModuleCspConfig/registration.php | 12 + .../TestModuleCspConfig2/composer.json | 21 ++ .../etc/csp_whitelist.xml | 17 + .../TestModuleCspConfig2/etc/module.xml | 14 + .../TestModuleCspConfig2/registration.php | 12 + .../testsuite/Magento/Csp/CspTest.php | 164 +++++++++ .../Model/Collector/ConfigCollectorTest.php | 343 ++++++++++++++++++ .../CspWhitelistXmlCollectorTest.php | 68 ++++ .../SimplePolicyHeaderRendererTest.php | 130 +++++++ 52 files changed, 2832 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Csp/Api/CspRendererInterface.php create mode 100644 app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php create mode 100644 app/code/Magento/Csp/Api/Data/PolicyInterface.php create mode 100644 app/code/Magento/Csp/Api/ModeConfigManagerInterface.php create mode 100644 app/code/Magento/Csp/Api/PolicyCollectorInterface.php create mode 100644 app/code/Magento/Csp/Api/PolicyRendererInterface.php create mode 100644 app/code/Magento/Csp/LICENSE.txt create mode 100644 app/code/Magento/Csp/LICENSE_AFL.txt create mode 100644 app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php create mode 100644 app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php create mode 100644 app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php create mode 100644 app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php create mode 100644 app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php create mode 100644 app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php create mode 100644 app/code/Magento/Csp/Model/Collector/ConfigCollector.php create mode 100644 app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php create mode 100644 app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php create mode 100644 app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php create mode 100644 app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php create mode 100644 app/code/Magento/Csp/Model/CompositePolicyCollector.php create mode 100644 app/code/Magento/Csp/Model/CspRenderer.php create mode 100644 app/code/Magento/Csp/Model/Mode/ConfigManager.php create mode 100644 app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php create mode 100644 app/code/Magento/Csp/Model/Policy/FetchPolicy.php create mode 100644 app/code/Magento/Csp/Model/Policy/FlagPolicy.php create mode 100644 app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php create mode 100644 app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php create mode 100644 app/code/Magento/Csp/Model/Policy/SandboxPolicy.php create mode 100644 app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php create mode 100644 app/code/Magento/Csp/Model/PolicyRendererPool.php create mode 100644 app/code/Magento/Csp/Observer/Render.php create mode 100644 app/code/Magento/Csp/README.md create mode 100644 app/code/Magento/Csp/composer.json create mode 100644 app/code/Magento/Csp/etc/adminhtml/events.xml create mode 100644 app/code/Magento/Csp/etc/csp_whitelist.xsd create mode 100644 app/code/Magento/Csp/etc/di.xml create mode 100644 app/code/Magento/Csp/etc/frontend/events.xml create mode 100644 app/code/Magento/Csp/etc/module.xml create mode 100644 app/code/Magento/Csp/registration.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml create mode 100644 dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php create mode 100644 dev/tests/integration/testsuite/Magento/Csp/CspTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php diff --git a/app/code/Magento/Csp/Api/CspRendererInterface.php b/app/code/Magento/Csp/Api/CspRendererInterface.php new file mode 100644 index 0000000000000..696d2e147f054 --- /dev/null +++ b/app/code/Magento/Csp/Api/CspRendererInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders configured CSPs + */ +interface CspRendererInterface +{ + /** + * Render configured CSP for the given HTTP response. + * + * @param HttpResponse $response + * @return void + */ + public function render(HttpResponse $response): void; +} diff --git a/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php b/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php new file mode 100644 index 0000000000000..5779bbd052792 --- /dev/null +++ b/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api\Data; + +/** + * CSP mode. + */ +interface ModeConfiguredInterface +{ + /** + * Report only mode flag. + * + * In "report-only" mode browsers only report violation but do not restrict them. + * + * @return bool + */ + public function isReportOnly(): bool; + + /** + * URI of endpoint logging reported violations. + * + * Even in "restrict" mode violations can be logged. + * + * @return string|null + */ + public function getReportUri(): ?string; +} diff --git a/app/code/Magento/Csp/Api/Data/PolicyInterface.php b/app/code/Magento/Csp/Api/Data/PolicyInterface.php new file mode 100644 index 0000000000000..c713c4b61417f --- /dev/null +++ b/app/code/Magento/Csp/Api/Data/PolicyInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api\Data; + +/** + * Defined Content Security Policy. + * + * Different policies will have different types of data but they all will have identifiers and string representations. + */ +interface PolicyInterface +{ + /** + * Policy unique name (ID). + * + * @return string + */ + public function getId(): string; + + /** + * Value of the policy. + * + * @return string + */ + public function getValue(): string; +} diff --git a/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php b/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php new file mode 100644 index 0000000000000..79aff996bbf5c --- /dev/null +++ b/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; + +/** + * CSP mode config manager. + * + * Responsible for CSP mode configurations like report-only/restrict modes, report URL etc. + */ +interface ModeConfigManagerInterface +{ + /** + * Load CSP mode config. + * + * @return ModeConfiguredInterface + * @throws \RuntimeException When failed to retrieve configurations. + */ + public function getConfigured(): ModeConfiguredInterface; +} diff --git a/app/code/Magento/Csp/Api/PolicyCollectorInterface.php b/app/code/Magento/Csp/Api/PolicyCollectorInterface.php new file mode 100644 index 0000000000000..139dec77e040b --- /dev/null +++ b/app/code/Magento/Csp/Api/PolicyCollectorInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Collects CSPs from a source. + */ +interface PolicyCollectorInterface +{ + /** + * Collect all configured policies. + * + * Collector finds CSPs from configurations and returns a list. + * The resulting list will be used to render policies as is so it is a collector's responsibility to include + * previously found policies from $defaultPolicies or redefine them. + * + * @param PolicyInterface[] $defaultPolicies Default policies/policies found previously. + * @return PolicyInterface[] + */ + public function collect(array $defaultPolicies = []): array; +} diff --git a/app/code/Magento/Csp/Api/PolicyRendererInterface.php b/app/code/Magento/Csp/Api/PolicyRendererInterface.php new file mode 100644 index 0000000000000..db761598291d4 --- /dev/null +++ b/app/code/Magento/Csp/Api/PolicyRendererInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders one policy at a time. + * + * Different type of CSPs may require specific renderers due to being represented by different headers. + */ +interface PolicyRendererInterface +{ + /** + * Render a policy for a response. + * + * @param PolicyInterface $policy + * @param HttpResponse $response + * @return void + */ + public function render(PolicyInterface $policy, HttpResponse $response): void; + + /** + * Would this renderer work for given policy? + * + * @param PolicyInterface $policy + * @return bool + */ + public function canRender(PolicyInterface $policy): bool; +} diff --git a/app/code/Magento/Csp/LICENSE.txt b/app/code/Magento/Csp/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Csp/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/Csp/LICENSE_AFL.txt b/app/code/Magento/Csp/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Csp/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php new file mode 100644 index 0000000000000..8728755c594e6 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * Reads fetch directives. + */ +class FetchPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new FetchPolicy( + $id, + !empty($value['none']), + !empty($value['hosts']) ? array_values($value['hosts']) : [], + !empty($value['schemes']) ? array_values($value['schemes']) : [], + !empty($value['self']), + !empty($value['inline']), + !empty($value['eval']), + [], + [], + !empty($value['dynamic']) + ); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return in_array($id, FetchPolicy::POLICIES, true); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php new file mode 100644 index 0000000000000..f8d1ea962308f --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FlagPolicy; + +/** + * @inheritDoc + */ +class FlagPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new FlagPolicy($id); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return in_array($id, FlagPolicy::POLICIES, true); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php new file mode 100644 index 0000000000000..7214e6da62273 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\PluginTypesPolicy; + +/** + * @inheritDoc + */ +class PluginTypesPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new PluginTypesPolicy(array_values($value['types'])); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return $id === 'plugin-types'; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php new file mode 100644 index 0000000000000..2ebac992222d9 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Initiates a policy DTO based on a value found in Magento config. + */ +interface PolicyReaderInterface +{ + /** + * Read a policy from a config value. + * + * @param string $id + * @param string|array|bool $value + * @return PolicyInterface + */ + public function read(string $id, $value): PolicyInterface; + + /** + * Can given policy be read by this reader? + * + * @param string $id + * @return bool + */ + public function canRead(string $id): bool; +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php new file mode 100644 index 0000000000000..0fc6fe03e05fa --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +/** + * Pool of readers. + */ +class PolicyReaderPool +{ + /** + * @var PolicyReaderInterface[] + */ + private $readers; + + /** + * @param PolicyReaderInterface[] $readers + */ + public function __construct(array $readers) + { + $this->readers = $readers; + } + + /** + * Find a reader for the policy. + * + * @param string $id + * @return PolicyReaderInterface + * @throws \RuntimeException When failed to find a reader for given policy. + */ + public function getReader(string $id): PolicyReaderInterface + { + foreach ($this->readers as $reader) { + if ($reader->canRead($id)) { + return $reader; + } + } + + throw new \RuntimeException(sprintf('Failed to find a config reader for policy #%s', $id)); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php new file mode 100644 index 0000000000000..2699b8d57c037 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\SandboxPolicy; + +/** + * @inheritDoc + */ +class SandboxPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new SandboxPolicy( + !empty($value['forms']), + !empty($value['modals']), + !empty($value['orientation']), + !empty($value['pointer']), + !empty($value['popup']), + !empty($value['popups_to_escape']), + !empty($value['presentation']), + !empty($value['same_origin']), + !empty($value['scripts']), + !empty($value['navigation']), + !empty($value['navigation_by_user']) + ); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return $id === 'sandbox'; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php new file mode 100644 index 0000000000000..f7b63e7549133 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\Config\PolicyReaderPool; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; + +/** + * Reads Magento config. + */ +class ConfigCollector implements PolicyCollectorInterface +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var PolicyReaderPool + */ + private $readersPool; + + /** + * @var State + */ + private $state; + + /** + * @var Store + */ + private $storeModel; + + /** + * @param ScopeConfigInterface $config + * @param PolicyReaderPool $readersPool + * @param State $state + * @param Store $storeModel + */ + public function __construct( + ScopeConfigInterface $config, + PolicyReaderPool $readersPool, + State $state, + Store $storeModel + ) { + $this->config = $config; + $this->readersPool = $readersPool; + $this->state = $state; + $this->storeModel = $storeModel; + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $collected = $defaultPolicies; + + $configArea = null; + $area = $this->state->getAreaCode(); + if ($area === Area::AREA_ADMINHTML) { + $configArea = 'admin'; + } elseif ($area === Area::AREA_FRONTEND) { + $configArea = 'storefront'; + } + + if ($configArea) { + $policiesConfig = $this->config->getValue( + 'csp/policies/' . $configArea, + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + if (is_array($policiesConfig) && $policiesConfig) { + foreach ($policiesConfig as $policyConfig) { + $collected[] = $this->readersPool->getReader($policyConfig['policy_id']) + ->read($policyConfig['policy_id'], $policyConfig); + } + } + } + + return $collected; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php new file mode 100644 index 0000000000000..ab1ee8bb0befe --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Config\ConverterInterface; + +/** + * Converts csp_whitelist.xml files' content into config data. + */ +class Converter implements ConverterInterface +{ + /** + * @inheritDoc + */ + public function convert($source) + { + $policyConfig = []; + + /** @var \DOMNodeList $policies */ + $policies = $source->getElementsByTagName('policy'); + /** @var \DOMElement $policy */ + foreach ($policies as $policy) { + if ($policy->nodeType != XML_ELEMENT_NODE) { + continue; + } + $id = $policy->attributes->getNamedItem('id')->nodeValue; + if (!array_key_exists($id, $policyConfig)) { + $policyConfig[$id] = ['hosts' => [], 'hashes' => []]; + } + /** @var \DOMElement $value */ + foreach ($policy->getElementsByTagName('value') as $value) { + if ($value->attributes->getNamedItem('type')->nodeValue === 'host') { + $policyConfig[$id]['hosts'][] = $value->nodeValue; + } else { + $policyConfig[$id]['hashes'][$value->nodeValue] + = $value->attributes->getNamedItem('algorithm')->nodeValue; + } + } + $policyConfig[$id]['hosts'] = array_unique($policyConfig[$id]['hosts']); + $policyConfig[$id]['hashes'] = array_unique($policyConfig[$id]['hashes']); + } + + return $policyConfig; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php new file mode 100644 index 0000000000000..63f570ceb7a71 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Config\Reader\Filesystem; + +/** + * Config reader for csp_whitelist.xml files. + */ +class Reader extends Filesystem +{ + /** + * List of id attributes for merge + * + * @var array + */ + protected $_idAttributes = [ + '/csp_whitelist/policies/policy' => ['id'], + '/csp_whitelist/policies/policy/values/value' => ['id'] + ]; +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php new file mode 100644 index 0000000000000..285d37a1b6270 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Module\Dir; +use Magento\Framework\Config\SchemaLocatorInterface; +use Magento\Framework\Module\Dir\Reader; + +/** + * CSP whitelist config schema locator. + */ +class SchemaLocator implements SchemaLocatorInterface +{ + /** + * Path to corresponding XSD file with validation rules for merged config and per file config. + * + * @var string + */ + private $schema ; + + /** + * @param Reader $moduleReader + */ + public function __construct(Reader $moduleReader) + { + $this->schema = $moduleReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Magento_Csp') + . '/csp_whitelist.xsd'; + } + + /** + * @inheritDoc + */ + public function getSchema() + { + return $this->schema; + } + + /** + * @inheritDoc + */ + public function getPerFileSchema() + { + return $this->schema; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php new file mode 100644 index 0000000000000..66eb3747bce3e --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\CspWhitelistXml\Reader as ConfigReader; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * Collects policies defined in csp_whitelist.xml configs. + */ +class CspWhitelistXmlCollector implements PolicyCollectorInterface +{ + /** + * @var ConfigReader + */ + private $configReader; + + /** + * @param ConfigReader $configReader + */ + public function __construct(ConfigReader $configReader) + { + $this->configReader = $configReader; + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $policies = $defaultPolicies; + $config = $this->configReader->read(); + foreach ($config as $policyId => $values) { + $policies[] = new FetchPolicy( + $policyId, + false, + $values['hosts'], + [], + false, + false, + false, + [], + $values['hashes'] + ); + } + + + return $policies; + } +} diff --git a/app/code/Magento/Csp/Model/CompositePolicyCollector.php b/app/code/Magento/Csp/Model/CompositePolicyCollector.php new file mode 100644 index 0000000000000..31da34960ea75 --- /dev/null +++ b/app/code/Magento/Csp/Model/CompositePolicyCollector.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\PolicyCollectorInterface; + +/** + * Delegates collecting to multiple collectors. + */ +class CompositePolicyCollector implements PolicyCollectorInterface +{ + /** + * @var PolicyCollectorInterface[] + */ + private $collectors; + + /** + * @param PolicyCollectorInterface[] $collectors + */ + public function __construct(array $collectors) + { + $this->collectors = $collectors; + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $collected = $defaultPolicies; + foreach ($this->collectors as $collector) { + $collected = $collector->collect($collected); + } + + return $collected; + } +} diff --git a/app/code/Magento/Csp/Model/CspRenderer.php b/app/code/Magento/Csp/Model/CspRenderer.php new file mode 100644 index 0000000000000..a883820f6743a --- /dev/null +++ b/app/code/Magento/Csp/Model/CspRenderer.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * @inheritDoc + */ +class CspRenderer implements CspRendererInterface +{ + /** + * @var PolicyRendererPool + */ + private $rendererPool; + + /** + * @var PolicyCollectorInterface + */ + private $collector; + + /** + * @param PolicyRendererPool $rendererPool + * @param PolicyCollectorInterface $collector + */ + public function __construct(PolicyRendererPool $rendererPool, PolicyCollectorInterface $collector) + { + $this->rendererPool = $rendererPool; + $this->collector = $collector; + } + + /** + * @inheritDoc + */ + public function render(HttpResponse $response): void + { + $policies = $this->collector->collect(); + foreach ($policies as $policy) { + $this->rendererPool->getRenderer($policy)->render($policy, $response); + } + } +} diff --git a/app/code/Magento/Csp/Model/Mode/ConfigManager.php b/app/code/Magento/Csp/Model/Mode/ConfigManager.php new file mode 100644 index 0000000000000..874df5e0d226f --- /dev/null +++ b/app/code/Magento/Csp/Model/Mode/ConfigManager.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Mode; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; +use Magento\Csp\Api\ModeConfigManagerInterface; +use Magento\Csp\Model\Mode\Data\ModeConfigured; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; + +/** + * @inheritDoc + */ +class ConfigManager implements ModeConfigManagerInterface +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var Store + */ + private $storeModel; + + /** + * @var State + */ + private $state; + + /** + * @param ScopeConfigInterface $config + * @param Store $store + * @param State $state + */ + public function __construct(ScopeConfigInterface $config, Store $store, State $state) + { + $this->config = $config; + $this->storeModel = $store; + $this->state = $state; + } + + /** + * @inheritDoc + */ + public function getConfigured(): ModeConfiguredInterface + { + $area = $this->state->getAreaCode(); + if ($area === Area::AREA_ADMINHTML) { + $configArea = 'admin'; + } elseif ($area === Area::AREA_FRONTEND) { + $configArea = 'storefront'; + } else { + throw new \RuntimeException('CSP can only be configured for storefront or admin area'); + } + + + $reportOnly = $this->config->isSetFlag( + 'csp/mode/' . $configArea .'/report_only', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + $reportUri = $this->config->getValue( + 'csp/mode/' . $configArea .'/report_uri', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + + return new ModeConfigured($reportOnly, !empty($reportUri) ? $reportUri : null); + } +} diff --git a/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php b/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php new file mode 100644 index 0000000000000..db5e362f560a7 --- /dev/null +++ b/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Mode\Data; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; + +/** + * @inheritDoc + */ +class ModeConfigured implements ModeConfiguredInterface +{ + /** + * @var bool + */ + private $reportOnly; + + /** + * @var string|null + */ + private $reportUri; + + /** + * @param bool $reportOnly + * @param string|null $reportUri + */ + public function __construct(bool $reportOnly, ?string $reportUri) + { + $this->reportOnly = $reportOnly; + $this->reportUri = $reportUri; + } + + /** + * @inheritDoc + */ + public function isReportOnly(): bool + { + return $this->reportOnly; + } + + /** + * @inheritDoc + */ + public function getReportUri(): ?string + { + return $this->reportUri; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php new file mode 100644 index 0000000000000..87e95d5818004 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php @@ -0,0 +1,259 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Represents a fetch directive. + */ +class FetchPolicy implements SimplePolicyInterface +{ + /** + * List of possible fetch directives. + */ + public const POLICIES = [ + 'default-src', + 'child-src', + 'connect-src', + 'font-src', + 'frame-src', + 'img-src', + 'manifest-src', + 'media-src', + 'object-src', + 'script-src', + 'style-src', + 'base-uri', + 'form-action', + 'frame-ancestors' + ]; + + /** + * @var string + */ + private $id; + + /** + * @var string[] + */ + private $hostSources; + + /** + * @var string[] + */ + private $schemeSources; + + /** + * @var bool + */ + private $selfAllowed; + + /** + * @var bool + */ + private $inlineAllowed; + + /** + * @var bool + */ + private $evalAllowed; + + /** + * @var bool + */ + private $noneAllowed; + + /** + * @var string[] + */ + private $nonceValues; + + /** + * @var string[] + */ + private $hashes; + + /** + * @var bool + */ + private $dynamicAllowed; + + /** + * @param string $id + * @param bool $noneAllowed + * @param string[] $hostSources + * @param string[] $schemeSources + * @param bool $selfAllowed + * @param bool $inlineAllowed + * @param bool $evalAllowed + * @param string[] $nonceValues + * @param string[] $hashValues + * @param bool $dynamicAllowed + */ + public function __construct( + string $id, + bool $noneAllowed = true, + array $hostSources = [], + array $schemeSources = [], + bool $selfAllowed = false, + bool $inlineAllowed = false, + bool $evalAllowed = false, + array $nonceValues = [], + array $hashValues = [], + bool $dynamicAllowed = false + ) { + $this->id = $id; + $this->noneAllowed = $noneAllowed; + $this->hostSources = array_unique($hostSources); + $this->schemeSources = array_unique($schemeSources); + $this->selfAllowed = $selfAllowed; + $this->inlineAllowed = $inlineAllowed; + $this->evalAllowed = $evalAllowed; + $this->nonceValues = array_unique($nonceValues); + $this->hashes = $hashValues; + $this->dynamicAllowed = $dynamicAllowed; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return $this->id; + } + + /** + * Items can be loaded from given hosts. + * + * @return string[] + */ + public function getHostSources(): array + { + return $this->hostSources; + } + + /** + * Items can be loaded using following schemes. + * + * @return string[] + */ + public function getSchemeSources(): array + { + return $this->schemeSources; + } + + /** + * Items can be loaded from the same host/port as the HTML page. + * + * @return bool + */ + public function isSelfAllowed(): bool + { + return $this->selfAllowed; + } + + /** + * Items can be loaded from tags present on the original HTML page. + * + * @return bool + */ + public function isInlineAllowed(): bool + { + return $this->inlineAllowed; + } + + /** + * Allows creating items from strings. + * + * For example using "eval()" for JavaScript. + * + * @return bool + */ + public function isEvalAllowed(): bool + { + return $this->evalAllowed; + } + + /** + * Content type governed by this policy is disabled completely. + * + * @return bool + */ + public function isNoneAllowed(): bool + { + return $this->noneAllowed; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + if ($this->isNoneAllowed()) { + return '\'none\''; + } else { + $sources = $this->getHostSources(); + foreach ($this->getSchemeSources() as $schemeSource) { + $sources[] = $schemeSource .':'; + } + if ($this->isSelfAllowed()) { + $sources[] = '\'self\''; + } + if ($this->isInlineAllowed()) { + $sources[] = '\'unsafe-inline\''; + } + if ($this->isEvalAllowed()) { + $sources[] = '\'unsafe-eval\''; + } + if ($this->isDynamicAllowed()) { + $sources[] = '\'strict-dynamic\''; + } + foreach ($this->getNonceValues() as $nonce) { + $sources[] = '\'nonce-' .base64_encode($nonce) .'\''; + } + foreach ($this->getHashes() as $hash => $algorithm) { + $sources[]= "'$algorithm-$hash'"; + } + + return implode(' ', $sources); + } + } + + /** + * Unique cryptographically random numbers marking inline items as trusted. + * + * Contains only numbers, not encoded. + * + * @return string[] + */ + public function getNonceValues(): array + { + return $this->nonceValues; + } + + /** + * Unique hashes generated based on inline items marking them as trusted. + * + * Contains only hashes themselves, encoded into base64. Keys are the hashes, values are algorithms used. + * + * @return string[] + */ + public function getHashes(): array + { + return $this->hashes; + } + + /** + * Is trust to inline items propagated to items loaded by root items. + * + * @return bool + */ + public function isDynamicAllowed(): bool + { + return $this->dynamicAllowed; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/FlagPolicy.php b/app/code/Magento/Csp/Model/Policy/FlagPolicy.php new file mode 100644 index 0000000000000..041e1ca5d6229 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/FlagPolicy.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Policies that are used as flags without a value. + */ +class FlagPolicy implements SimplePolicyInterface +{ + public const POLICIES = [ + 'upgrade-insecure-requests', + 'block-all-mixed-content' + ]; + + /** + * @var string + */ + private $id; + + /** + * @param string $id + */ + public function __construct(string $id) + { + $this->id = $id; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return $this->id; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + return ''; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php b/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php new file mode 100644 index 0000000000000..4f34f49bfffe7 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Governs allowed plugin mime-types. + */ +class PluginTypesPolicy implements SimplePolicyInterface +{ + /** + * @var string[] + */ + private $types; + + /** + * @param string[] $types + */ + public function __construct(array $types) + { + if (!$types) { + throw new \RuntimeException('PluginTypePolicy must be given at least 1 type.'); + } + $this->types = array_unique($types); + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return 'plugin-types'; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + return implode(' ', $this->getTypes()); + } + + /** + * Mime types of allowed plugins. + * + * Types like "application/x-shockwave-flash", "application/x-java-applet". + * Will only work if object-src directive != "none". + * + * @return string[] + */ + public function getTypes(): array + { + return $this->types; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php new file mode 100644 index 0000000000000..b4fd418e89635 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy\Renderer; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Api\ModeConfigManagerInterface; +use Magento\Csp\Api\PolicyRendererInterface; +use Magento\Csp\Model\Policy\SimplePolicyInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders a simple policy as a "Content-Security-Policy" header. + */ +class SimplePolicyHeaderRenderer implements PolicyRendererInterface +{ + /** + * @var ModeConfigManagerInterface + */ + private $modeConfig; + + /** + * @var ModeConfiguredInterface + */ + private $config; + + /** + * @param ModeConfigManagerInterface $modeConfig + */ + public function __construct(ModeConfigManagerInterface $modeConfig) + { + $this->modeConfig = $modeConfig; + } + + /** + * @inheritDoc + */ + public function render(PolicyInterface $policy, HttpResponse $response): void + { + /** @var SimplePolicyInterface $policy */ + $config = $this->modeConfig->getConfigured(); + if ($config->isReportOnly()) { + $header = 'Content-Security-Policy-Report-Only'; + } else { + $header = 'Content-Security-Policy'; + } + $value = $policy->getId() .' ' .$policy->getValue() .';'; + if ($config->getReportUri()) { + $reportToData = [ + 'group' => 'report-endpoint', + 'max_age' => 10886400, + 'endpoints' => [ + ['url' => $config->getReportUri()] + ] + ]; + $value .= ' report-uri ' .$config->getReportUri() .';'; + $value .= ' report-to '. $reportToData['group'] .';'; + $response->setHeader('Report-To', json_encode($reportToData), true); + } + $response->setHeader($header, $value, false); + } + + /** + * @inheritDoc + */ + public function canRender(PolicyInterface $policy): bool + { + return true; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php new file mode 100644 index 0000000000000..0aeb221e79ca6 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php @@ -0,0 +1,275 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * "sandbox" directive enables sandbox mode for requested pages limiting their functionality. + * + * Works the same as "sandbox" attribute for iframes but for the main document. + */ +class SandboxPolicy implements SimplePolicyInterface +{ + /** + * @var bool + */ + private $formAllowed; + + /** + * @var bool + */ + private $modalsAllowed; + + /** + * @var bool + */ + private $orientationLockAllowed; + + /** + * @var bool + */ + private $pointerLockAllowed; + + /** + * @var bool + */ + private $popupsAllowed; + + /** + * @var bool + */ + private $popupsToEscapeSandboxAllowed; + + /** + * @var bool + */ + private $presentationAllowed; + + /** + * @var bool + */ + private $sameOriginAllowed; + + /** + * @var bool + */ + private $scriptsAllowed; + + /** + * @var bool + */ + private $topNavigationAllowed; + + /** + * @var bool + */ + private $topNavigationByUserActivationAllowed; + + /** + * @param bool $formAllowed + * @param bool $modalsAllowed + * @param bool $orientationLockAllowed + * @param bool $pointerLockAllowed + * @param bool $popupsAllowed + * @param bool $popupsToEscapeSandboxAllowed + * @param bool $presentationAllowed + * @param bool $sameOriginAllowed + * @param bool $scriptsAllowed + * @param bool $topNavigationAllowed + * @param bool $topNavigationByUserActivationAllowed + */ + public function __construct( + bool $formAllowed, + bool $modalsAllowed, + bool $orientationLockAllowed, + bool $pointerLockAllowed, + bool $popupsAllowed, + bool $popupsToEscapeSandboxAllowed, + bool $presentationAllowed, + bool $sameOriginAllowed, + bool $scriptsAllowed, + bool $topNavigationAllowed, + bool $topNavigationByUserActivationAllowed + ) { + $this->formAllowed = $formAllowed; + $this->modalsAllowed = $modalsAllowed; + $this->orientationLockAllowed = $orientationLockAllowed; + $this->pointerLockAllowed = $pointerLockAllowed; + $this->popupsAllowed = $popupsAllowed; + $this->popupsToEscapeSandboxAllowed = $popupsToEscapeSandboxAllowed; + $this->presentationAllowed = $presentationAllowed; + $this->sameOriginAllowed = $sameOriginAllowed; + $this->scriptsAllowed = $scriptsAllowed; + $this->topNavigationAllowed = $topNavigationAllowed; + $this->topNavigationByUserActivationAllowed = $topNavigationByUserActivationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isFormAllowed(): bool + { + return $this->formAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isModalsAllowed(): bool + { + return $this->modalsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isOrientationLockAllowed(): bool + { + return $this->orientationLockAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPointerLockAllowed(): bool + { + return $this->pointerLockAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPopupsAllowed(): bool + { + return $this->popupsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPopupsToEscapeSandboxAllowed(): bool + { + return $this->popupsToEscapeSandboxAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPresentationAllowed(): bool + { + return $this->presentationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isSameOriginAllowed(): bool + { + return $this->sameOriginAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isScriptsAllowed(): bool + { + return $this->scriptsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isTopNavigationAllowed(): bool + { + return $this->topNavigationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isTopNavigationByUserActivationAllowed(): bool + { + return $this->topNavigationByUserActivationAllowed; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return 'sandbox'; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + $allowed = []; + + if ($this->isFormAllowed()) { + $allowed[] = 'allow-forms'; + } + if ($this->isModalsAllowed()) { + $allowed[] = 'allow-modals'; + } + if ($this->isOrientationLockAllowed()) { + $allowed[] = 'allow-orientation-lock'; + } + if ($this->isPointerLockAllowed()) { + $allowed[] = 'allow-pointer-lock'; + } + if ($this->isPopupsAllowed()) { + $allowed[] = 'allow-popups'; + } + if ($this->isPopupsToEscapeSandboxAllowed()) { + $allowed[] = 'allow-popups-to-escape-sandbox'; + } + if ($this->isPresentationAllowed()) { + $allowed[] = 'allow-presentation'; + } + if ($this->isSameOriginAllowed()) { + $allowed[] = 'allow-same-origin'; + } + if ($this->isScriptsAllowed()) { + $allowed[] = 'allow-scripts'; + } + if ($this->isTopNavigationAllowed()) { + $allowed[] = 'allow-top-navigation'; + } + if ($this->isTopNavigationByUserActivationAllowed()) { + $allowed[] = 'allow-top-navigation-by-user-activation'; + } + + if (!$allowed) { + throw new \RuntimeException('At least 1 option must be selected'); + } + return implode(' ', $allowed); + } +} diff --git a/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php b/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php new file mode 100644 index 0000000000000..aea28a1469de5 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Simple policy that is represented by the default prefix and an ID - string value combination. + */ +interface SimplePolicyInterface extends PolicyInterface +{ + +} diff --git a/app/code/Magento/Csp/Model/PolicyRendererPool.php b/app/code/Magento/Csp/Model/PolicyRendererPool.php new file mode 100644 index 0000000000000..5bce191e3a878 --- /dev/null +++ b/app/code/Magento/Csp/Model/PolicyRendererPool.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Api\PolicyRendererInterface; + +/** + * Pool of policy renderers. + */ +class PolicyRendererPool +{ + /** + * @var PolicyRendererInterface[] + */ + private $renderers; + + /** + * @param PolicyRendererInterface[] $renderers + */ + public function __construct(array $renderers) + { + $this->renderers = $renderers; + } + + /** + * Get renderer for the given policy. + * + * @param PolicyInterface $forPolicy + * @return PolicyRendererInterface + * @throws \RuntimeException When it's impossible to find a proper renderer. + */ + public function getRenderer(PolicyInterface $forPolicy): PolicyRendererInterface + { + foreach ($this->renderers as $renderer) { + if ($renderer->canRender($forPolicy)) { + return $renderer; + } + } + + throw new \RuntimeException(sprintf('Failed to find a renderer for policy #%s', $forPolicy->getId())); + } +} diff --git a/app/code/Magento/Csp/Observer/Render.php b/app/code/Magento/Csp/Observer/Render.php new file mode 100644 index 0000000000000..2b88d685f3fe4 --- /dev/null +++ b/app/code/Magento/Csp/Observer/Render.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Observer; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Adds CSP rendering after HTTP response is generated. + */ +class Render implements ObserverInterface +{ + /** + * @var CspRendererInterface + */ + private $cspRenderer; + + /** + * @param CspRendererInterface $cspRenderer + */ + public function __construct(CspRendererInterface $cspRenderer) + { + $this->cspRenderer = $cspRenderer; + } + + /** + * @inheritDoc + */ + public function execute(Observer $observer) + { + /** @var HttpResponse $response */ + $response = $observer->getEvent()->getData('response'); + + $this->cspRenderer->render($response); + } +} diff --git a/app/code/Magento/Csp/README.md b/app/code/Magento/Csp/README.md new file mode 100644 index 0000000000000..47f0f196becd5 --- /dev/null +++ b/app/code/Magento/Csp/README.md @@ -0,0 +1,2 @@ +Magento_Csp implements Content Security Policies for Magento. Allows CSP configuration for Merchants, +provides a way for extension and theme developers to configure CSP headers for their extensions. diff --git a/app/code/Magento/Csp/composer.json b/app/code/Magento/Csp/composer.json new file mode 100644 index 0000000000000..a18fcf21dcca2 --- /dev/null +++ b/app/code/Magento/Csp/composer.json @@ -0,0 +1,24 @@ +{ + "name": "magento/module-csp", + "description": "CSP module enables Content Security Policies for Magento", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Csp\\": "" + } + } +} diff --git a/app/code/Magento/Csp/etc/adminhtml/events.xml b/app/code/Magento/Csp/etc/adminhtml/events.xml new file mode 100644 index 0000000000000..f81031d2a779a --- /dev/null +++ b/app/code/Magento/Csp/etc/adminhtml/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_front_send_response_before"> + <observer name="csp_render" instance="Magento\Csp\Observer\Render" /> + </event> +</config> diff --git a/app/code/Magento/Csp/etc/csp_whitelist.xsd b/app/code/Magento/Csp/etc/csp_whitelist.xsd new file mode 100644 index 0000000000000..8b4b78008fe99 --- /dev/null +++ b/app/code/Magento/Csp/etc/csp_whitelist.xsd @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Structure description for webapi.xml configuration files. + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="csp_whitelist" type="cspWhitelistType" /> + + <xs:complexType name="cspWhitelistType"> + <xs:sequence> + <xs:element name="policies" type="policiesType" minOccurs="1" maxOccurs="1"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="policiesType"> + <xs:sequence> + <xs:element name="policy" type="policyType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="policyType"> + <xs:sequence> + <xs:element name="values" type="valuesType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="id" type="xs:string" use="required" /> + </xs:complexType> + + <xs:complexType name="valuesType"> + <xs:sequence> + <xs:element name="value" type="valueType" minOccurs="1" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + <xs:complexType name="valueType"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute type="xs:string" name="id" use="required" /> + <xs:attribute type="cspValueType" name="type" use="required" /> + <xs:attribute type="xs:string" name="algorithm" use="optional" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:simpleType name="cspValueType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="host" /> + <xs:enumeration value="hash" /> + </xs:restriction> + </xs:simpleType> +</xs:schema> diff --git a/app/code/Magento/Csp/etc/di.xml b/app/code/Magento/Csp/etc/di.xml new file mode 100644 index 0000000000000..f18453c91791d --- /dev/null +++ b/app/code/Magento/Csp/etc/di.xml @@ -0,0 +1,44 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\Csp\Api\CspRendererInterface" type="Magento\Csp\Model\CspRenderer" /> + <type name="Magento\Csp\Model\PolicyRendererPool"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="header" xsi:type="object">Magento\Csp\Model\Policy\Renderer\SimplePolicyHeaderRenderer</item> + </argument> + </arguments> + </type> + <preference for="Magento\Csp\Api\PolicyCollectorInterface" type="Magento\Csp\Model\CompositePolicyCollector" /> + <type name="Magento\Csp\Model\CompositePolicyCollector"> + <arguments> + <argument name="collectors" xsi:type="array"> + <item name="config" xsi:type="object">Magento\Csp\Model\Collector\ConfigCollector</item> + <item name="csp_whitelist" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXmlCollector</item> + </argument> + </arguments> + </type> + <type name="Magento\Csp\Model\Collector\Config\PolicyReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="fetch" xsi:type="object">Magento\Csp\Model\Collector\Config\FetchPolicyReader</item> + <item name="plugin_types" xsi:type="object">Magento\Csp\Model\Collector\Config\PluginTypesPolicyReader</item> + <item name="sandbox" xsi:type="object">Magento\Csp\Model\Collector\Config\SandboxPolicyReader</item> + <item name="flag" xsi:type="object">Magento\Csp\Model\Collector\Config\FlagPolicyReader</item> + </argument> + </arguments> + </type> + <preference for="Magento\Csp\Api\ModeConfigManagerInterface" type="Magento\Csp\Model\Mode\ConfigManager" /> + <type name="Magento\Csp\Model\Collector\CspWhitelistXml\Reader"> + <arguments> + <argument name="converter" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\Converter</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\SchemaLocator</argument> + <argument name="fileName" xsi:type="string">csp_whitelist.xml</argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Csp/etc/frontend/events.xml b/app/code/Magento/Csp/etc/frontend/events.xml new file mode 100644 index 0000000000000..f81031d2a779a --- /dev/null +++ b/app/code/Magento/Csp/etc/frontend/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_front_send_response_before"> + <observer name="csp_render" instance="Magento\Csp\Observer\Render" /> + </event> +</config> diff --git a/app/code/Magento/Csp/etc/module.xml b/app/code/Magento/Csp/etc/module.xml new file mode 100644 index 0000000000000..e99608ef13f15 --- /dev/null +++ b/app/code/Magento/Csp/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_Csp" /> +</config> diff --git a/app/code/Magento/Csp/registration.php b/app/code/Magento/Csp/registration.php new file mode 100644 index 0000000000000..90f4a25452858 --- /dev/null +++ b/app/code/Magento/Csp/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use \Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Csp', __DIR__); diff --git a/composer.json b/composer.json index 62366037bc18b..3644a87b160b4 100644 --- a/composer.json +++ b/composer.json @@ -289,7 +289,8 @@ "components/jqueryui": "1.10.4", "twbs/bootstrap": "3.1.0", "tinymce/tinymce": "3.4.7", - "magento/module-tinymce-3": "*" + "magento/module-tinymce-3": "*", + "magento/module-csp": "*" }, "conflict": { "gene/bluefoot": "*" diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json b/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json new file mode 100644 index 0000000000000..f4d4075fe3377 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-csp-config", + "description": "test csp module", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-integration": "*" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/TestModuleCspConfig" + ] + ] + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml new file mode 100644 index 0000000000000..e9eb1fe21aa4f --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd"> + <policies> + <policy id="object-src"> + <values> + <value id="mage-base" type="host">https://magento.com</value> + <value id="hash" type="hash" algorithm="sha256">B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=</value> + </values> + </policy> + <policy id="media-src"> + <values> + <value id="mage-base" type="host">https://magento.com</value> + <value id="devdocs-base" type="host">https://devdocs.magento.com</value> + </values> + </policy> + </policies> +</csp_whitelist> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml new file mode 100644 index 0000000000000..ee90595cacf19 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleCspConfig" active="true" /> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php b/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php new file mode 100644 index 0000000000000..9749bf5a7f621 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig', __DIR__); +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json new file mode 100644 index 0000000000000..ebf1d57fe6593 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-csp-config2", + "description": "test csp module 2", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-integration": "*" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/TestModuleCspConfig2" + ] + ] + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml new file mode 100644 index 0000000000000..eff59271bf422 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd"> + <policies> + <policy id="object-src"> + <values> + <value id="devdocs-base" type="host">https://devdocs.magento.com</value> + <value id="mage-base" type="host">http://magento.com</value> + </values> + </policy> + </policies> +</csp_whitelist> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml new file mode 100644 index 0000000000000..de3c2ea25f4f2 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleCspConfig2" active="true"> + <sequence> + <module name="Magento_TestModuleCspConfig"/> + </sequence> + </module> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php new file mode 100644 index 0000000000000..539d9cd20e10f --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig2') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig2', __DIR__); +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/CspTest.php b/dev/tests/integration/testsuite/Magento/Csp/CspTest.php new file mode 100644 index 0000000000000..e66c6af36e42c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/CspTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp; + +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test CSP being rendered when Magento processes an HTTP request. + */ +class CspTest extends AbstractController +{ + /** + * Search the whole response for a string. + * + * @param \Magento\Framework\App\ResponseInterface|\Magento\Framework\App\Response\Http $response + * @param string $search + * @return bool + */ + private function searchInResponse($response, string $search): bool + { + if (mb_stripos(mb_strtolower($response->getBody()), mb_strtolower($search)) !== false) { + return true; + } + + foreach ($response->getHeaders() as $header) { + if (mb_stripos(mb_strtolower($header->toString()), mb_strtolower($search)) !== false) { + return true; + } + } + + return false; + } + + /** + * Check that configured policies are rendered on frontend. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/storefront/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/inline 1 + * @magentoConfigFixture default_store csp/policies/admin/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/admin/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/font_src/self 1 + * @return void + */ + public function testStorefrontPolicies(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + $this->assertFalse($this->searchInResponse($response, '\'none\'')); + $this->assertTrue($this->searchInResponse($response, 'script-src')); + $this->assertTrue($this->searchInResponse($response, '\'unsafe-inline\'')); + $this->assertFalse($this->searchInResponse($response, 'font-src')); + //Policies configured in cps_whitelist.xml files + $this->assertTrue($this->searchInResponse($response, 'object-src')); + $this->assertTrue($this->searchInResponse($response, 'media-src')); + } + + /** + * Check that configured policies are rendered on backend. + * + * @magentoAppArea adminhtml + * @magentoConfigFixture default_store csp/policies/admin/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/admin/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/admin/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/admin/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/admin/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/admin/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/admin/default_src/inline 1 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/storefront/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/self 1 + * @return void + */ + public function testAdminPolicies(): void + { + $this->dispatch('backend/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + $this->assertFalse($this->searchInResponse($response, '\'none\'')); + $this->assertTrue($this->searchInResponse($response, 'script-src')); + $this->assertTrue($this->searchInResponse($response, '\'unsafe-inline\'')); + $this->assertFalse($this->searchInResponse($response, 'font-src')); + } + + /** + * Check that CSP mode is considered when rendering policies. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /cspEndpoint/ + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @return void + */ + public function testReportOnlyMode(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy-Report-Only')); + $this->assertTrue($this->searchInResponse($response, '/cspEndpoint/')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + } + + /** + * Check that CSP reporting options are rendered for 'restrict' mode as well. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /cspEndpoint/ + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @return void + */ + public function testRestrictMode(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertFalse($this->searchInResponse($response, 'Content-Security-Policy-Report-Only')); + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, '/cspEndpoint/')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php new file mode 100644 index 0000000000000..db67b322ebd5d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -0,0 +1,343 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FetchPolicy; +use Magento\Csp\Model\Policy\FlagPolicy; +use Magento\Csp\Model\Policy\PluginTypesPolicy; +use Magento\Csp\Model\Policy\SandboxPolicy; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Test collecting policies from Magento config. + */ +class ConfigCollectorTest extends TestCase +{ + /** + * @var ConfigCollector + */ + private $collector; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->collector = Bootstrap::getObjectManager()->get(ConfigCollector::class); + } + + /** + * Test initiating policies from config. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/policy_id child-src + * @magentoConfigFixture default_store csp/policies/storefront/child_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/child_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/child_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/inline 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/schemes/scheme1 http + * @magentoConfigFixture default_store csp/policies/storefront/child_src/dynamic 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/policy_id child-src + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/eval 1 + * @magentoConfigFixture default_store csp/policies/storefront/connect_src/policy_id connect-src + * @magentoConfigFixture default_store csp/policies/storefront/connect_src/none 1 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/storefront/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/policy_id frame-src + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/dynamic 1 + * @magentoConfigFixture default_store csp/policies/storefront/img_src/policy_id img-src + * @magentoConfigFixture default_store csp/policies/storefront/img_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/img_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/policy_id manifest-src + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/media_src/policy_id media-src + * @magentoConfigFixture default_store csp/policies/storefront/media_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/media_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/object_src/policy_id object-src + * @magentoConfigFixture default_store csp/policies/storefront/object_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/object_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/storefront/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/policy_id base-uri + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/style_src/policy_id style-src + * @magentoConfigFixture default_store csp/policies/storefront/style_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/style_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/form_action/policy_id form-action + * @magentoConfigFixture default_store csp/policies/storefront/form_action/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/form_action/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/policy_id frame-ancestors + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/policy_id plugin-types + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/flash application/x-shockwave-flash + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/applet application/x-java-applet + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/policy_id sandbox + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/forms 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/modals 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/orientation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/pointer 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/popup 0 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/popups_to_escape 0 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/presentation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/same_origin 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/scripts 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/navigation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/navigation_by_user 1 + * @magentoConfigFixture default_store csp/policies/storefront/mixed_content/policy_id block-all-mixed-content + * @magentoConfigFixture default_store csp/policies/storefront/upgrade/policy_id upgrade-insecure-requests + * @return void + */ + public function testCollecting(): void + { + $policies = $this->collector->collect([]); + $childScrChecked = false; + $childScr2Checked = false; + $connectScrChecked = false; + $defaultScrChecked = false; + $fontScrChecked = false; + $frameScrChecked = false; + $imgScrChecked = false; + $manifestScrChecked = false; + $mediaScrChecked = false; + $objectScrChecked = false; + $scriptScrChecked = false; + $styleScrChecked = false; + $baseUriChecked = false; + $pluginTypesChecked = false; + $sandboxChecked = false; + $formActionChecked = false; + $frameAncestorsChecked = false; + $blockAllMixedChecked = false; + $upgradeChecked = false; + + $this->assertNotEmpty($policies); + /** @var PolicyInterface|FetchPolicy|FlagPolicy|SandboxPolicy|PluginTypesPolicy $policy */ + foreach ($policies as $policy) { + switch ($policy->getId()) + { + case 'child-src': + if ($policy->isEvalAllowed()) { + $childScr2Checked = true; + } else { + $childScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == ['http://magento.com', 'http://devdocs.magento.com'] + && $policy->getSchemeSources() == ['http'] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && $policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && $policy->isInlineAllowed(); + } + break; + case 'connect-src': + $connectScrChecked = $policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && !$policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'default-src': + $defaultScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == ['http://magento.com', 'http://devdocs.magento.com'] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'font-src': + $fontScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'frame-src': + $frameScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && $policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'img-src': + $imgScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'manifest-src': + $manifestScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'media-src': + $mediaScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'object-src': + $objectScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'script-src': + $scriptScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'style-src': + $styleScrChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'base-uri': + $baseUriChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'plugin-types': + $pluginTypesChecked = $policy->getTypes() + == ['application/x-shockwave-flash', 'application/x-java-applet']; + break; + case 'sandbox': + $sandboxChecked = $policy->isFormAllowed() + && $policy->isModalsAllowed() + && $policy->isOrientationLockAllowed() + && $policy->isPointerLockAllowed() + && !$policy->isPopupsAllowed() + && !$policy->isPopupsToEscapeSandboxAllowed() + && $policy->isPresentationAllowed() + && $policy->isSameOriginAllowed() + && $policy->isScriptsAllowed() + && $policy->isTopNavigationAllowed() + && $policy->isTopNavigationByUserActivationAllowed(); + break; + case 'form-action': + $formActionChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'frame-ancestors': + $frameAncestorsChecked = !$policy->isNoneAllowed() + && $policy->getHostSources() == [] + && $policy->getSchemeSources() == [] + && $policy->isSelfAllowed() + && !$policy->isEvalAllowed() + && !$policy->isDynamicAllowed() + && $policy->getHashes() == [] + && $policy->getNonceValues() == [] + && !$policy->isInlineAllowed(); + break; + case 'block-all-mixed-content': + $blockAllMixedChecked = $policy instanceof FlagPolicy; + break; + case 'upgrade-insecure-requests': + $upgradeChecked = $policy instanceof FlagPolicy; + break; + } + } + + $this->assertTrue($childScrChecked); + $this->assertTrue($childScr2Checked); + $this->assertTrue($connectScrChecked); + $this->assertTrue($defaultScrChecked); + $this->assertTrue($fontScrChecked); + $this->assertTrue($frameScrChecked); + $this->assertTrue($imgScrChecked); + $this->assertTrue($manifestScrChecked); + $this->assertTrue($mediaScrChecked); + $this->assertTrue($objectScrChecked); + $this->assertTrue($scriptScrChecked); + $this->assertTrue($styleScrChecked); + $this->assertTrue($baseUriChecked); + $this->assertTrue($pluginTypesChecked); + $this->assertTrue($sandboxChecked); + $this->assertTrue($formActionChecked); + $this->assertTrue($frameAncestorsChecked); + $this->assertTrue($blockAllMixedChecked); + $this->assertTrue($upgradeChecked); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php new file mode 100644 index 0000000000000..bbaabba9dd268 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Model\Policy\FetchPolicy; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test collecting csp_whitelist configurations. + */ +class CspWhitelistXmlCollectorTest extends TestCase +{ + /** + * @var CspWhitelistXmlCollector + */ + private $collector; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->collector = Bootstrap::getObjectManager()->get(CspWhitelistXmlCollector::class); + } + + /** + * Test collecting configurations from multiple XML files. + * + * @return void + */ + public function testCollecting(): void + { + $policies = $this->collector->collect([]); + + $mediaSrcChecked = false; + $objectSrcChecked = false; + $this->assertNotEmpty($policies); + /** @var FetchPolicy $policy */ + foreach ($policies as $policy) { + $this->assertFalse($policy->isNoneAllowed()); + $this->assertFalse($policy->isSelfAllowed()); + $this->assertFalse($policy->isInlineAllowed()); + $this->assertFalse($policy->isEvalAllowed()); + $this->assertFalse($policy->isDynamicAllowed()); + $this->assertEmpty($policy->getSchemeSources()); + $this->assertEmpty($policy->getNonceValues()); + if ($policy->getId() === 'object-src') { + $this->assertInstanceOf(FetchPolicy::class, $policy); + $this->assertEquals(['http://magento.com', 'https://devdocs.magento.com'], $policy->getHostSources()); + $this->assertEquals(['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], $policy->getHashes()); + $objectSrcChecked = true; + } elseif ($policy->getId() === 'media-src') { + $this->assertInstanceOf(FetchPolicy::class, $policy); + $this->assertEquals(['https://magento.com', 'https://devdocs.magento.com'], $policy->getHostSources()); + $this->assertEmpty($policy->getHashes()); + $mediaSrcChecked = true; + } + } + $this->assertTrue($objectSrcChecked); + $this->assertTrue($mediaSrcChecked); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php new file mode 100644 index 0000000000000..eed72ffef93b1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy\Renderer; + +use Magento\Csp\Model\Policy\FetchPolicy; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Response\Http as HttpResponse; + +/** + * Test that rendering policies via headers works. + */ +class SimplePolicyHeaderRendererTest extends TestCase +{ + /** + * @var SimplePolicyHeaderRenderer + */ + private $renderer; + + /** + * @var HttpResponse + */ + private $response; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->renderer = Bootstrap::getObjectManager()->get(SimplePolicyHeaderRenderer::class); + $this->response = Bootstrap::getObjectManager()->create(HttpResponse::class); + } + + /** + * Test policy rendering in restrict mode. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri 0 + * + * @return void + */ + public function testRenderRestrictMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue()); + } + + /** + * Test policy rendering in restrict mode with report URL provided. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /csp-reports/ + * + * @return void + */ + public function testRenderRestrictWithReportingMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEquals( + 'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;', + $header->getFieldValue() + ); + $this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To')); + $this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true)); + $this->assertEquals('report-endpoint', $reportData[0]['group']); + } + + /** + * Test policy rendering in report-only mode. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri 0 + * + * @return void + */ + public function testRenderReportMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy')); + $this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue()); + } + + /** + * Test policy rendering in report-only mode with report URL provided. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /csp-reports/ + * + * @return void + */ + public function testRenderReportWithReportingMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy')); + $this->assertEquals( + 'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;', + $header->getFieldValue() + ); + $this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To')); + $this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true)); + $this->assertEquals('report-endpoint', $reportData[0]['group']); + } +} From 2e359d1c579840d1989e0ffd10d4ad34a6ac426c Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 14 Nov 2019 16:34:36 -0600 Subject: [PATCH 2060/2437] MC-21727: It is impossible to remove Related, Up-Sells and Cross-Sells products via the import procedure --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index ed8599a182b23..e92bb95789eca 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -3061,6 +3061,7 @@ private function getValidationErrorLevel($sku): string * @param array $positionAttrId * @return void * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processLinkBunches( array $bunch, @@ -3149,7 +3150,8 @@ function ($linkedSku) use ($sku) { * @return void * @throws LocalizedException */ - private function deleteProductsLinks(Link $resource, array $linksToDelete) { + private function deleteProductsLinks(Link $resource, array $linksToDelete) + { if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $this->getBehavior()) { foreach ($linksToDelete as $linkTypeId => $productIds) { if (!empty($productIds)) { From 9898d5cc6e7dcec9eb3428d2e17a9fba014c6440 Mon Sep 17 00:00:00 2001 From: Diego Pires <diegocpires@gmail.com> Date: Thu, 14 Nov 2019 19:46:23 -0300 Subject: [PATCH 2061/2437] Fix regex for validate action in admin menu --- app/code/Magento/Backend/Model/Menu/Item/Validator.php | 2 +- app/code/Magento/Backend/etc/menu.xsd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Model/Menu/Item/Validator.php b/app/code/Magento/Backend/Model/Menu/Item/Validator.php index 7b72b355f551d..1dd1bd7861e4d 100644 --- a/app/code/Magento/Backend/Model/Menu/Item/Validator.php +++ b/app/code/Magento/Backend/Model/Menu/Item/Validator.php @@ -49,7 +49,7 @@ public function __construct() $attributeValidator = new \Zend_Validate(); $attributeValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); - $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_]+$/')); + $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_\-]+$/')); $textValidator = new \Zend_Validate_StringLength(['min' => 3, 'max' => 50]); diff --git a/app/code/Magento/Backend/etc/menu.xsd b/app/code/Magento/Backend/etc/menu.xsd index 2619b3f5fedac..4b408e8e86a17 100644 --- a/app/code/Magento/Backend/etc/menu.xsd +++ b/app/code/Magento/Backend/etc/menu.xsd @@ -100,7 +100,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9/_]{3,}" /> + <xs:pattern value="[a-zA-Z0-9/_\-]{3,}" /> </xs:restriction> </xs:simpleType> From 93dcd502a9b04c56fa46b5cc446915b4de5bf0c8 Mon Sep 17 00:00:00 2001 From: Diego Pires <diegocpires@gmail.com> Date: Thu, 14 Nov 2019 20:13:35 -0300 Subject: [PATCH 2062/2437] Fixed Code Style --- .../Backend/Model/Menu/Item/Validator.php | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Backend/Model/Menu/Item/Validator.php b/app/code/Magento/Backend/Model/Menu/Item/Validator.php index 1dd1bd7861e4d..d722dfd700dad 100644 --- a/app/code/Magento/Backend/Model/Menu/Item/Validator.php +++ b/app/code/Magento/Backend/Model/Menu/Item/Validator.php @@ -5,7 +5,14 @@ */ namespace Magento\Backend\Model\Menu\Item; +use BadMethodCallException; +use InvalidArgumentException; +use Zend_Validate; + /** + * Class Validator + * + * @package Magento\Backend\Model\Menu\Item * @api * @since 100.0.2 */ @@ -28,7 +35,7 @@ class Validator /** * The list of primitive validators * - * @var \Zend_Validate[] + * @var Zend_Validate[] */ protected $_validators = []; @@ -37,17 +44,17 @@ class Validator */ public function __construct() { - $idValidator = new \Zend_Validate(); + $idValidator = new Zend_Validate(); $idValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); $idValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/:_]+$/')); - $resourceValidator = new \Zend_Validate(); + $resourceValidator = new Zend_Validate(); $resourceValidator->addValidator(new \Zend_Validate_StringLength(['min' => 8])); $resourceValidator->addValidator( new \Zend_Validate_Regex('/^[A-Z][A-Za-z0-9]+_[A-Z][A-Za-z0-9]+::[A-Za-z_0-9]+$/') ); - $attributeValidator = new \Zend_Validate(); + $attributeValidator = new Zend_Validate(); $attributeValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_\-]+$/')); @@ -70,8 +77,8 @@ public function __construct() * * @param array $data * @return void - * @throws \InvalidArgumentException - * @throws \BadMethodCallException + * @throws InvalidArgumentException + * @throws BadMethodCallException */ public function validate($data) { @@ -101,15 +108,16 @@ private function checkMenuItemIsRemoved($data) /** * Check that menu item contains all required data + * * @param array $data * - * @throws \BadMethodCallException + * @throws BadMethodCallException */ private function assertContainsRequiredParameters($data) { foreach ($this->_required as $param) { if (!isset($data[$param])) { - throw new \BadMethodCallException('Missing required param ' . $param); + throw new BadMethodCallException('Missing required param ' . $param); } } } @@ -118,12 +126,12 @@ private function assertContainsRequiredParameters($data) * Check that menu item id is not used * * @param string $id - * @throws \InvalidArgumentException + * @throws InvalidArgumentException */ private function assertIdentifierIsNotUsed($id) { if (array_search($id, $this->_ids) !== false) { - throw new \InvalidArgumentException('Item with id ' . $id . ' already exists'); + throw new InvalidArgumentException('Item with id ' . $id . ' already exists'); } } @@ -132,7 +140,7 @@ private function assertIdentifierIsNotUsed($id) * * @param string $param * @param mixed $value - * @throws \InvalidArgumentException + * @throws InvalidArgumentException */ private function validateMenuItemParameter($param, $value) { @@ -148,7 +156,7 @@ private function validateMenuItemParameter($param, $value) return; } - throw new \InvalidArgumentException( + throw new InvalidArgumentException( "Param " . $param . " doesn't pass validation: " . implode( '; ', $validator->getMessages() @@ -162,16 +170,16 @@ private function validateMenuItemParameter($param, $value) * @param string $param * @param mixed $value * @return void - * @throws \InvalidArgumentException + * @throws InvalidArgumentException */ public function validateParam($param, $value) { if (in_array($param, $this->_required) && $value === null) { - throw new \InvalidArgumentException('Param ' . $param . ' is required'); + throw new InvalidArgumentException('Param ' . $param . ' is required'); } if ($value !== null && isset($this->_validators[$param]) && !$this->_validators[$param]->isValid($value)) { - throw new \InvalidArgumentException( + throw new InvalidArgumentException( 'Param ' . $param . ' doesn\'t pass validation: ' . implode( '; ', $this->_validators[$param]->getMessages() From 8327db6cb299ca3932b6e053dfb9c3da7f694170 Mon Sep 17 00:00:00 2001 From: Diego Pires <diegocpires@gmail.com> Date: Thu, 14 Nov 2019 20:22:09 -0300 Subject: [PATCH 2063/2437] Fixed Unit Tests --- .../Model/Menu/Config/_files/invalidMenuXmlArray.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php index cd3128754444b..5a4c8e978b78b 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php @@ -10,7 +10,7 @@ ' resource="Test_Value::value"/></menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value '' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': '' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -20,7 +20,7 @@ 'resource="Test_Value::value"/></menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value 'ad' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': 'ad' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -31,7 +31,7 @@ '</menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value 'adm$#@inhtml/notification' is not " . - "accepted by the pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': 'adm$#@inhtml/notification' is not a valid value of the atomic " . "type 'typeAction'.\nLine: 1\n" ], @@ -452,7 +452,7 @@ '<?xml version="1.0"?><config><menu><update action="" ' . 'id="Test_Value::some_value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value '' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': '' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -462,7 +462,7 @@ 'resource="Test_Value::value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value 'v' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': 'v' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -471,7 +471,7 @@ 'id="Test_Value::some_value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value '/@##gt;' is not " . - "accepted by the pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': '/@##gt;' is not a valid value of the atomic" . " type 'typeAction'.\nLine: 1\n" ], From 49b8adc054a2e87ad7b76719f8709cf628cebd11 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 14 Nov 2019 17:35:01 -0600 Subject: [PATCH 2064/2437] MC-21727: It is impossible to remove Related, Up-Sells and Cross-Sells products via the import procedure --- .../Model/Import/ProductTest.php | 47 ++++++++++++++++++- ...rt_with_product_links_with_empty_value.csv | 2 + 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 3e16e5b82a9bf..3a039217d61fc 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -16,6 +16,8 @@ use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; @@ -29,6 +31,7 @@ use Magento\Store\Model\Store; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; use Psr\Log\LoggerInterface; +use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; /** * Class ProductTest @@ -312,7 +315,6 @@ public function testStockState() * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException * @magentoAppIsolation enabled - * * @return void */ @@ -1574,6 +1576,49 @@ public function testValidateUrlKeysMultipleStores() $this->assertTrue($errors->getErrorsCount() == 0); } + /** + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_product_links_data.php + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testProductLinksWithEmptyValue() + { + // import data from CSV file + $pathToFile = __DIR__ . '/_files/products_to_import_with_product_links_with_empty_value.csv'; + $filesystem = BootstrapHelper::getObjectManager()->create(Filesystem::class); + + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + Csv::class, + [ + 'file' => $pathToFile, + 'directory' => $directory + ] + ); + $errors = $this->_model->setSource( + $source + )->setParameters( + [ + 'behavior' => Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product' + ] + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $objectManager = BootstrapHelper::getObjectManager(); + $resource = $objectManager->get(ProductResource::class); + $productId = $resource->getIdBySku('simple'); + /** @var \Magento\Catalog\Model\Product $product */ + $product = BootstrapHelper::getObjectManager()->create(Product::class); + $product->load($productId); + + $this->assertEmpty($product->getCrossSellProducts()); + $this->assertEmpty($product->getUpSellProducts()); + } + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv new file mode 100644 index 0000000000000..fbbf6e2fb33f2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv @@ -0,0 +1,2 @@ +sku,crosssell_skus,crosssell_position,upsell_skus,upsell_position +simple,__EMPTY__VALUE__,__EMPTY__VALUE__,__EMPTY__VALUE__,__EMPTY__VALUE__ From 16fa4c4a6d4fd2e49cfbecef7a3fae11f18613cc Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Thu, 14 Nov 2019 20:50:50 -0300 Subject: [PATCH 2065/2437] changes in menujs file into iu module --- lib/web/jquery/ui-modules/menu.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/web/jquery/ui-modules/menu.js b/lib/web/jquery/ui-modules/menu.js index 6d575b0462a9d..82a9b54f69e46 100644 --- a/lib/web/jquery/ui-modules/menu.js +++ b/lib/web/jquery/ui-modules/menu.js @@ -440,10 +440,7 @@ define([ return; } - this.timer = this._delay(function () { - this._close(); - this._open(submenu); - }, this.delay); + this._open(submenu); }, _open: function (submenu) { From 4fd4e60670df5bfe960ee7ad6f9ac96ac11fdcba Mon Sep 17 00:00:00 2001 From: Diego Pires <diegocpires@gmail.com> Date: Thu, 14 Nov 2019 21:09:43 -0300 Subject: [PATCH 2066/2437] Remove use statements --- .../Backend/Model/Menu/Item/Validator.php | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Backend/Model/Menu/Item/Validator.php b/app/code/Magento/Backend/Model/Menu/Item/Validator.php index d722dfd700dad..62225c5707c0d 100644 --- a/app/code/Magento/Backend/Model/Menu/Item/Validator.php +++ b/app/code/Magento/Backend/Model/Menu/Item/Validator.php @@ -5,10 +5,6 @@ */ namespace Magento\Backend\Model\Menu\Item; -use BadMethodCallException; -use InvalidArgumentException; -use Zend_Validate; - /** * Class Validator * @@ -35,7 +31,7 @@ class Validator /** * The list of primitive validators * - * @var Zend_Validate[] + * @var \Zend_Validate[] */ protected $_validators = []; @@ -44,17 +40,17 @@ class Validator */ public function __construct() { - $idValidator = new Zend_Validate(); + $idValidator = new \Zend_Validate(); $idValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); $idValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/:_]+$/')); - $resourceValidator = new Zend_Validate(); + $resourceValidator = new \Zend_Validate(); $resourceValidator->addValidator(new \Zend_Validate_StringLength(['min' => 8])); $resourceValidator->addValidator( new \Zend_Validate_Regex('/^[A-Z][A-Za-z0-9]+_[A-Z][A-Za-z0-9]+::[A-Za-z_0-9]+$/') ); - $attributeValidator = new Zend_Validate(); + $attributeValidator = new \Zend_Validate(); $attributeValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_\-]+$/')); @@ -77,8 +73,8 @@ public function __construct() * * @param array $data * @return void - * @throws InvalidArgumentException - * @throws BadMethodCallException + * @throws \InvalidArgumentException + * @throws \BadMethodCallException */ public function validate($data) { @@ -111,13 +107,13 @@ private function checkMenuItemIsRemoved($data) * * @param array $data * - * @throws BadMethodCallException + * @throws \BadMethodCallException */ private function assertContainsRequiredParameters($data) { foreach ($this->_required as $param) { if (!isset($data[$param])) { - throw new BadMethodCallException('Missing required param ' . $param); + throw new \BadMethodCallException('Missing required param ' . $param); } } } @@ -126,12 +122,12 @@ private function assertContainsRequiredParameters($data) * Check that menu item id is not used * * @param string $id - * @throws InvalidArgumentException + * @throws \InvalidArgumentException */ private function assertIdentifierIsNotUsed($id) { if (array_search($id, $this->_ids) !== false) { - throw new InvalidArgumentException('Item with id ' . $id . ' already exists'); + throw new \InvalidArgumentException('Item with id ' . $id . ' already exists'); } } @@ -140,7 +136,7 @@ private function assertIdentifierIsNotUsed($id) * * @param string $param * @param mixed $value - * @throws InvalidArgumentException + * @throws \InvalidArgumentException */ private function validateMenuItemParameter($param, $value) { @@ -156,7 +152,7 @@ private function validateMenuItemParameter($param, $value) return; } - throw new InvalidArgumentException( + throw new \InvalidArgumentException( "Param " . $param . " doesn't pass validation: " . implode( '; ', $validator->getMessages() @@ -170,16 +166,16 @@ private function validateMenuItemParameter($param, $value) * @param string $param * @param mixed $value * @return void - * @throws InvalidArgumentException + * @throws \InvalidArgumentException */ public function validateParam($param, $value) { if (in_array($param, $this->_required) && $value === null) { - throw new InvalidArgumentException('Param ' . $param . ' is required'); + throw new \InvalidArgumentException('Param ' . $param . ' is required'); } if ($value !== null && isset($this->_validators[$param]) && !$this->_validators[$param]->isValid($value)) { - throw new InvalidArgumentException( + throw new \InvalidArgumentException( 'Param ' . $param . ' doesn\'t pass validation: ' . implode( '; ', $this->_validators[$param]->getMessages() From 3a82b4af977764a6f8226b2f237dfbe768661970 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Fri, 15 Nov 2019 09:35:35 +0200 Subject: [PATCH 2067/2437] REFACTORING: adjust commands to the common style in the MediaGallery and change exception handle logic --- .../Model/Asset/Command/DeleteByPath.php | 2 +- .../Model/Asset/Command/GetById.php | 2 +- .../Model/Asset/Command/GetByPath.php | 2 +- .../MediaGallery/Model/Asset/Command/Save.php | 2 +- .../MediaGallery/Model/DataExtractor.php | 8 +++++++- .../Keyword/Command/GetAssetKeywords.php | 20 ++++++++++++++----- .../Keyword/Command/SaveAssetKeywords.php | 18 +++++++++++++++-- .../Model/Keyword/Command/SaveAssetLinks.php | 14 +++++++++++-- .../Plugin/Product/Gallery/Processor.php | 3 +-- .../Keyword/Command/GetAssetKeywordsTest.php | 18 +++++++++++++++-- .../Keyword/Command/SaveAssetKeywordsTest.php | 13 +++++++++++- .../Keyword/Command/SaveAssetLinksTest.php | 16 +++++++++++++-- 12 files changed, 97 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php index b7880354720b7..e387da6de6beb 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php @@ -62,11 +62,11 @@ public function execute(string $mediaAssetPath): void $tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); $connection->delete($tableName, [self::MEDIA_GALLERY_ASSET_PATH . ' = ?' => $mediaAssetPath]); } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __( 'Could not delete media asset with path %path: %error', ['path' => $mediaAssetPath, 'error' => $exception->getMessage()] ); - $this->logger->critical($message); throw new CouldNotDeleteException($message, $exception); } } diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php index 4519e0f926981..aa3bedd438c36 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php @@ -79,11 +79,11 @@ public function execute(int $mediaAssetId): AssetInterface return $this->assetFactory->create(['data' => $data]); } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __( 'En error occurred during get media asset with id %id by id: %error', ['id' => $mediaAssetId, 'error' => $exception->getMessage()] ); - $this->logger->critical($message); throw new IntegrationException($message, $exception); } } diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php index babf05599e6ce..db8482d3399ba 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php @@ -82,8 +82,8 @@ public function execute(string $mediaFilePath): AssetInterface return $mediaAssets; } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __('An error occurred during get media asset list: %1', $exception->getMessage()); - $this->logger->critical($message); throw new IntegrationException($message, $exception); } } diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php b/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php index 8deaca70a2173..7cb2f73169642 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php @@ -71,8 +71,8 @@ public function execute(AssetInterface $mediaAsset): int $connection->insertOnDuplicate($tableName, $this->extractor->extract($mediaAsset, AssetInterface::class)); return (int) $connection->lastInsertId($tableName); } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __('An error occurred during media asset save: %1', $exception->getMessage()); - $this->logger->critical($message); throw new CouldNotSaveException($message, $exception); } } diff --git a/app/code/Magento/MediaGallery/Model/DataExtractor.php b/app/code/Magento/MediaGallery/Model/DataExtractor.php index d4f73dda92036..92cf237022c28 100644 --- a/app/code/Magento/MediaGallery/Model/DataExtractor.php +++ b/app/code/Magento/MediaGallery/Model/DataExtractor.php @@ -15,7 +15,13 @@ class DataExtractor implements DataExtractorInterface { /** - * @inheritdoc + * Extract data from an object using available getters (does not process extension attributes) + * + * @param object $object + * @param string|null $interface + * + * @return array + * @throws \ReflectionException */ public function extract($object, string $interface = null): array { diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php index 2a1706c1a198c..5b826a26e937d 100644 --- a/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php @@ -7,11 +7,12 @@ namespace Magento\MediaGallery\Model\Keyword\Command; +use Magento\Framework\Exception\IntegrationException; use Magento\MediaGalleryApi\Api\Data\KeywordInterface; use Magento\MediaGalleryApi\Api\Data\KeywordInterfaceFactory; use Magento\MediaGalleryApi\Model\Keyword\Command\GetAssetKeywordsInterface; use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Exception\NotFoundException; +use Psr\Log\LoggerInterface; /** * ClassGetAssetKeywords @@ -31,18 +32,26 @@ class GetAssetKeywords implements GetAssetKeywordsInterface */ private $assetKeywordFactory; + /** + * @var LoggerInterface + */ + private $logger; + /** * GetAssetKeywords constructor. * * @param ResourceConnection $resourceConnection * @param KeywordInterfaceFactory $assetKeywordFactory + * @param LoggerInterface $logger */ public function __construct( ResourceConnection $resourceConnection, - KeywordInterfaceFactory $assetKeywordFactory + KeywordInterfaceFactory $assetKeywordFactory, + LoggerInterface $logger ) { $this->resourceConnection = $resourceConnection; $this->assetKeywordFactory = $assetKeywordFactory; + $this->logger = $logger; } /** @@ -50,8 +59,8 @@ public function __construct( * * @param int $assetId * - * @return KeywordInterface[] - * @throws NotFoundException + * @return KeywordInterface[]|[] + * @throws IntegrationException */ public function execute(int $assetId): array { @@ -71,8 +80,9 @@ public function execute(int $assetId): array return $keywords; } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __('An error occurred during get asset keywords: %1', $exception->getMessage()); - throw new NotFoundException($message, $exception); + throw new IntegrationException($message, $exception); } } } diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php index 12bb1035f20b3..b355a9a651cd4 100644 --- a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php @@ -13,6 +13,7 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Adapter\Pdo\Mysql; use Magento\Framework\Exception\CouldNotSaveException; +use Psr\Log\LoggerInterface; /** * Class SaveAssetKeywords @@ -33,22 +34,34 @@ class SaveAssetKeywords implements SaveAssetKeywordsInterface */ private $saveAssetLinks; + /** + * @var LoggerInterface + */ + private $logger; + /** * SaveAssetKeywords constructor. * * @param ResourceConnection $resourceConnection * @param SaveAssetLinks $saveAssetLinks + * @param LoggerInterface $logger */ public function __construct( ResourceConnection $resourceConnection, - SaveAssetLinks $saveAssetLinks + SaveAssetLinks $saveAssetLinks, + LoggerInterface $logger ) { $this->resourceConnection = $resourceConnection; $this->saveAssetLinks = $saveAssetLinks; + $this->logger = $logger; } /** - * @inheritdoc + * Save asset keywords. + * + * @param KeywordInterface[] $keywords + * @param int $assetId + * @throws CouldNotSaveException */ public function execute(array $keywords, int $assetId): void { @@ -72,6 +85,7 @@ public function execute(array $keywords, int $assetId): void $this->saveAssetLinks->execute($assetId, $this->getKeywordIds($data)); } } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __('An error occurred during save asset keyword: %1', $exception->getMessage()); throw new CouldNotSaveException($message, $exception); } diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php index 7f0e9b5cf94a3..4d3fd2bb5c30d 100644 --- a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php @@ -13,6 +13,7 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Adapter\Pdo\Mysql; use Magento\Framework\Exception\CouldNotSaveException; +use Psr\Log\LoggerInterface; /** * Class SaveAssetLinks @@ -29,14 +30,22 @@ class SaveAssetLinks private $resourceConnection; /** - * SaveAssetKeywords constructor. + * @var LoggerInterface + */ + private $logger; + + /** + * SaveAssetLinks constructor. * * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger */ public function __construct( - ResourceConnection $resourceConnection + ResourceConnection $resourceConnection, + LoggerInterface $logger ) { $this->resourceConnection = $resourceConnection; + $this->logger = $logger; } /** @@ -66,6 +75,7 @@ public function execute(int $assetId, array $keywordIds): void ); } } catch (\Exception $exception) { + $this->logger->critical($exception); $message = __('An error occurred during save asset keyword links: %1', $exception->getMessage()); throw new CouldNotSaveException($message, $exception); } diff --git a/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php index 7541231601db4..9144e77213179 100644 --- a/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php +++ b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php @@ -67,8 +67,7 @@ public function afterRemoveImage( try { $this->deleteMediaAssetByPath->execute($file); } catch (\Exception $exception) { - $message = __('An error occurred during media asset delete at media processor: %1', $exception->getMessage()); - $this->logger->critical($message->render()); + $this->logger->critical($$exception); } return $result; diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php index 6934a7e4e5503..3f92ccd0eb997 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php @@ -16,7 +16,11 @@ use Magento\Framework\Exception\NotFoundException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +/** + * GetAssetKeywordsTest + */ class GetAssetKeywordsTest extends TestCase { /** @@ -34,14 +38,21 @@ class GetAssetKeywordsTest extends TestCase */ private $assetKeywordFactoryStub; + /** + * @var LoggerInterface|MockObject + */ + private $logger; + protected function setUp(): void { $this->resourceConnectionStub = $this->createMock(ResourceConnection::class); $this->assetKeywordFactoryStub = $this->createMock(KeywordInterfaceFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->sut = new GetAssetKeywords( $this->resourceConnectionStub, - $this->assetKeywordFactoryStub + $this->assetKeywordFactoryStub, + $this->logger ); } @@ -80,7 +91,7 @@ public function casesProvider(): array } /** - * Negative test + * Test case when an error occured during get data request. * * @throws NotFoundException */ @@ -93,6 +104,9 @@ public function testNotFoundBecauseOfError(): void ->willThrowException((new \Exception())); $this->expectException(NotFoundException::class); + $this->logger->expects($this->once()) + ->method('critical') + ->willReturnSelf(); $this->sut->execute($randomAssetId); } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php index 52864f7aa89f2..57397f2705278 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php @@ -16,6 +16,7 @@ use Magento\Framework\Exception\CouldNotSaveException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * SaveAssetKeywordsTest. @@ -47,6 +48,11 @@ class SaveAssetKeywordsTest extends TestCase */ private $selectMock; + /** + * @var LoggerInterface|MockObject + */ + private $logger; + /** * SetUp */ @@ -58,10 +64,12 @@ public function setUp(): void ->disableOriginalConstructor() ->getMock(); $this->selectMock = $this->createMock(Select::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->sut = new SaveAssetKeywords( $this->resourceConnectionMock, - $this->saveAssetLinksMock + $this->saveAssetLinksMock, + $this->logger ); } @@ -106,6 +114,9 @@ public function testAssetNotSavingCausedByError(): void ->method('getConnection') ->willThrowException((new \Exception())); $this->expectException(CouldNotSaveException::class); + $this->logger->expects($this->once()) + ->method('critical') + ->willReturnSelf(); $this->sut->execute([$keyword], 1); } diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php index a4cdd76f90bfc..17916814eda95 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php @@ -13,6 +13,7 @@ use Magento\Framework\Exception\CouldNotSaveException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; /** * SaveAssetLinksTest. @@ -34,17 +35,23 @@ class SaveAssetLinksTest extends TestCase */ private $resourceConnectionMock; + /** + * @var LoggerInterface|MockObject + */ + private $logger; + /** * Prepare test objects. */ public function setUp(): void { - $this->connectionMock = $this->createMock(AdapterInterface::class); $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->sut = new SaveAssetLinks( - $this->resourceConnectionMock + $this->resourceConnectionMock, + $this->logger ); } @@ -56,6 +63,8 @@ public function setUp(): void * @param int $assetId * @param array $keywordIds * @param array $values + * + * @throws CouldNotSaveException */ public function testAssetKeywordsSave(int $assetId, array $keywordIds, array $values): void { @@ -96,6 +105,9 @@ public function testAssetNotSavingCausedByError(): void ->method('insertArray') ->willThrowException((new \Exception())); $this->expectException(CouldNotSaveException::class); + $this->logger->expects($this->once()) + ->method('critical') + ->willReturnSelf(); $this->sut->execute(1, [1, 2]); } From e694954529046406ba97ce0e723d48901a6e6e18 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Fri, 15 Nov 2019 10:24:57 +0200 Subject: [PATCH 2068/2437] Fix exception in test --- .../Unit/Model/Keyword/Command/GetAssetKeywordsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php index 3f92ccd0eb997..85030763303f0 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php @@ -7,13 +7,13 @@ namespace Magento\MediaGallery\Test\Unit\Model\Keyword\Command; +use Magento\Framework\Exception\IntegrationException; use Magento\MediaGallery\Model\Keyword\Command\GetAssetKeywords; use Magento\MediaGalleryApi\Api\Data\KeywordInterface; use Magento\MediaGalleryApi\Api\Data\KeywordInterfaceFactory; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Select; -use Magento\Framework\Exception\NotFoundException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -93,7 +93,7 @@ public function casesProvider(): array /** * Test case when an error occured during get data request. * - * @throws NotFoundException + * @throws IntegrationException */ public function testNotFoundBecauseOfError(): void { @@ -103,7 +103,7 @@ public function testNotFoundBecauseOfError(): void ->method('getConnection') ->willThrowException((new \Exception())); - $this->expectException(NotFoundException::class); + $this->expectException(IntegrationException::class); $this->logger->expects($this->once()) ->method('critical') ->willReturnSelf(); From 42ccbc354378faf8a907770bf7b3cfc46c40727c Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 15 Nov 2019 12:17:28 +0200 Subject: [PATCH 2069/2437] MC-20684: Admin: Add/remove product from other storeviews and websites --- .../Product/UpdateProductWebsiteTest.php | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php new file mode 100644 index 0000000000000..e2fc326dac985 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product\WebsiteFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks product websites attribute save behaviour + * + * @magentoDbIsolation enabled + */ +class UpdateProductWebsiteTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var WebsiteFactory */ + private $websiteProductsResourceFactory; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->websiteProductsResourceFactory = $this->objectManager->get(WebsiteFactory::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/Store/_files/website.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testAssignProductToWebsite(): void + { + $defaultWebsiteId = $this->websiteRepository->get('base')->getId(); + $secondWebsiteId = $this->websiteRepository->get('test')->getId(); + $product = $this->updateProductWebsites('simple2', [$defaultWebsiteId, $secondWebsiteId]); + $this->assertProductWebsites((int)$product->getId(), [$defaultWebsiteId, $secondWebsiteId]); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @return void + */ + public function testUnassignProductFromWebsite(): void + { + $product = $this->productRepository->get('simple-on-two-websites'); + $secondWebsiteId = $this->websiteRepository->get('test')->getId(); + $product->setWebsiteIds([$secondWebsiteId]); + $product = $this->productRepository->save($product); + $this->assertProductWebsites((int)$product->getId(), [$secondWebsiteId]); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testAssignNonExistingWebsite(): void + { + $messageFormat = 'The website with id %s that was requested wasn\'t found. Verify the website and try again.'; + $nonExistingWebsiteId = 921564; + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage((string)__(sprintf($messageFormat, $nonExistingWebsiteId))); + $this->updateProductWebsites('simple2', [$nonExistingWebsiteId]); + } + + /** + * Update product websites attribute + * + * @param string $productSku + * @param array $websiteIds + * @return ProductInterface + */ + private function updateProductWebsites(string $productSku, array $websiteIds): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->setWebsiteIds($websiteIds); + + return $this->productRepository->save($product); + } + + /** + * Assert that websites attribute was correctly saved + * + * @param int $productId + * @param array $expectedData + * @return void + */ + private function assertProductWebsites(int $productId, array $expectedData): void + { + $websiteResource = $this->websiteProductsResourceFactory->create(); + $this->assertEquals($expectedData, $websiteResource->getWebsites([$productId])[$productId]); + } +} From 0ba961533a2eb357df7d71964fcff154a4689be8 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Fri, 15 Nov 2019 13:05:46 +0200 Subject: [PATCH 2070/2437] MC-18057: Product doesn'y match by "Date" attribute condition --- .../Model/Condition/AbstractCondition.php | 12 ++++--- .../product_simple_with_date_attribute.php | 27 ++++++++++++++++ ...ct_simple_with_date_attribute_rollback.php | 32 +++++++++++++++++++ .../Block/Product/ProductListTest.php | 25 +++++++++++++++ .../Rule/Model/Condition/Sql/BuilderTest.php | 2 +- 5 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index f6e782b3e7a53..f60817cce608b 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -386,7 +386,7 @@ public function getValueSelectOptions() public function getValueParsed() { if (!$this->hasValueParsed()) { - $value = $this->getData('value'); + $value = $this->getValue(); if (is_array($value) && count($value) === 1) { $value = reset($value); } @@ -765,7 +765,7 @@ public function asStringRecursive($level = 0) /** * Validate product attribute value for condition * - * @param object|array|int|string|float|bool $validatedValue product attribute value + * @param object|array|int|string|float|bool|null $validatedValue product attribute value * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -830,6 +830,7 @@ public function validateAttribute($validatedValue) case '{}': case '!{}': if (is_scalar($validatedValue) && is_array($value)) { + $validatedValue = (string)$validatedValue; foreach ($value as $item) { if (stripos($validatedValue, (string)$item) !== false) { $result = true; @@ -877,13 +878,16 @@ public function validateAttribute($validatedValue) /** * Case and type insensitive comparison of values * - * @param string|int|float $validatedValue - * @param string|int|float $value + * @param string|int|float|null $validatedValue + * @param string|int|float|null $value * @param bool $strict * @return bool */ protected function _compareValues($validatedValue, $value, $strict = true) { + if (null === $value || null === $validatedValue) { + return $value == $validatedValue; + } if ($strict && is_numeric($validatedValue) && is_numeric($value)) { return $validatedValue == $value; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php new file mode 100644 index 0000000000000..2c78d9b74f969 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +include __DIR__ . '/product_date_attribute.php'; + +$attribute->setData('is_used_for_promo_rules', 1); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product with date') + ->setSku('simple_with_date') + ->setPrice(10) + ->setDescription('Description with <b>html tag</b>') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d')) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php new file mode 100644 index 0000000000000..0f155926dd6db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('simple_with_date', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +include __DIR__ . '/product_date_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php index 2695948e78314..85cd5331a29c4 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php @@ -172,4 +172,29 @@ public function createCollectionForSkuDataProvider() . '`attribute`:`sku`,`operator`:`!^[^]`,`value`:`virtual`^]^]', 'product-with-xss'] ]; } + + /** + * Check that collection returns correct result if use date attribute. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_date_attribute.php + * @return void + */ + public function testProductListWithDateAttribute() + { + $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,' + . '`aggregator`:`all`,`value`:`1`,`new_child`:``^],' + . '`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,' + . '`attribute`:`date_attribute`,`operator`:`==`,`value`:`' . date('Y-m-d') . '`^]^]'; + $this->block->setData('conditions_encoded', $encodedConditions); + + // Load products collection filtered using specified conditions and perform assertions + $productCollection = $this->block->createCollection(); + $productCollection->load(); + $this->assertEquals( + 1, + $productCollection->count(), + "Product collection was not filtered according to the widget condition." + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php b/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php index edf456f17fd9c..48e0bb62a4fba 100644 --- a/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php +++ b/dev/tests/integration/testsuite/Magento/Rule/Model/Condition/Sql/BuilderTest.php @@ -72,7 +72,7 @@ public function testAttachConditionToCollection(): void $rule->loadPost($ruleConditionArray); $this->model->attachConditionToCollection($collection, $rule->getConditions()); - $whereString = "/\(category_id IN \('3'\).+\(IFNULL\(`e`\.`entity_id`,.+\) = '2017-09-15'\)" + $whereString = "/\(category_id IN \('3'\).+\(IFNULL\(`e`\.`entity_id`,.+\) = '2017-09-15 00:00:00'\)" . ".+ORDER BY \(FIELD\(`e`.`sku`, ':\(', ':\)'\)\)/"; $this->assertEquals(1, preg_match($whereString, $collection->getSelectSql(true))); } From fa5c821b1ad4d5570be49ed14723ffd44e7427aa Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Wed, 13 Nov 2019 14:13:28 +0200 Subject: [PATCH 2071/2437] MC-22031: Admin: Product URL Management --- .../Model/AbstractUrlRewriteTest.php | 134 ++++++++ .../Model/CategoryUrlRewriteTest.php | 219 ++++++------- .../Model/ProductUrlRewriteTest.php | 304 ++++++++++++++++++ 3 files changed, 532 insertions(+), 125 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php new file mode 100644 index 0000000000000..bec07cc7cd5ec --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use PHPUnit\Framework\TestCase; + +/** + * Base class for url rewrites tests logic + * + * @magentoDbIsolation enabled + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +abstract class AbstractUrlRewriteTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var StoreRepositoryInterface */ + protected $storeRepository; + + /** @var ScopeConfigInterface */ + protected $config; + + /** @var UrlRewriteCollectionFactory */ + protected $urlRewriteCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); + $this->config = $this->objectManager->get(ScopeConfigInterface::class); + $this->urlRewriteCollectionFactory = $this->objectManager->get(UrlRewriteCollectionFactory::class); + } + + /** + * Retrieve all rewrite ids + * + * @return array + */ + protected function getAllRewriteIds(): array + { + $urlRewriteCollection = $this->urlRewriteCollectionFactory->create(); + + return $urlRewriteCollection->getAllIds(); + } + + /** + * Check that actual data contains of expected values + * + * @param UrlRewriteCollection $collection + * @param array $expectedData + * @return void + */ + protected function assertRewrites(UrlRewriteCollection $collection, array $expectedData): void + { + $collectionItems = $collection->toArray()['items']; + $this->assertTrue(count($collectionItems) === count($expectedData)); + foreach ($expectedData as $expectedItem) { + $found = false; + foreach ($collectionItems as $item) { + $found = array_intersect_assoc($item, $expectedItem) == $expectedItem; + if ($found) { + break; + } + } + $this->assertTrue($found, 'The actual data does not contains of expected values'); + } + } + + /** + * Get category url rewrites collection + * + * @param string|array $entityId + * @return UrlRewriteCollection + */ + protected function getEntityRewriteCollection($entityId): UrlRewriteCollection + { + $condition = is_array($entityId) ? ['in' => $entityId] : $entityId; + $entityRewriteCollection = $this->urlRewriteCollectionFactory->create(); + $entityRewriteCollection->addFieldToFilter(UrlRewrite::ENTITY_ID, $condition) + ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => $this->getEntityType()]); + + return $entityRewriteCollection; + } + + /** + * Prepare expected data + * + * @param array $expectedData + * @param int|null $id + * @return array + */ + protected function prepareData(array $expectedData, ?int $id = null): array + { + $newData = []; + foreach ($expectedData as $key => $expectedItem) { + $newData[$key] = str_replace(['%suffix%', '%id%'], [$this->getUrlSuffix(), $id], $expectedItem); + } + + return $newData; + } + + /** + * Get entity type + * + * @return string + */ + abstract protected function getEntityType(): string; + + /** + * Get config value for url suffix + * + * @return string + */ + abstract protected function getUrlSuffix(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index 32c5e5b5bfc63..10d605537db31 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -9,23 +9,17 @@ use Magento\Catalog\Api\CategoryLinkManagementInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\CategoryFactory as CategoryResourceFactory; use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Model\ScopeInterface; -use Magento\TestFramework\Helper\Bootstrap; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\OptionProvider; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; -use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use PHPUnit\Framework\TestCase; /** * Class for category url rewrites tests @@ -34,17 +28,8 @@ * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CategoryUrlRewriteTest extends TestCase +class CategoryUrlRewriteTest extends AbstractUrlRewriteTest { - /** @var ObjectManagerInterface */ - private $objectManager; - - /** @var CategoryFactory */ - private $categoryFactory; - - /** @var UrlRewriteCollectionFactory */ - private $urlRewriteCollectionFactory; - /** @var CategoryRepositoryInterface */ private $categoryRepository; @@ -52,16 +37,13 @@ class CategoryUrlRewriteTest extends TestCase private $categoryResourceFactory; /** @var CategoryLinkManagementInterface */ - private $categoryLinkManagment; + private $categoryLinkManagement; - /** @var ProductRepositoryInterface */ - private $productRepository; - - /** @var StoreRepositoryInterface */ - private $storeRepository; + /** @var CategoryFactory */ + private $categoryFactory; - /** @var ScopeConfigInterface */ - private $config; + /** @var string */ + private $suffix; /** * @inheritdoc @@ -70,15 +52,14 @@ protected function setUp() { parent::setUp(); - $this->objectManager = Bootstrap::getObjectManager(); - $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); - $this->urlRewriteCollectionFactory = $this->objectManager->get(UrlRewriteCollectionFactory::class); $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); $this->categoryResourceFactory = $this->objectManager->get(CategoryResourceFactory::class); - $this->categoryLinkManagment = $this->objectManager->create(CategoryLinkManagementInterface::class); - $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); - $this->config = $this->objectManager->get(ScopeConfigInterface::class); + $this->categoryLinkManagement = $this->objectManager->create(CategoryLinkManagementInterface::class); + $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); + $this->suffix = $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); } /** @@ -89,22 +70,13 @@ protected function setUp() */ public function testUrlRewriteOnCategorySave(array $data): void { - $categoryModel = $this->categoryFactory->create(); - $categoryModel->isObjectNew(true); - $categoryModel->setData($data['data']); - $categoryResource = $this->categoryResourceFactory->create(); - $categoryResource->save($categoryModel); + $categoryModel = $this->resourceSaveCategoryWithData($data['data']); $this->assertNotNull($categoryModel->getId(), 'The category was not created'); - $urlRewriteCollection = $this->getCategoryRewriteCollection($categoryModel->getId()); - foreach ($urlRewriteCollection as $item) { - foreach ($data['expected_data'] as $field => $expectedItem) { - $this->assertEquals( - sprintf($expectedItem, $categoryModel->getId()), - $item[$field], - 'The expected data does not match actual value' - ); - } - } + $urlRewriteCollection = $this->getEntityRewriteCollection($categoryModel->getId()); + $this->assertRewrites( + $urlRewriteCollection, + $this->prepareData($data['expected_data'], (int)$categoryModel->getId()) + ); } /** @@ -123,8 +95,10 @@ public function categoryProvider(): array 'is_active' => true, ], 'expected_data' => [ - 'request_path' => 'test-category.html', - 'target_path' => 'catalog/category/view/id/%s', + [ + 'request_path' => 'test-category%suffix%', + 'target_path' => 'catalog/category/view/id/%id%', + ], ], ], ], @@ -138,8 +112,10 @@ public function categoryProvider(): array 'is_active' => true, ], 'expected_data' => [ - 'request_path' => 'category-1/test-sub-category.html', - 'target_path' => 'catalog/category/view/id/%s', + [ + 'request_path' => 'category-1/test-sub-category%suffix%', + 'target_path' => 'catalog/category/view/id/%id%', + ], ], ], ], @@ -156,9 +132,11 @@ public function categoryProvider(): array public function testCategoryProductUrlRewrite(array $data): void { $category = $this->categoryRepository->get(402); - $this->categoryLinkManagment->assignProductToCategories('simple2', [$category->getId()]); - $productRewriteCollection = $this->getProductRewriteCollection(array_keys($category->getParentCategories())); - $this->assertRewrites($productRewriteCollection, $data); + $this->categoryLinkManagement->assignProductToCategories('simple2', [$category->getId()]); + $productRewriteCollection = $this->getCategoryProductRewriteCollection( + array_keys($category->getParentCategories()) + ); + $this->assertRewrites($productRewriteCollection, $this->prepareData($data)); } /** @@ -170,15 +148,15 @@ public function productRewriteProvider(): array [ [ [ - 'request_path' => 'category-1/category-1-1/category-1-1-1/simple-product2.html', + 'request_path' => 'category-1/category-1-1/category-1-1-1/simple-product2%suffix%', 'target_path' => 'catalog/product/view/id/6/category/402', ], [ - 'request_path' => 'category-1/simple-product2.html', + 'request_path' => 'category-1/simple-product2%suffix%', 'target_path' => 'catalog/product/view/id/6/category/400', ], [ - 'request_path' => 'category-1/category-1-1/simple-product2.html', + 'request_path' => 'category-1/category-1-1/simple-product2%suffix%', 'target_path' => 'catalog/product/view/id/6/category/401', ], ], @@ -197,10 +175,7 @@ public function testUrlRewriteOnCategorySaveWithExistingUrlKey(array $data): voi { $this->expectException(UrlAlreadyExistsException::class); $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); - $category = $this->categoryFactory->create(); - $category->setData($data); - $categoryResource = $this->categoryResourceFactory->create(); - $categoryResource->save($category); + $this->resourceSaveCategoryWithData($data); } /** @@ -262,10 +237,12 @@ public function testUrlRewriteOnCategoryMove(array $data): void $categoryId = $data['data']['id']; $category = $this->categoryRepository->get($categoryId); $category->move($data['data']['pid'], $data['data']['aid']); - $productRewriteCollection = $this->getProductRewriteCollection(array_keys($category->getParentCategories())); - $categoryRewriteCollection = $this->getCategoryRewriteCollection($categoryId); - $this->assertRewrites($categoryRewriteCollection, $data['expected_data']['category']); - $this->assertRewrites($productRewriteCollection, $data['expected_data']['product']); + $productRewriteCollection = $this->getCategoryProductRewriteCollection( + array_keys($category->getParentCategories()) + ); + $categoryRewriteCollection = $this->getEntityRewriteCollection($categoryId); + $this->assertRewrites($categoryRewriteCollection, $this->prepareData($data['expected_data']['category'])); + $this->assertRewrites($productRewriteCollection, $this->prepareData($data['expected_data']['product'])); } /** @@ -285,21 +262,21 @@ public function categoryMoveProvider(): array 'category' => [ [ 'request_path' => 'category-1.html', - 'target_path' => 'category-with-slash-symbol/category-1.html', + 'target_path' => 'category-with-slash-symbol/category-1%suffix%', 'redirect_type' => OptionProvider::PERMANENT, ], [ - 'request_path' => 'category-with-slash-symbol/category-1.html', + 'request_path' => 'category-with-slash-symbol/category-1%suffix%', 'target_path' => 'catalog/category/view/id/333', ], ], 'product' => [ [ - 'request_path' => 'category-with-slash-symbol/simple-product-three.html', + 'request_path' => 'category-with-slash-symbol/simple-product-three%suffix%', 'target_path' => 'catalog/product/view/id/333/category/3331', ], [ - 'request_path' => 'category-with-slash-symbol/category-1/simple-product-three.html', + 'request_path' => 'category-with-slash-symbol/category-1/simple-product-three%suffix%', 'target_path' => 'catalog/product/view/id/333/category/333', ], ], @@ -316,7 +293,7 @@ public function categoryMoveProvider(): array public function testUrlRewritesAfterCategoryDelete(): void { $categoryId = 333; - $categoryItemIds = $this->getCategoryRewriteCollection($categoryId)->getAllIds(); + $categoryItemIds = $this->getEntityRewriteCollection($categoryId)->getAllIds(); $this->categoryRepository->deleteByIdentifier($categoryId); $this->assertEmpty( array_intersect($this->getAllRewriteIds(), $categoryItemIds), @@ -333,8 +310,8 @@ public function testUrlRewritesAfterCategoryWithProductsDelete(): void { $category = $this->categoryRepository->get(3); $childIds = explode(',', $category->getAllChildren()); - $productRewriteIds = $this->getProductRewriteCollection($childIds)->getAllIds(); - $categoryItemIds = $this->getCategoryRewriteCollection($childIds)->getAllIds(); + $productRewriteIds = $this->getCategoryProductRewriteCollection($childIds)->getAllIds(); + $categoryItemIds = $this->getEntityRewriteCollection($childIds)->getAllIds(); $this->categoryRepository->deleteByIdentifier($category->getId()); $allIds = $this->getAllRewriteIds(); $this->assertEmpty( @@ -363,11 +340,12 @@ public function testCategoryUrlRewritePerStoreViews(): void $categoryId = 333; $category = $this->categoryRepository->get($categoryId); $urlKeyFirstStore = $category->getUrlKey(); - $category->setStoreId($secondStoreId); - $category->setUrlKey($urlKeySecondStore); - $categoryResource = $this->categoryResourceFactory->create(); - $categoryResource->save($category); - $urlRewriteItems = $this->getCategoryRewriteCollection($categoryId)->getItems(); + $this->resourceSaveCategoryWithData( + ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], + $category + ); + $urlRewriteItems = $this->getEntityRewriteCollection($categoryId)->getItems(); + $this->assertTrue(count($urlRewriteItems) == 2); foreach ($urlRewriteItems as $item) { $item->getData('store_id') == $secondStoreId ? $this->assertEquals($urlKeySecondStore . $urlSuffix, $item->getRequestPath()) @@ -376,74 +354,65 @@ public function testCategoryUrlRewritePerStoreViews(): void } /** - * Get products url rewrites collection referred to categories - * - * @param string|array $categoryId - * @return UrlRewriteCollection + * @inheritdoc */ - private function getProductRewriteCollection($categoryId): UrlRewriteCollection + protected function getUrlSuffix(): string { - $condition = is_array($categoryId) ? ['in' => $categoryId] : $categoryId; - $productRewriteCollection = $this->urlRewriteCollectionFactory->create(); - $productRewriteCollection - ->join( - ['p' => Product::TABLE_NAME], - 'main_table.url_rewrite_id = p.url_rewrite_id', - 'category_id' - ) - ->addFieldToFilter('category_id', $condition) - ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataProductUrlRewriteDatabaseMap::ENTITY_TYPE]); - - return $productRewriteCollection; + return $this->suffix; } /** - * Retrieve all rewrite ids - * - * @return array + * @inheritdoc */ - private function getAllRewriteIds(): array + protected function getEntityType(): string { - $urlRewriteCollection = $this->urlRewriteCollectionFactory->create(); + return DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE; + } - return $urlRewriteCollection->getAllIds(); + /** + * @inheritdoc + */ + protected function getEntityFactory() + { + return $this->categoryFactory; } /** - * Get category url rewrites collection + * Save product with data using resource model directly * - * @param string|array $categoryId - * @return UrlRewriteCollection + * @param array $data + * @param CategoryInterface|null $category + * @return CategoryInterface */ - private function getCategoryRewriteCollection($categoryId): UrlRewriteCollection + private function resourceSaveCategoryWithData(array $data, $category = null): CategoryInterface { - $condition = is_array($categoryId) ? ['in' => $categoryId] : $categoryId; - $categoryRewriteCollection = $this->urlRewriteCollectionFactory->create(); - $categoryRewriteCollection->addFieldToFilter(UrlRewrite::ENTITY_ID, $condition) - ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE]); + $category = $category ?: $this->categoryFactory->create(); + $category->addData($data); + $categoryResource = $this->categoryResourceFactory->create(); + $categoryResource->save($category); - return $categoryRewriteCollection; + return $category; } /** - * Check that actual data contains of expected values + * Get products url rewrites collection referred to categories * - * @param UrlRewriteCollection $collection - * @param array $expectedData - * @return void + * @param string|array $categoryId + * @return UrlRewriteCollection */ - private function assertRewrites(UrlRewriteCollection $collection, array $expectedData): void + private function getCategoryProductRewriteCollection($categoryId): UrlRewriteCollection { - $collectionItems = $collection->toArray()['items']; - foreach ($collectionItems as $item) { - $found = false; - foreach ($expectedData as $expectedItem) { - $found = array_intersect_assoc($item, $expectedItem) == $expectedItem; - if ($found) { - break; - } - } - $this->assertTrue($found, 'The actual data does not contains of expected values'); - } + $condition = is_array($categoryId) ? ['in' => $categoryId] : $categoryId; + $productRewriteCollection = $this->urlRewriteCollectionFactory->create(); + $productRewriteCollection + ->join( + ['p' => Product::TABLE_NAME], + 'main_table.url_rewrite_id = p.url_rewrite_id', + 'category_id' + ) + ->addFieldToFilter('category_id', $condition) + ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataProductUrlRewriteDatabaseMap::ENTITY_TYPE]); + + return $productRewriteCollection; } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php new file mode 100644 index 0000000000000..e1a79334117d9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -0,0 +1,304 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Catalog\Model\ResourceModel\ProductFactory as ProductResourceFactory; +use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; +use Magento\Store\Model\ScopeInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; +use Magento\UrlRewrite\Model\OptionProvider; + +/** + * Class for product url rewrites tests + * + * @magentoDbIsolation enabled + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductUrlRewriteTest extends AbstractUrlRewriteTest +{ + /** @var ProductFactory */ + private $productFactory; + + /** @var string */ + private $suffix; + + /** @var ProductResourceFactory */ + private $productResourceFactory; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productResourceFactory = $this->objectManager->create(ProductResourceFactory::class); + $this->productFactory = $this->objectManager->get(ProductFactory::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->suffix = $this->config->getValue( + ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * @dataProvider productDataProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnProductSave(array $data): void + { + $product = $this->resourceSaveProductWithData($data['data']); + $this->assertNotNull($product->getId(), 'The product was not created'); + $productUrlRewriteCollection = $this->getEntityRewriteCollection($product->getId()); + $this->assertRewrites( + $productUrlRewriteCollection, + $this->prepareData($data['expected_data'], (int)$product->getId()) + ); + } + + /** + * @return array + */ + public function productDataProvider(): array + { + return [ + 'without_url_key' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'name' => 'test product', + 'price' => 150, + ], + 'expected_data' => [ + [ + 'request_path' => 'test-product%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + ], + ], + ], + 'with_url_key' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'name' => 'test product', + 'price' => 150, + 'url_key' => 'test-product-url-key', + ], + 'expected_data' => [ + [ + 'request_path' => 'test-product-url-key%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + ], + ], + ], + 'with_invisible_product' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'name' => 'test product', + 'price' => 150, + 'url_key' => 'test-product-url-key', + ], + 'expected_data' => [], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_simple.php + * @dataProvider productEditProvider + * @param array $expectedData + * @return void + */ + public function testUrlRewriteOnProductEdit(array $expectedData): void + { + $product = $this->productRepository->get('simple'); + $data = [ + 'url_key' => 'new-url-key', + 'url_key_create_redirect' => $product->getUrlKey(), + 'save_rewrites_history' => true, + ]; + $product = $this->resourceSaveProductWithData($data, $product); + $productRewriteCollection = $this->getEntityRewriteCollection($product->getId()); + $this->assertRewrites( + $productRewriteCollection, + $this->prepareData($expectedData, (int)$product->getId()) + ); + } + + /** + * @return array + */ + public function productEditProvider(): array + { + return [ + [ + 'expected_data' => [ + [ + 'request_path' => 'new-url-key%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + [ + 'request_path' => 'simple-product%suffix%', + 'target_path' => 'new-url-key%suffix%', + 'redirect_type' => OptionProvider::PERMANENT, + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/category_with_products.php + * @dataProvider existingUrlKeyProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnProductSaveWithExistingUrlKey(array $data): void + { + $this->expectException(UrlAlreadyExistsException::class); + $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); + $this->resourceSaveProductWithData($data); + } + + /** + * @return array + */ + public function existingUrlKeyProvider(): array + { + return [ + [ + 'with_specified_existing_product_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'test-simple-product', + 'price' => 150, + 'url_key' => 'simple-product', + ], + 'with_autogenerated_existing_product_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'simple product', + 'price' => 150, + ], + 'with_specified_existing_category_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'test-simple-product', + 'price' => 150, + 'url_key' => 'category-1', + ], + 'with_autogenerated_existing_category_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'category 1', + 'price' => 150, + ], + ], + ]; + } + + /** + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ + public function testUrlRewritesAfterProductDelete(): void + { + $product = $this->productRepository->get('simple2'); + $rewriteIds = $this->getEntityRewriteCollection($product->getId())->getAllIds(); + $this->productRepository->delete($product); + $this->assertEmpty( + array_intersect($this->getAllRewriteIds(), $rewriteIds), + 'Not all expected category url rewrites were deleted' + ); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductUrlRewritePerStoreViews(): void + { + $urlKeySecondStore = 'url-key-for-second-store'; + $secondStoreId = $this->storeRepository->get('fixture_second_store')->getId(); + $product = $this->productRepository->get('simple2'); + $urlKeyFirstStore = $product->getUrlKey(); + $product = $this->resourceSaveProductWithData( + ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], + $product + ); + $urlRewriteItems = $this->getEntityRewriteCollection($product->getId())->getItems(); + $this->assertTrue(count($urlRewriteItems) == 2); + foreach ($urlRewriteItems as $item) { + $item->getData('store_id') == $secondStoreId + ? $this->assertEquals($urlKeySecondStore . $this->suffix, $item->getRequestPath()) + : $this->assertEquals($urlKeyFirstStore . $this->suffix, $item->getRequestPath()); + } + } + + /** + * Save product with data using resource model directly + * + * @param array $data + * @param ProductInterface|null $product + * @return ProductInterface + */ + protected function resourceSaveProductWithData(array $data, $product = null): ProductInterface + { + $product = $product ?: $this->productFactory->create(); + $product->addData($data); + $productResource = $this->productResourceFactory->create(); + $productResource->save($product); + + return $product; + } + + /** + * @inheritdoc + */ + protected function getUrlSuffix(): string + { + return $this->suffix; + } + + /** + * @inheritdoc + */ + protected function getEntityType(): string + { + return DataProductUrlRewriteDatabaseMap::ENTITY_TYPE; + } +} From f0ed1c48e28245898194beb5077905292af10077 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 15 Nov 2019 14:20:03 +0200 Subject: [PATCH 2072/2437] MC-22031: Admin: Product URL Management --- .../Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php | 1 - .../Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php | 1 - .../Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php | 1 - 3 files changed, 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php index bec07cc7cd5ec..251a79f46e38c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php @@ -21,7 +21,6 @@ * * @magentoDbIsolation enabled * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractUrlRewriteTest extends TestCase { diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index 10d605537db31..6efbb7d99fcde 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -26,7 +26,6 @@ * * @magentoDbIsolation enabled * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategoryUrlRewriteTest extends AbstractUrlRewriteTest { diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php index e1a79334117d9..2964eb25a8f3b 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -23,7 +23,6 @@ * * @magentoDbIsolation enabled * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductUrlRewriteTest extends AbstractUrlRewriteTest { From a3c67daa340a018854e1f3118d98f6400bb07b88 Mon Sep 17 00:00:00 2001 From: Edward Simpson <edward@skywire.co.uk> Date: Fri, 15 Nov 2019 13:23:23 +0000 Subject: [PATCH 2073/2437] Magento_Ui messages js - Using .bind to remove the need for self --- app/code/Magento/Ui/view/frontend/web/js/view/messages.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js index 735c3219c8136..b34eea5aa226d 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js @@ -65,13 +65,11 @@ define([ * @param {Boolean} isHidden */ onHiddenChange: function (isHidden) { - var self = this; - // Hide message block if needed if (isHidden) { setTimeout(function () { - $(self.selector).hide('blind', {}, self.hideSpeed); - }, self.hideTimeout); + $(this.selector).hide('blind', {}, this.hideSpeed); + }.bind(this), this.hideTimeout); } } }); From a565ee001e76ed3b5d53b7b21ed8dd637c1fb71b Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 15 Nov 2019 15:57:23 +0200 Subject: [PATCH 2074/2437] Remove duplicated hide() method --- .../Ui/view/base/web/js/grid/columns/image-preview.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index cf597f8d3a543..372ab0e093727 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -101,13 +101,12 @@ define([ show: function (record) { var img; + this.hide(); + if (record._rowIndex === this.visibleRecord()) { - this.hide(); - return; } - - this.hide(); + this.displayedRecord(record); this._selectRow(record.rowNumber || null); this.visibleRecord(record._rowIndex); From 0ef20cfe912493e585c5a7c2aa7e24a52c83bd3a Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 15 Nov 2019 16:30:20 +0200 Subject: [PATCH 2075/2437] MC-22031: Admin: Product URL Management --- .../Model/CategoryUrlRewriteTest.php | 21 +++++++++---------- .../Model/ProductUrlRewriteTest.php | 21 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index 6efbb7d99fcde..b50f48f00e473 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -11,7 +11,7 @@ use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\CategoryFactory; -use Magento\Catalog\Model\ResourceModel\CategoryFactory as CategoryResourceFactory; +use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; @@ -32,8 +32,8 @@ class CategoryUrlRewriteTest extends AbstractUrlRewriteTest /** @var CategoryRepositoryInterface */ private $categoryRepository; - /** @var CategoryResourceFactory */ - private $categoryResourceFactory; + /** @var CategoryResource */ + private $categoryResource; /** @var CategoryLinkManagementInterface */ private $categoryLinkManagement; @@ -52,7 +52,7 @@ protected function setUp() parent::setUp(); $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); - $this->categoryResourceFactory = $this->objectManager->get(CategoryResourceFactory::class); + $this->categoryResource = $this->objectManager->get(CategoryResource::class); $this->categoryLinkManagement = $this->objectManager->create(CategoryLinkManagementInterface::class); $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); $this->suffix = $this->config->getValue( @@ -69,7 +69,7 @@ protected function setUp() */ public function testUrlRewriteOnCategorySave(array $data): void { - $categoryModel = $this->resourceSaveCategoryWithData($data['data']); + $categoryModel = $this->saveCategory($data['data']); $this->assertNotNull($categoryModel->getId(), 'The category was not created'); $urlRewriteCollection = $this->getEntityRewriteCollection($categoryModel->getId()); $this->assertRewrites( @@ -174,7 +174,7 @@ public function testUrlRewriteOnCategorySaveWithExistingUrlKey(array $data): voi { $this->expectException(UrlAlreadyExistsException::class); $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); - $this->resourceSaveCategoryWithData($data); + $this->saveCategory($data); } /** @@ -339,7 +339,7 @@ public function testCategoryUrlRewritePerStoreViews(): void $categoryId = 333; $category = $this->categoryRepository->get($categoryId); $urlKeyFirstStore = $category->getUrlKey(); - $this->resourceSaveCategoryWithData( + $this->saveCategory( ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], $category ); @@ -383,12 +383,11 @@ protected function getEntityFactory() * @param CategoryInterface|null $category * @return CategoryInterface */ - private function resourceSaveCategoryWithData(array $data, $category = null): CategoryInterface + private function saveCategory(array $data, $category = null): CategoryInterface { $category = $category ?: $this->categoryFactory->create(); $category->addData($data); - $categoryResource = $this->categoryResourceFactory->create(); - $categoryResource->save($category); + $this->categoryResource->save($category); return $category; } @@ -405,7 +404,7 @@ private function getCategoryProductRewriteCollection($categoryId): UrlRewriteCol $productRewriteCollection = $this->urlRewriteCollectionFactory->create(); $productRewriteCollection ->join( - ['p' => Product::TABLE_NAME], + ['p' => $this->categoryResource->getTable(Product::TABLE_NAME)], 'main_table.url_rewrite_id = p.url_rewrite_id', 'category_id' ) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php index 2964eb25a8f3b..f8fe68c2e0a2d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -12,7 +12,7 @@ use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ProductFactory; -use Magento\Catalog\Model\ResourceModel\ProductFactory as ProductResourceFactory; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; use Magento\Store\Model\ScopeInterface; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; @@ -32,8 +32,8 @@ class ProductUrlRewriteTest extends AbstractUrlRewriteTest /** @var string */ private $suffix; - /** @var ProductResourceFactory */ - private $productResourceFactory; + /** @var ProductResource */ + private $productResource; /** @var ProductRepositoryInterface */ private $productRepository; @@ -45,7 +45,7 @@ protected function setUp() { parent::setUp(); - $this->productResourceFactory = $this->objectManager->create(ProductResourceFactory::class); + $this->productResource = $this->objectManager->create(ProductResource::class); $this->productFactory = $this->objectManager->get(ProductFactory::class); $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $this->suffix = $this->config->getValue( @@ -61,7 +61,7 @@ protected function setUp() */ public function testUrlRewriteOnProductSave(array $data): void { - $product = $this->resourceSaveProductWithData($data['data']); + $product = $this->saveProduct($data['data']); $this->assertNotNull($product->getId(), 'The product was not created'); $productUrlRewriteCollection = $this->getEntityRewriteCollection($product->getId()); $this->assertRewrites( @@ -144,7 +144,7 @@ public function testUrlRewriteOnProductEdit(array $expectedData): void 'url_key_create_redirect' => $product->getUrlKey(), 'save_rewrites_history' => true, ]; - $product = $this->resourceSaveProductWithData($data, $product); + $product = $this->saveProduct($data, $product); $productRewriteCollection = $this->getEntityRewriteCollection($product->getId()); $this->assertRewrites( $productRewriteCollection, @@ -184,7 +184,7 @@ public function testUrlRewriteOnProductSaveWithExistingUrlKey(array $data): void { $this->expectException(UrlAlreadyExistsException::class); $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); - $this->resourceSaveProductWithData($data); + $this->saveProduct($data); } /** @@ -255,7 +255,7 @@ public function testProductUrlRewritePerStoreViews(): void $secondStoreId = $this->storeRepository->get('fixture_second_store')->getId(); $product = $this->productRepository->get('simple2'); $urlKeyFirstStore = $product->getUrlKey(); - $product = $this->resourceSaveProductWithData( + $product = $this->saveProduct( ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], $product ); @@ -275,12 +275,11 @@ public function testProductUrlRewritePerStoreViews(): void * @param ProductInterface|null $product * @return ProductInterface */ - protected function resourceSaveProductWithData(array $data, $product = null): ProductInterface + protected function saveProduct(array $data, $product = null): ProductInterface { $product = $product ?: $this->productFactory->create(); $product->addData($data); - $productResource = $this->productResourceFactory->create(); - $productResource->save($product); + $this->productResource->save($product); return $product; } From b30b855668313aa55c00156927e9392723795195 Mon Sep 17 00:00:00 2001 From: Nazarn96 <nazarn96@gmail.com> Date: Fri, 15 Nov 2019 16:33:16 +0200 Subject: [PATCH 2076/2437] Fix static test --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 372ab0e093727..57f16dc0c1a02 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -102,11 +102,11 @@ define([ var img; this.hide(); - + if (record._rowIndex === this.visibleRecord()) { return; } - + this.displayedRecord(record); this._selectRow(record.rowNumber || null); this.visibleRecord(record._rowIndex); From 6e2b738a8e5b56d4c5cce608c73ce7d21713d797 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 15 Nov 2019 11:45:41 -0600 Subject: [PATCH 2077/2437] MC-19926: Implement CSP --- .../Collector/CspWhitelistXmlCollector.php | 1 - .../Magento/Csp/Model/Mode/ConfigManager.php | 1 - .../Magento/Csp/Model/Policy/FetchPolicy.php | 1 + .../Renderer/SimplePolicyHeaderRenderer.php | 5 - .../Csp/Model/Policy/SandboxPolicy.php | 3 + app/code/Magento/Csp/composer.json | 2 +- .../Model/Collector/ConfigCollectorTest.php | 284 ++++-------------- 7 files changed, 65 insertions(+), 232 deletions(-) diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php index 66eb3747bce3e..f4a75f120dab9 100644 --- a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php @@ -51,7 +51,6 @@ public function collect(array $defaultPolicies = []): array ); } - return $policies; } } diff --git a/app/code/Magento/Csp/Model/Mode/ConfigManager.php b/app/code/Magento/Csp/Model/Mode/ConfigManager.php index 874df5e0d226f..9f10154604d5f 100644 --- a/app/code/Magento/Csp/Model/Mode/ConfigManager.php +++ b/app/code/Magento/Csp/Model/Mode/ConfigManager.php @@ -62,7 +62,6 @@ public function getConfigured(): ModeConfiguredInterface throw new \RuntimeException('CSP can only be configured for storefront or admin area'); } - $reportOnly = $this->config->isSetFlag( 'csp/mode/' . $configArea .'/report_only', ScopeInterface::SCOPE_STORE, diff --git a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php index 87e95d5818004..76d17e3cb96c7 100644 --- a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php +++ b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php @@ -93,6 +93,7 @@ class FetchPolicy implements SimplePolicyInterface * @param string[] $nonceValues * @param string[] $hashValues * @param bool $dynamicAllowed + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( string $id, diff --git a/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php index b4fd418e89635..14ae23eb3fe37 100644 --- a/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php +++ b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php @@ -24,11 +24,6 @@ class SimplePolicyHeaderRenderer implements PolicyRendererInterface */ private $modeConfig; - /** - * @var ModeConfiguredInterface - */ - private $config; - /** * @param ModeConfigManagerInterface $modeConfig */ diff --git a/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php index 0aeb221e79ca6..33e3b06f56aec 100644 --- a/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php +++ b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php @@ -81,6 +81,7 @@ class SandboxPolicy implements SimplePolicyInterface * @param bool $scriptsAllowed * @param bool $topNavigationAllowed * @param bool $topNavigationByUserActivationAllowed + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( bool $formAllowed, @@ -228,6 +229,8 @@ public function getId(): string /** * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function getValue(): string { diff --git a/app/code/Magento/Csp/composer.json b/app/code/Magento/Csp/composer.json index a18fcf21dcca2..91146b00f3f76 100644 --- a/app/code/Magento/Csp/composer.json +++ b/app/code/Magento/Csp/composer.json @@ -6,7 +6,7 @@ }, "require": { "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*", + "magento/framework": "*" }, "type": "magento2-module", "license": [ diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php index db67b322ebd5d..ae8013d065194 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -33,6 +33,55 @@ public function setUp() $this->collector = Bootstrap::getObjectManager()->get(ConfigCollector::class); } + /** + * Create expected policy objects. + * + * @return PolicyInterface[] + */ + private function getExpectedPolicies(): array + { + return [ + 'child-src' => new FetchPolicy( + 'child-src', + false, + ['http://magento.com', 'http://devdocs.magento.com'], + ['http'], + true, + true, + false, + [], + [], + true + ), + 'child-src2' => new FetchPolicy('child-src', false, [], [], false, false, true), + 'connect-src' => new FetchPolicy('connect-src'), + 'default-src' => new FetchPolicy( + 'default-src', + false, + ['http://magento.com', 'http://devdocs.magento.com'], + [], + true + ), + 'font-src' => new FetchPolicy('font-src', false, [], [], true), + 'frame-src' => new FetchPolicy('frame-src', false, [], [], true, false, false, [], [], true), + 'img-src' => new FetchPolicy('img-src', false, [], [], true), + 'manifest-src' => new FetchPolicy('manifest-src', false, [], [], true), + 'media-src' => new FetchPolicy('media-src', false, [], [], true), + 'object-src' => new FetchPolicy('object-src', false, [], [], true), + 'script-src' => new FetchPolicy('script-src', false, [], [], true), + 'style-src' => new FetchPolicy('style-src', false, [], [], true), + 'base-uri' => new FetchPolicy('base-uri', false, [], [], true), + 'plugin-types' => new PluginTypesPolicy( + ['application/x-shockwave-flash', 'application/x-java-applet'] + ), + 'sandbox' => new SandboxPolicy(true, true, true, true, false, false, true, true, true, true, true), + 'form-action' => new FetchPolicy('form-action', false, [], [], true), + 'frame-ancestors' => new FetchPolicy('frame-ancestors', false, [], [], true), + 'block-all-mixed-content' => new FlagPolicy('block-all-mixed-content'), + 'upgrade-insecure-requests' => new FlagPolicy('upgrade-insecure-requests') + ]; + } + /** * Test initiating policies from config. * @@ -90,7 +139,7 @@ public function setUp() * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/none 0 * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/self 1 * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/policy_id plugin-types - * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/flash application/x-shockwave-flash + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/fl application/x-shockwave-flash * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/applet application/x-java-applet * @magentoConfigFixture default_store csp/policies/storefront/sandbox/policy_id sandbox * @magentoConfigFixture default_store csp/policies/storefront/sandbox/forms 1 @@ -111,233 +160,20 @@ public function setUp() public function testCollecting(): void { $policies = $this->collector->collect([]); - $childScrChecked = false; - $childScr2Checked = false; - $connectScrChecked = false; - $defaultScrChecked = false; - $fontScrChecked = false; - $frameScrChecked = false; - $imgScrChecked = false; - $manifestScrChecked = false; - $mediaScrChecked = false; - $objectScrChecked = false; - $scriptScrChecked = false; - $styleScrChecked = false; - $baseUriChecked = false; - $pluginTypesChecked = false; - $sandboxChecked = false; - $formActionChecked = false; - $frameAncestorsChecked = false; - $blockAllMixedChecked = false; - $upgradeChecked = false; + $checked = []; + $expectedPolicies = $this->getExpectedPolicies(); $this->assertNotEmpty($policies); - /** @var PolicyInterface|FetchPolicy|FlagPolicy|SandboxPolicy|PluginTypesPolicy $policy */ + /** @var PolicyInterface $policy */ foreach ($policies as $policy) { - switch ($policy->getId()) - { - case 'child-src': - if ($policy->isEvalAllowed()) { - $childScr2Checked = true; - } else { - $childScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == ['http://magento.com', 'http://devdocs.magento.com'] - && $policy->getSchemeSources() == ['http'] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && $policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && $policy->isInlineAllowed(); - } - break; - case 'connect-src': - $connectScrChecked = $policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && !$policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'default-src': - $defaultScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == ['http://magento.com', 'http://devdocs.magento.com'] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'font-src': - $fontScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'frame-src': - $frameScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && $policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'img-src': - $imgScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'manifest-src': - $manifestScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'media-src': - $mediaScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'object-src': - $objectScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'script-src': - $scriptScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'style-src': - $styleScrChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'base-uri': - $baseUriChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'plugin-types': - $pluginTypesChecked = $policy->getTypes() - == ['application/x-shockwave-flash', 'application/x-java-applet']; - break; - case 'sandbox': - $sandboxChecked = $policy->isFormAllowed() - && $policy->isModalsAllowed() - && $policy->isOrientationLockAllowed() - && $policy->isPointerLockAllowed() - && !$policy->isPopupsAllowed() - && !$policy->isPopupsToEscapeSandboxAllowed() - && $policy->isPresentationAllowed() - && $policy->isSameOriginAllowed() - && $policy->isScriptsAllowed() - && $policy->isTopNavigationAllowed() - && $policy->isTopNavigationByUserActivationAllowed(); - break; - case 'form-action': - $formActionChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'frame-ancestors': - $frameAncestorsChecked = !$policy->isNoneAllowed() - && $policy->getHostSources() == [] - && $policy->getSchemeSources() == [] - && $policy->isSelfAllowed() - && !$policy->isEvalAllowed() - && !$policy->isDynamicAllowed() - && $policy->getHashes() == [] - && $policy->getNonceValues() == [] - && !$policy->isInlineAllowed(); - break; - case 'block-all-mixed-content': - $blockAllMixedChecked = $policy instanceof FlagPolicy; - break; - case 'upgrade-insecure-requests': - $upgradeChecked = $policy instanceof FlagPolicy; - break; + $id = $policy->getId(); + if ($id === 'child-src' && $policy->isEvalAllowed()) { + $id = 'child-src2'; } + $this->assertEquals($expectedPolicies[$id], $policy); + $checked[] = $id; } - - $this->assertTrue($childScrChecked); - $this->assertTrue($childScr2Checked); - $this->assertTrue($connectScrChecked); - $this->assertTrue($defaultScrChecked); - $this->assertTrue($fontScrChecked); - $this->assertTrue($frameScrChecked); - $this->assertTrue($imgScrChecked); - $this->assertTrue($manifestScrChecked); - $this->assertTrue($mediaScrChecked); - $this->assertTrue($objectScrChecked); - $this->assertTrue($scriptScrChecked); - $this->assertTrue($styleScrChecked); - $this->assertTrue($baseUriChecked); - $this->assertTrue($pluginTypesChecked); - $this->assertTrue($sandboxChecked); - $this->assertTrue($formActionChecked); - $this->assertTrue($frameAncestorsChecked); - $this->assertTrue($blockAllMixedChecked); - $this->assertTrue($upgradeChecked); + $expectedIds = array_keys($expectedPolicies); + $this->assertEquals(sort($expectedIds), sort($checked)); } } From f28f7de0dad14421d3690d84d0ca393c3dbcc364 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 15 Nov 2019 11:55:02 -0600 Subject: [PATCH 2078/2437] MC-19926: Implement CSP --- composer.lock | 160 +++++++++++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/composer.lock b/composer.lock index f5e4fe783879e..6c26f5d30cc76 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "21394914b3f105a33f583ba59aeba748", + "content-hash": "1bdcad292b0fcfb4e3f669de79a8eee0", "packages": [ { "name": "braintree/braintree_php", @@ -1111,16 +1111,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.1", + "version": "1.25.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", "shasum": "" }, "require": { @@ -1185,7 +1185,7 @@ "logging", "psr-3" ], - "time": "2019-09-06T13:49:17+00:00" + "time": "2019-11-13T10:00:05+00:00" }, { "name": "paragonie/random_compat", @@ -1390,39 +1390,36 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v2.7.3", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f" + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { "ext-bcmath": "*", - "ext-mbstring": "*", - "php": ">=5.3.0" + "ext-sockets": "*", + "php": ">=5.6" }, "replace": { "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "phpdocumentor/phpdocumentor": "^2.9", - "phpunit/phpunit": "^4.8", - "scrutinizer/ocular": "^1.1", + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", "squizlabs/php_codesniffer": "^2.5" }, - "suggest": { - "ext-sockets": "Use AMQPSocketConnection" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.10-dev" } }, "autoload": { @@ -1448,6 +1445,11 @@ "name": "Raúl Araya", "email": "nubeiro@gmail.com", "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" } ], "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", @@ -1457,7 +1459,7 @@ "queue", "rabbitmq" ], - "time": "2018-04-30T03:54:54+00:00" + "time": "2019-10-10T13:23:40+00:00" }, { "name": "phpseclib/mcrypt_compat", @@ -2080,7 +2082,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2133,7 +2135,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2261,7 +2263,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2311,7 +2313,7 @@ }, { "name": "symfony/finder", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2477,7 +2479,7 @@ }, { "name": "symfony/process", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2723,23 +2725,23 @@ }, { "name": "wikimedia/less.php", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/wikimedia/less.php.git", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b" + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=7.2.9" }, "require-dev": { - "phpunit/phpunit": "~4.8.24" + "phpunit/phpunit": "7.5.14" }, "bin": [ "bin/lessc" @@ -2758,6 +2760,10 @@ "Apache-2.0" ], "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, { "name": "Matt Agar", "homepage": "https://github.com/agar" @@ -2765,10 +2771,6 @@ { "name": "Martin Jantošovič", "homepage": "https://github.com/Mordred" - }, - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" } ], "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", @@ -2780,7 +2782,7 @@ "php", "stylesheet" ], - "time": "2019-01-19T01:01:33+00:00" + "time": "2019-11-06T18:30:11+00:00" }, { "name": "zendframework/zend-captcha", @@ -6159,16 +6161,16 @@ }, { "name": "doctrine/cache", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a" + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c15dcd24b756f9e52ea7c3ae8227354f3628f11a", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a", + "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", "shasum": "" }, "require": { @@ -6238,7 +6240,7 @@ "riak", "xcache" ], - "time": "2019-11-11T10:31:52+00:00" + "time": "2019-11-15T14:31:57+00:00" }, { "name": "doctrine/inflector", @@ -6365,28 +6367,30 @@ }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -6399,14 +6403,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -6421,7 +6425,7 @@ "parser", "php" ], - "time": "2019-06-08T11:03:04+00:00" + "time": "2019-10-30T14:39:59+00:00" }, { "name": "facebook/webdriver", @@ -6615,16 +6619,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", "shasum": "" }, "require": { @@ -6633,13 +6637,11 @@ "require-dev": { "ext-intl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" + "squizlabs/php_codesniffer": "^2.9.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -6661,7 +6663,7 @@ "faker", "fixtures" ], - "time": "2018-07-12T10:23:15+00:00" + "time": "2019-11-14T13:13:06+00:00" }, { "name": "grasmash/expander", @@ -7667,16 +7669,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.3", + "version": "9.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696" + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1af08ca3861048a8bfb39d0405d0ac3e50ba2696", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", "shasum": "" }, "require": { @@ -7721,7 +7723,7 @@ "phpcs", "standards" ], - "time": "2019-11-11T03:25:23+00:00" + "time": "2019-11-15T04:12:02+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -9242,7 +9244,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -9301,7 +9303,7 @@ }, { "name": "symfony/config", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -9365,7 +9367,7 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", @@ -9438,7 +9440,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -9499,16 +9501,16 @@ }, { "name": "symfony/http-foundation", - "version": "v2.8.50", + "version": "v2.8.52", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a" + "reference": "3929d9fe8148d17819ad0178c748b8d339420709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/746f8d3638bf46ee8b202e62f2b214c3d61fb06a", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3929d9fe8148d17819ad0178c748b8d339420709", + "reference": "3929d9fe8148d17819ad0178c748b8d339420709", "shasum": "" }, "require": { @@ -9550,11 +9552,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-16T10:00:53+00:00" + "time": "2019-11-12T12:34:41+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -9894,7 +9896,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -9944,7 +9946,7 @@ }, { "name": "symfony/yaml", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", From 90f5bc5a186bcb5821cd3ac20df95bd99ba78abb Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 15 Nov 2019 13:44:29 -0600 Subject: [PATCH 2079/2437] MC-19926: Implement CSP --- app/code/Magento/Csp/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Csp/composer.json b/app/code/Magento/Csp/composer.json index 91146b00f3f76..cd8a47a92159d 100644 --- a/app/code/Magento/Csp/composer.json +++ b/app/code/Magento/Csp/composer.json @@ -6,7 +6,8 @@ }, "require": { "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-store": "*" }, "type": "magento2-module", "license": [ From b2912953e9940fd1d36600d0538ad6d95106c30c Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 15 Nov 2019 14:36:14 -0600 Subject: [PATCH 2080/2437] MC-21727: It is impossible to remove Related, Up-Sells and Cross-Sells products via the import procedure --- app/code/Magento/CatalogImportExport/Model/Import/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index e92bb95789eca..9becb57bba379 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -3090,7 +3090,7 @@ function ($linkName) use ($rowData) { foreach ($linkNameToId as $linkName => $linkId) { $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); //process empty value - if (!empty($linkSkus[0]) && $linkSkus[0] === Import::DEFAULT_EMPTY_ATTRIBUTE_VALUE_CONSTANT) { + if (!empty($linkSkus[0]) && $linkSkus[0] === $this->getEmptyAttributeValueConstant()) { $linksToDelete[$linkId][] = $productId; continue; } From dd27fff78f9ff9f6f05aab9e3c320acbecccbb70 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 15 Nov 2019 15:56:13 -0600 Subject: [PATCH 2081/2437] MC-19926: Implement CSP --- .../Framework/HTTP/PhpEnvironment/Response.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index 17d7482607622..db33b0feaf783 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -5,6 +5,8 @@ */ namespace Magento\Framework\HTTP\PhpEnvironment; +use Zend\Http\Header\GenericMultiHeader; + /** * Base HTTP response object */ @@ -77,11 +79,18 @@ public function setHeader($name, $value, $replace = false) { $value = (string)$value; + $headers = $this->getHeaders(); if ($replace) { + $headers->addHeaderLine($name, $value); $this->clearHeader($name); + } else { + //Zend framework will only force multiple headers for header objects + //extending MultiHeader interface. + $pluginKey = str_replace('-', '', mb_strtolower($name)); + $headers->getPluginClassLoader()->registerPlugin($pluginKey, GenericMultiHeader::class); + $headers->addHeader(new GenericMultiHeader($name, $value)); } - $this->getHeaders()->addHeaderLine($name, $value); return $this; } From f2dd874b312c82061e963bdf9ea985234c94538e Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Fri, 15 Nov 2019 16:02:25 -0600 Subject: [PATCH 2082/2437] MC-19926: Implement CSP --- composer.lock | 160 +++++++++++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/composer.lock b/composer.lock index 65e5fd8e5a42c..bc2a9cc59b01f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e75fa994f056960e832018efd6af5a40", + "content-hash": "d90898f5936c9a12214a556055caf8bc", "packages": [ { "name": "braintree/braintree_php", @@ -1111,16 +1111,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.1", + "version": "1.25.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", "shasum": "" }, "require": { @@ -1185,7 +1185,7 @@ "logging", "psr-3" ], - "time": "2019-09-06T13:49:17+00:00" + "time": "2019-11-13T10:00:05+00:00" }, { "name": "paragonie/random_compat", @@ -1390,39 +1390,36 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v2.7.3", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f" + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { "ext-bcmath": "*", - "ext-mbstring": "*", - "php": ">=5.3.0" + "ext-sockets": "*", + "php": ">=5.6" }, "replace": { "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "phpdocumentor/phpdocumentor": "^2.9", - "phpunit/phpunit": "^4.8", - "scrutinizer/ocular": "^1.1", + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", "squizlabs/php_codesniffer": "^2.5" }, - "suggest": { - "ext-sockets": "Use AMQPSocketConnection" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.10-dev" } }, "autoload": { @@ -1448,6 +1445,11 @@ "name": "Raúl Araya", "email": "nubeiro@gmail.com", "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" } ], "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", @@ -1457,7 +1459,7 @@ "queue", "rabbitmq" ], - "time": "2018-04-30T03:54:54+00:00" + "time": "2019-10-10T13:23:40+00:00" }, { "name": "phpseclib/mcrypt_compat", @@ -2080,7 +2082,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2133,7 +2135,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2261,7 +2263,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2311,7 +2313,7 @@ }, { "name": "symfony/finder", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2477,7 +2479,7 @@ }, { "name": "symfony/process", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2723,23 +2725,23 @@ }, { "name": "wikimedia/less.php", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/wikimedia/less.php.git", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b" + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=7.2.9" }, "require-dev": { - "phpunit/phpunit": "~4.8.24" + "phpunit/phpunit": "7.5.14" }, "bin": [ "bin/lessc" @@ -2758,6 +2760,10 @@ "Apache-2.0" ], "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, { "name": "Matt Agar", "homepage": "https://github.com/agar" @@ -2765,10 +2771,6 @@ { "name": "Martin Jantošovič", "homepage": "https://github.com/Mordred" - }, - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" } ], "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", @@ -2780,7 +2782,7 @@ "php", "stylesheet" ], - "time": "2019-01-19T01:01:33+00:00" + "time": "2019-11-06T18:30:11+00:00" }, { "name": "zendframework/zend-captcha", @@ -6159,16 +6161,16 @@ }, { "name": "doctrine/cache", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a" + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c15dcd24b756f9e52ea7c3ae8227354f3628f11a", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a", + "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", "shasum": "" }, "require": { @@ -6238,7 +6240,7 @@ "riak", "xcache" ], - "time": "2019-11-11T10:31:52+00:00" + "time": "2019-11-15T14:31:57+00:00" }, { "name": "doctrine/inflector", @@ -6365,28 +6367,30 @@ }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -6399,14 +6403,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -6421,7 +6425,7 @@ "parser", "php" ], - "time": "2019-06-08T11:03:04+00:00" + "time": "2019-10-30T14:39:59+00:00" }, { "name": "facebook/webdriver", @@ -6615,16 +6619,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", "shasum": "" }, "require": { @@ -6633,13 +6637,11 @@ "require-dev": { "ext-intl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" + "squizlabs/php_codesniffer": "^2.9.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -6661,7 +6663,7 @@ "faker", "fixtures" ], - "time": "2018-07-12T10:23:15+00:00" + "time": "2019-11-14T13:13:06+00:00" }, { "name": "grasmash/expander", @@ -7667,16 +7669,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.3", + "version": "9.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696" + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1af08ca3861048a8bfb39d0405d0ac3e50ba2696", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", "shasum": "" }, "require": { @@ -7721,7 +7723,7 @@ "phpcs", "standards" ], - "time": "2019-11-11T03:25:23+00:00" + "time": "2019-11-15T04:12:02+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -9242,7 +9244,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -9301,7 +9303,7 @@ }, { "name": "symfony/config", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -9365,7 +9367,7 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", @@ -9438,7 +9440,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -9499,16 +9501,16 @@ }, { "name": "symfony/http-foundation", - "version": "v2.8.50", + "version": "v2.8.52", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a" + "reference": "3929d9fe8148d17819ad0178c748b8d339420709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/746f8d3638bf46ee8b202e62f2b214c3d61fb06a", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3929d9fe8148d17819ad0178c748b8d339420709", + "reference": "3929d9fe8148d17819ad0178c748b8d339420709", "shasum": "" }, "require": { @@ -9550,11 +9552,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-16T10:00:53+00:00" + "time": "2019-11-12T12:34:41+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -9894,7 +9896,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -9944,7 +9946,7 @@ }, { "name": "symfony/yaml", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", From aff1ce097053f6d7adb3d807591001ed13c62848 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Fri, 15 Nov 2019 16:24:27 -0600 Subject: [PATCH 2083/2437] MC-23093: The initial option value for DHL is incorrect --- app/code/Magento/Dhl/Model/Carrier.php | 2 +- app/code/Magento/Dhl/etc/config.xml | 2 +- .../Magento/Dhl/Model/CarrierTest.php | 132 ++++++++++++++---- 3 files changed, 103 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 0890466e8a40f..ad76f5070b35b 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -940,7 +940,7 @@ protected function _getDimension($dimension, $configWeightUnit = false) ); } - return sprintf('%.3f', $dimension); + return round($dimension, 3); } /** diff --git a/app/code/Magento/Dhl/etc/config.xml b/app/code/Magento/Dhl/etc/config.xml index b46152fb0ecad..3408447e70650 100644 --- a/app/code/Magento/Dhl/etc/config.xml +++ b/app/code/Magento/Dhl/etc/config.xml @@ -32,7 +32,7 @@ <specificerrmsg>This shipping method is currently unavailable. If you would like to ship using this shipping method, please contact us.</specificerrmsg> <divide_order_weight>1</divide_order_weight> <unit_of_measure>K</unit_of_measure> - <size>R</size> + <size>0</size> <handling_type>F</handling_type> <handling_action>O</handling_action> <shipment_days>Mon,Tue,Wed,Thu,Fri</shipment_days> diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php index 69eab0656f89b..3a1df79179f64 100644 --- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -15,6 +15,7 @@ use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Shipment\Request; use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\HTTP\AsyncClientInterfaceMock; use Magento\Shipping\Model\Simplexml\Element as ShippingElement; @@ -411,7 +412,106 @@ private function getExpectedLabelRequestXml( */ public function testCollectRates() { - $requestData = [ + $requestData = $this->getRequestData(); + //phpcs:disable Magento2.Functions.DiscouragedFunction + $response = new Response( + 200, + [], + file_get_contents(__DIR__ . '/../_files/dhl_quote_response.xml') + ); + //phpcs:enable Magento2.Functions.DiscouragedFunction + $this->httpClient->nextResponses(array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response)); + /** @var RateRequest $request */ + $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $expectedRates = [ + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, 'method' => 'E', 'price' => 45.85], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'Q', 'price' => 35.26], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 37.38, 'method' => 'Y', 'price' => 37.38], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'P', 'price' => 35.26] + ]; + + $actualRates = $this->dhlCarrier->collectRates($request)->getAllRates(); + + self::assertEquals(count($expectedRates), count($actualRates)); + foreach ($actualRates as $i => $actualRate) { + $actualRate = $actualRate->getData(); + unset($actualRate['method_title']); + self::assertEquals($expectedRates[$i], $actualRate); + } + $requestXml = $this->httpClient->getLastRequest()->getBody(); + self::assertContains('<Weight>18.223</Weight>', $requestXml); + self::assertContains('<Height>0.63</Height>', $requestXml); + self::assertContains('<Width>0.63</Width>', $requestXml); + self::assertContains('<Depth>0.63</Depth>', $requestXml); + } + + /** + * Tests that quotes request doesn't contain dimensions when it shouldn't. + * + * @param string|null $size + * @param string|null $height + * @param string|null $width + * @param string|null $depth + * @magentoConfigFixture default_store carriers/dhl/active 1 + * @dataProvider collectRatesWithoutDimensionsDataProvider + */ + public function testCollectRatesWithoutDimensions(?string $size, ?string $height, ?string $width, ?string $depth) + { + $requestData = $this->getRequestData(); + $this->setDhlConfig(['size' => $size, 'height' => $height, 'width' => $width, 'depth' => $depth]); + + /** @var RateRequest $request */ + $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $this->dhlCarrier = Bootstrap::getObjectManager()->create(Carrier::class); + $this->dhlCarrier->collectRates($request)->getAllRates(); + + $requestXml = $this->httpClient->getLastRequest()->getBody(); + $this->assertNotContains('<Width>', $requestXml); + $this->assertNotContains('<Height>', $requestXml); + $this->assertNotContains('<Depth>', $requestXml); + } + + /** + * @return array + */ + public function collectRatesWithoutDimensionsDataProvider() + { + return [ + ['size' => '0', 'height' => '1.1', 'width' => '0.6', 'depth' => '0.7'], + ['size' => '1', 'height' => '', 'width' => '', 'depth' => ''], + ['size' => null, 'height' => '1.1', 'width' => '0.6', 'depth' => '0.7'], + ['size' => '1', 'height' => '1', 'width' => '', 'depth' => ''], + ['size' => null, 'height' => null, 'width' => null, 'depth' => null], + ]; + } + + /** + * Sets DHL config value. + * + * @param array $params + * @return void + */ + private function setDhlConfig(array $params) + { + foreach ($params as $name => $val) { + if ($val !== null) { + $this->config->setValue( + 'carriers/dhl/' . $name, + $val, + ScopeInterface::SCOPE_STORE + ); + } + } + } + + /** + * Returns request data. + * + * @return array + */ + private function getRequestData(): array + { + return [ 'data' => [ 'dest_country_id' => 'DE', 'dest_region_id' => '82', @@ -454,35 +554,5 @@ public function testCollectRates() 'all_items' => [], ] ]; - //phpcs:disable Magento2.Functions.DiscouragedFunction - $response = new Response( - 200, - [], - file_get_contents(__DIR__ . '/../_files/dhl_quote_response.xml') - ); - //phpcs:enable Magento2.Functions.DiscouragedFunction - $this->httpClient->nextResponses(array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response)); - /** @var RateRequest $request */ - $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); - $expectedRates = [ - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, 'method' => 'E', 'price' => 45.85], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'Q', 'price' => 35.26], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 37.38, 'method' => 'Y', 'price' => 37.38], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'P', 'price' => 35.26] - ]; - - $actualRates = $this->dhlCarrier->collectRates($request)->getAllRates(); - - self::assertEquals(count($expectedRates), count($actualRates)); - foreach ($actualRates as $i => $actualRate) { - $actualRate = $actualRate->getData(); - unset($actualRate['method_title']); - self::assertEquals($expectedRates[$i], $actualRate); - } - $requestXml = $this->httpClient->getLastRequest()->getBody(); - self::assertContains('<Weight>18.223</Weight>', $requestXml); - self::assertContains('<Height>0.630</Height>', $requestXml); - self::assertContains('<Width>0.630</Width>', $requestXml); - self::assertContains('<Depth>0.630</Depth>', $requestXml); } } From d0167a0029358c0324abdf3a8f6313bf66cff2ff Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Fri, 15 Nov 2019 23:59:29 -0300 Subject: [PATCH 2084/2437] fixed resize logo mobile --- .../Magento/blank/Magento_Theme/web/css/source/_module.less | 1 + .../Magento/luma/Magento_Theme/web/css/source/_module.less | 1 + 2 files changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index b314bcf5b3473..3faa8ca965410 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -89,6 +89,7 @@ img { display: block; + height: auto; } .page-print & { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index b841f2206e1d9..438fb55d32e5c 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -148,6 +148,7 @@ img { display: block; + height: auto; } .page-print & { From 8f7085f68245c79a0ee0291d55bf17a6246d09f9 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Sat, 16 Nov 2019 08:50:41 +0200 Subject: [PATCH 2085/2437] Adjustments according review --- .../Model/Asset/Command/DeleteByPath.php | 4 ++-- .../Model/Keyword/Command/GetAssetKeywordsTest.php | 9 ++++----- .../Model/Keyword/Command/SaveAssetKeywordsTest.php | 12 ++++++------ .../Model/Keyword/Command/SaveAssetLinksTest.php | 10 ++++------ 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php index e387da6de6beb..c05a08149bfca 100644 --- a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php @@ -7,10 +7,10 @@ namespace Magento\MediaGallery\Model\Asset\Command; -use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; use Psr\Log\LoggerInterface; /** diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php index 85030763303f0..2ccac4eac8343 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php @@ -41,18 +41,18 @@ class GetAssetKeywordsTest extends TestCase /** * @var LoggerInterface|MockObject */ - private $logger; + private $loggerMock; protected function setUp(): void { $this->resourceConnectionStub = $this->createMock(ResourceConnection::class); $this->assetKeywordFactoryStub = $this->createMock(KeywordInterfaceFactory::class); - $this->logger = $this->createMock(LoggerInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); $this->sut = new GetAssetKeywords( $this->resourceConnectionStub, $this->assetKeywordFactoryStub, - $this->logger + $this->loggerMock ); } @@ -62,7 +62,6 @@ protected function setUp(): void * @dataProvider casesProvider() * @param array $databaseQueryResult * @param int $expectedNumberOfFoundKeywords - * @throws NotFoundException */ public function testFind(array $databaseQueryResult, int $expectedNumberOfFoundKeywords): void { @@ -104,7 +103,7 @@ public function testNotFoundBecauseOfError(): void ->willThrowException((new \Exception())); $this->expectException(IntegrationException::class); - $this->logger->expects($this->once()) + $this->loggerMock->expects($this->once()) ->method('critical') ->willReturnSelf(); diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php index 57397f2705278..a55c60024c08d 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php @@ -7,13 +7,13 @@ namespace Magento\MediaGallery\Test\Unit\Model\Keyword\Command; -use Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords; -use Magento\MediaGallery\Model\Keyword\Command\SaveAssetLinks; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\Pdo\Mysql; use Magento\Framework\DB\Select; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords; +use Magento\MediaGallery\Model\Keyword\Command\SaveAssetLinks; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -51,7 +51,7 @@ class SaveAssetKeywordsTest extends TestCase /** * @var LoggerInterface|MockObject */ - private $logger; + private $loggerMock; /** * SetUp @@ -64,12 +64,12 @@ public function setUp(): void ->disableOriginalConstructor() ->getMock(); $this->selectMock = $this->createMock(Select::class); - $this->logger = $this->createMock(LoggerInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); $this->sut = new SaveAssetKeywords( $this->resourceConnectionMock, $this->saveAssetLinksMock, - $this->logger + $this->loggerMock ); } @@ -114,7 +114,7 @@ public function testAssetNotSavingCausedByError(): void ->method('getConnection') ->willThrowException((new \Exception())); $this->expectException(CouldNotSaveException::class); - $this->logger->expects($this->once()) + $this->loggerMock->expects($this->once()) ->method('critical') ->willReturnSelf(); diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php index 17916814eda95..2981c534586e2 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php @@ -38,7 +38,7 @@ class SaveAssetLinksTest extends TestCase /** * @var LoggerInterface|MockObject */ - private $logger; + private $loggerMock; /** * Prepare test objects. @@ -47,11 +47,11 @@ public function setUp(): void { $this->connectionMock = $this->createMock(AdapterInterface::class); $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); - $this->logger = $this->createMock(LoggerInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); $this->sut = new SaveAssetLinks( $this->resourceConnectionMock, - $this->logger + $this->loggerMock ); } @@ -63,8 +63,6 @@ public function setUp(): void * @param int $assetId * @param array $keywordIds * @param array $values - * - * @throws CouldNotSaveException */ public function testAssetKeywordsSave(int $assetId, array $keywordIds, array $values): void { @@ -105,7 +103,7 @@ public function testAssetNotSavingCausedByError(): void ->method('insertArray') ->willThrowException((new \Exception())); $this->expectException(CouldNotSaveException::class); - $this->logger->expects($this->once()) + $this->loggerMock->expects($this->once()) ->method('critical') ->willReturnSelf(); From 20a6a37db4baaeddc7dae246edcb5ecfb6de60e5 Mon Sep 17 00:00:00 2001 From: Alexander Shkurko <coderimus@gmail.com> Date: Sat, 16 Nov 2019 13:05:29 +0200 Subject: [PATCH 2086/2437] Adjustments according review --- .../Magento/MediaGallery/Plugin/Product/Gallery/Processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php index 9144e77213179..3fbe4e3a91a2b 100644 --- a/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php +++ b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php @@ -67,7 +67,7 @@ public function afterRemoveImage( try { $this->deleteMediaAssetByPath->execute($file); } catch (\Exception $exception) { - $this->logger->critical($$exception); + $this->logger->critical($exception); } return $result; From 094565fa335f20e3b77280c12090f5ad58dd0625 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Sat, 16 Nov 2019 14:55:14 +0000 Subject: [PATCH 2087/2437] Add right arrow to show some items have children --- .../Magento/backend/web/css/source/_actions.less | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index 886bbcc29a3b9..28912d873ae00 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -444,6 +444,20 @@ button { > .action-menu-item { min-width: 100%; } + + &::after { + border-color: transparent transparent transparent #000; + border-style: solid; + border-width: 0.4rem 0 0.4rem 0.5rem; + content: ''; + height: 0; + margin-top: -0.2rem; + position: absolute; + right: 1rem; + top: 50%; + transition: all .2s linear; + width: 0; + } } } From 4b79d4dbcd50487674697dc73c21dc45299e7d87 Mon Sep 17 00:00:00 2001 From: Evgenii Kalashnikov <e.kalashnikov@prola.ru> Date: Sat, 16 Nov 2019 19:05:00 +0300 Subject: [PATCH 2088/2437] upload media files fix --- .../Test/Unit/Model/Import/ProductTest.php | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index f85d33edb5d8c..427ea25d98368 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -410,7 +410,7 @@ protected function _objectConstructor() $this->_filesystem->expects($this->once()) ->method('getDirectoryWrite') ->with(DirectoryList::ROOT) - ->will($this->returnValue(self::MEDIA_DIRECTORY)); + ->willReturn($this->_mediaDirectory); $this->validator->expects($this->any())->method('init'); return $this; @@ -1229,6 +1229,56 @@ public function testParseAttributesWithWrappedValuesWillReturnsLowercasedAttribu $this->assertArrayNotHasKey('PARAM2', $attributes); } + /** + * @param bool $isRead + * @param bool $isWrite + * @param string $message + * @dataProvider fillUploaderObjectDataProvider + */ + public function testFillUploaderObject($isRead, $isWrite, $message) + { + $fileUploaderMock = $this + ->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Uploader::class) + ->disableOriginalConstructor() + ->getMock(); + + $fileUploaderMock + ->method('setTmpDir') + ->with('pub/media/import') + ->willReturn($isRead); + + $fileUploaderMock + ->method('setDestDir') + ->with('pub/media/catalog/product') + ->willReturn($isWrite); + + $this->_mediaDirectory + ->method('getRelativePath') + ->willReturnMap( + [ + ['import', 'import'], + ['catalog/product', 'catalog/product'], + ] + ); + + $this->_mediaDirectory + ->method('create') + ->with('pub/media/catalog/product'); + + $this->_uploaderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($fileUploaderMock); + + try { + $this->importProduct->getUploader(); + $this->assertNotNull($this->getPropertyValue($this->importProduct, '_fileUploader')); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->assertNull($this->getPropertyValue($this->importProduct, '_fileUploader')); + $this->assertEquals($message, $e->getMessage()); + } + } + /** * Test that errors occurred during importing images are logged. * @@ -1275,6 +1325,20 @@ function ($name) use ($throwException, $exception) { ); } + /** + * Data provider for testFillUploaderObject. + * + * @return array + */ + public function fillUploaderObjectDataProvider() + { + return [ + [false, true, 'File directory \'pub/media/import\' is not readable.'], + [true, false, 'File directory \'pub/media/catalog/product\' is not writable.'], + [true, true, ''], + ]; + } + /** * Data provider for testUploadMediaFiles. * From 82c6446c79b99f1bb03dffd79b43f98f6b8276b1 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Sat, 16 Nov 2019 11:56:49 -0600 Subject: [PATCH 2089/2437] MC-23093: The initial option value for DHL is incorrect --- .../integration/testsuite/Magento/Dhl/Model/CarrierTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php index 3a1df79179f64..a6a293d384d56 100644 --- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -22,6 +22,8 @@ /** * Test for DHL integration. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CarrierTest extends \PHPUnit\Framework\TestCase { @@ -469,6 +471,8 @@ public function testCollectRatesWithoutDimensions(?string $size, ?string $height $this->assertNotContains('<Width>', $requestXml); $this->assertNotContains('<Height>', $requestXml); $this->assertNotContains('<Depth>', $requestXml); + + $this->config->reinit(); } /** From d8e1a7166e97da1f1a750bd585a653b8a62b5c1f Mon Sep 17 00:00:00 2001 From: Evgenii Kalashnikov <e.kalashnikov@prola.ru> Date: Sat, 16 Nov 2019 23:29:10 +0300 Subject: [PATCH 2090/2437] upload media files fix --- .../Test/Unit/Model/Import/ProductTest.php | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 427ea25d98368..40041fe90db96 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -284,9 +284,11 @@ protected function setUp() ->getMock(); $this->storeResolver = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product\StoreResolver::class) - ->setMethods([ - 'getStoreCodeToId', - ]) + ->setMethods( + [ + 'getStoreCodeToId', + ] + ) ->disableOriginalConstructor() ->getMock(); $this->skuProcessor = @@ -596,9 +598,13 @@ public function testGetMultipleValueSeparatorDefault() public function testGetMultipleValueSeparatorFromParameters() { $expectedSeparator = 'value'; - $this->setPropertyValue($this->importProduct, '_parameters', [ - \Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $expectedSeparator, - ]); + $this->setPropertyValue( + $this->importProduct, + '_parameters', + [ + \Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $expectedSeparator, + ] + ); $this->assertEquals( $expectedSeparator, @@ -618,9 +624,13 @@ public function testGetEmptyAttributeValueConstantDefault() public function testGetEmptyAttributeValueConstantFromParameters() { $expectedSeparator = '__EMPTY__VALUE__TEST__'; - $this->setPropertyValue($this->importProduct, '_parameters', [ - \Magento\ImportExport\Model\Import::FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT => $expectedSeparator, - ]); + $this->setPropertyValue( + $this->importProduct, + '_parameters', + [ + \Magento\ImportExport\Model\Import::FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT => $expectedSeparator, + ] + ); $this->assertEquals( $expectedSeparator, @@ -632,9 +642,12 @@ public function testDeleteProductsForReplacement() { $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods([ - 'setParameters', '_deleteProducts' - ]) + ->setMethods( + [ + 'setParameters', + '_deleteProducts' + ] + ) ->getMock(); $importProduct->expects($this->once())->method('setParameters')->with( @@ -764,9 +777,13 @@ public function testGetProductWebsites() 'key 3' => 'val', ]; $expectedResult = array_keys($productValue); - $this->setPropertyValue($this->importProduct, 'websitesCache', [ - $productSku => $productValue - ]); + $this->setPropertyValue( + $this->importProduct, + 'websitesCache', + [ + $productSku => $productValue + ] + ); $actualResult = $this->importProduct->getProductWebsites($productSku); @@ -785,9 +802,13 @@ public function testGetProductCategories() 'key 3' => 'val', ]; $expectedResult = array_keys($productValue); - $this->setPropertyValue($this->importProduct, 'categoriesCache', [ - $productSku => $productValue - ]); + $this->setPropertyValue( + $this->importProduct, + 'categoriesCache', + [ + $productSku => $productValue + ] + ); $actualResult = $this->importProduct->getProductCategories($productSku); @@ -1112,9 +1133,13 @@ public function testValidateRowSetAttributeSetCodeIntoRowData() ->disableOriginalConstructor() ->getMock(); $productType->expects($this->once())->method('isRowValid')->with($expectedRowData); - $this->setPropertyValue($importProduct, '_productTypeModels', [ - $newSku['type_id'] => $productType - ]); + $this->setPropertyValue( + $importProduct, + '_productTypeModels', + [ + $newSku['type_id'] => $productType + ] + ); //suppress option validation $this->_rewriteGetOptionEntityInImportProduct($importProduct); From 540b1340275b001aca44ce3d889a039614167a8e Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Wed, 13 Nov 2019 18:10:05 +0100 Subject: [PATCH 2091/2437] Refactor JavaScript mixins module Fixes https://github.com/magento/magento2/issues/25586. --- lib/web/mage/requirejs/mixins.js | 152 ++++++++++++------------------- 1 file changed, 57 insertions(+), 95 deletions(-) diff --git a/lib/web/mage/requirejs/mixins.js b/lib/web/mage/requirejs/mixins.js index 77d98e0f81394..acda3ebf467d7 100644 --- a/lib/web/mage/requirejs/mixins.js +++ b/lib/web/mage/requirejs/mixins.js @@ -7,7 +7,25 @@ define('mixins', [ ], function (module) { 'use strict'; - var rjsMixins; + var contexts = require.s.contexts, + defContextName = '_', + defContext = contexts[defContextName], + unbundledContext = require.s.newContext('$'), + defaultConfig = defContext.config, + unbundledConfig = { + baseUrl: defaultConfig.baseUrl, + paths: defaultConfig.paths, + shim: defaultConfig.shim, + config: defaultConfig.config, + map: defaultConfig.map + }, + rjsMixins; + + /** + * Prepare a separate context where modules are not assigned to bundles + * so we are able to get their true path and corresponding mixins. + */ + unbundledContext.configure(unbundledConfig); /** * Checks if specified string contains @@ -50,14 +68,14 @@ define('mixins', [ /** * Extracts url (without baseUrl prefix) - * from a modules' name. + * from a module name ignoring the fact that it may be bundled. * * @param {String} name - Name, path or alias of a module. - * @param {Object} config - Contexts' configuartion. + * @param {Object} config - Context's configuartion. * @returns {String} */ function getPath(name, config) { - var url = require.toUrl(name); + var url = unbundledContext.require.toUrl(name); return removeBaseUrl(url, config); } @@ -73,11 +91,11 @@ define('mixins', [ } /** - * Iterativly calls mixins passing to them + * Iteratively calls mixins passing to them * current value of a 'target' parameter. * * @param {*} target - Value to be modified. - * @param {...Function} mixins + * @param {...Function} mixins - List of mixins to apply. * @returns {*} Modified 'target' value. */ function applyMixins(target) { @@ -94,8 +112,13 @@ define('mixins', [ /** * Loads specified module along with its' mixins. + * This method is called for each module defined with "mixins!" prefix + * in its name that was added by processNames method. * * @param {String} name - Module to be loaded. + * @param {Function} req - Local "require" function to use to load other modules. + * @param {Function} onLoad - A function to call with the value for name. + * @param {Object} config - RequireJS configuration object. */ load: function (name, req, onLoad, config) { var path = getPath(name, config), @@ -110,14 +133,14 @@ define('mixins', [ /** * Retrieves list of mixins associated with a specified module. * - * @param {String} path - Path to the module (without base url). + * @param {String} path - Path to the module (without base URL). * @returns {Array} An array of paths to mixins. */ getMixins: function (path) { var config = module.config() || {}, - mixins; + mixins; - // fix for when urlArgs is set + // Fix for when urlArgs is set. if (path.indexOf('?') !== -1) { path = path.substring(0, path.indexOf('?')); } @@ -131,7 +154,7 @@ define('mixins', [ /** * Checks if specified module has associated with it mixins. * - * @param {String} path - Path to the module (without base url). + * @param {String} path - Path to the module (without base URL). * @returns {Boolean} */ hasMixins: function (path) { @@ -139,11 +162,11 @@ define('mixins', [ }, /** - * Modifies provided names perpending to them + * Modifies provided names prepending to them * the 'mixins!' plugin prefix if it's necessary. * * @param {(Array|String)} names - Module names, paths or aliases. - * @param {Object} context - Current requirejs context. + * @param {Object} context - Current RequireJS context. * @returns {Array|String} */ processNames: function (names, context) { @@ -179,101 +202,40 @@ require([ ], function (mixins) { 'use strict'; - var originalRequire = window.require, - originalDefine = window.define, - contexts = originalRequire.s.contexts, - defContextName = '_', - hasOwn = Object.prototype.hasOwnProperty, - getLastInQueue; - - getLastInQueue = - '(function () {' + - 'var queue = globalDefQueue,' + - 'item = queue[queue.length - 1];' + - '' + - 'return item;' + - '})();'; - - /** - * Returns property of an object if - * it's not defined in it's prototype. - * - * @param {Object} obj - Object whose property should be retrieved. - * @param {String} prop - Name of the property. - * @returns {*} Value of the property or false. - */ - function getOwn(obj, prop) { - return hasOwn.call(obj, prop) && obj[prop]; - } - - /** - * Overrides global 'require' method adding to it dependencies modfication. - */ - window.require = function (deps, callback, errback, optional) { - var contextName = defContextName, - context, - config; - - if (!Array.isArray(deps) && typeof deps !== 'string') { - config = deps; - - if (Array.isArray(callback)) { - deps = callback; - callback = errback; - errback = optional; - } else { - deps = []; - } - } - - if (config && config.context) { - contextName = config.context; - } - - context = getOwn(contexts, contextName); - - if (!context) { - context = contexts[contextName] = require.s.newContext(contextName); - } - - if (config) { - context.configure(config); - } - - deps = mixins.processNames(deps, context); - - return context.require(deps, callback, errback); - }; + var contexts = require.s.contexts, + defContextName = '_', + defContext = contexts[defContextName], + originalContextRequire = defContext.require, + processNames = mixins.processNames; /** - * Overrides global 'define' method adding to it dependencies modfication. + * Wrap default context's require function which gets called every time + * module is requested using require call. The upside of this approach + * is that deps parameter is already normalized and guaranteed to be an array. */ - window.define = function (name, deps, callback) { // eslint-disable-line no-unused-vars - var context = getOwn(contexts, defContextName), - result = originalDefine.apply(this, arguments), - queueItem = require.exec(getLastInQueue), - lastDeps = queueItem && queueItem[1]; - - if (Array.isArray(lastDeps)) { - queueItem[1] = mixins.processNames(lastDeps, context); - } + defContext.require = function (deps, callback, errback) { + deps = processNames(deps, defContext); - return result; + return originalContextRequire(deps, callback, errback); }; /** * Copy properties of original 'require' method. */ - Object.keys(originalRequire).forEach(function (key) { - require[key] = originalRequire[key]; + Object.keys(originalContextRequire).forEach(function (key) { + defContext.require[key] = originalContextRequire[key]; }); /** - * Copy properties of original 'define' method. + * Wrap shift method from context's definitions queue. + * Items are added to the queue when a new module is defined and taken + * from it every time require call happens. */ - Object.keys(originalDefine).forEach(function (key) { - define[key] = originalDefine[key]; - }); + defContext.defQueue.shift = function () { + var queueItem = Array.prototype.shift.call(this); + + queueItem[1] = processNames(queueItem[1], defContext); - window.requirejs = window.require; + return queueItem; + }; }); From 16aabf76e99e00ba58ea133e4c140698acc17f17 Mon Sep 17 00:00:00 2001 From: Adarsh Manickam <adarsh.apple@icloud.com> Date: Mon, 18 Nov 2019 11:48:49 +0530 Subject: [PATCH 2092/2437] Reverted PR 25022 --- .../backend/web/css/source/forms/_controls.less | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index ab4bac919ee5f..c6f39e8e8840d 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -59,15 +59,15 @@ .admin__control-select { &:extend(.abs-form-control-pattern all); .lib-css(appearance, none, 1); - background-image+: url('../images/arrows-bg.svg') !important; + background-image+: url('../images/arrows-bg.svg'); background-position+: ~'calc(100% - 12px)' -34px; background-size+: auto; - background-image+: linear-gradient(@color-gray89, @color-gray89) !important; + background-image+: linear-gradient(@color-gray89, @color-gray89); background-position+: 100%; background-size+: @field-control__height 100%; - background-image+: linear-gradient(@field-control__border-color,@field-control__border-color) !important; + background-image+: linear-gradient(@field-control__border-color,@field-control__border-color); background-position+: ~'calc(100% - @{field-control__height})' 0; background-size+: 1px 100%; @@ -86,13 +86,13 @@ } &:active { - background-image+: url('../images/arrows-bg.svg') !important; + background-image+: url('../images/arrows-bg.svg'); background-position+: ~'calc(100% - 12px)' 13px; - background-image+: linear-gradient(@color-gray89, @color-gray89) !important; + background-image+: linear-gradient(@color-gray89, @color-gray89); background-position+: 100%; - background-image+: linear-gradient(@field-control__focus__border-color, @field-control__focus__border-color) !important; + background-image+: linear-gradient(@field-control__focus__border-color, @field-control__focus__border-color); background-position+: ~'calc(100% - @{field-control__height})' 0; border-color: @field-control__focus__border-color; } From f6b310afbbf8cd64e6d684925d2b4c104d314e0d Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 18 Nov 2019 10:13:47 +0200 Subject: [PATCH 2093/2437] MC-22031: Admin: Product URL Management --- .../Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index b50f48f00e473..a98c50e21dd3b 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -26,6 +26,7 @@ * * @magentoDbIsolation enabled * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategoryUrlRewriteTest extends AbstractUrlRewriteTest { From 0bff8934405db67c219d79192c123dd0b9598ae4 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 18 Nov 2019 10:21:42 +0200 Subject: [PATCH 2094/2437] MC-22031: Admin: Product URL Management --- .../CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index a98c50e21dd3b..1431148c5f868 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -369,14 +369,6 @@ protected function getEntityType(): string return DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE; } - /** - * @inheritdoc - */ - protected function getEntityFactory() - { - return $this->categoryFactory; - } - /** * Save product with data using resource model directly * From 3a7a953802482647e9030066c0f83e4b252f2127 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 18 Nov 2019 11:37:39 +0200 Subject: [PATCH 2095/2437] MC-19235: SalesRule improvement in TestFramework --- .../SalesRule/Model/GetSalesRuleByName.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php b/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php new file mode 100644 index 0000000000000..db94d9ee97e04 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\SalesRule\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Api\Data\RuleInterface; + +/** + * Search and return Sales rule by name. + */ +class GetSalesRuleByName +{ + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var RuleRepositoryInterface */ + private $ruleRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param RuleRepositoryInterface $ruleRepository + */ + public function __construct(SearchCriteriaBuilder $searchCriteriaBuilder, RuleRepositoryInterface $ruleRepository) + { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->ruleRepository = $ruleRepository; + } + + /** + * Return Sales Rule by name. + * + * @param string $name + * @return RuleInterface|null + */ + public function execute(string $name): ?RuleInterface + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('name', $name)->create(); + $salesRules = $this->ruleRepository->getList($searchCriteria)->getItems(); + + return array_shift($salesRules); + } +} From a7ffbd6842a0e4e243a014b8ceb8d197fca83f2e Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 18 Nov 2019 10:32:38 +0200 Subject: [PATCH 2096/2437] Move hide() method to if statement --- .../Ui/view/base/web/js/grid/columns/image-preview.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 57f16dc0c1a02..cf597f8d3a543 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -101,12 +101,13 @@ define([ show: function (record) { var img; - this.hide(); - if (record._rowIndex === this.visibleRecord()) { + this.hide(); + return; } + this.hide(); this.displayedRecord(record); this._selectRow(record.rowNumber || null); this.visibleRecord(record._rowIndex); From 0f007c46801a2473ecf0938624a3b2a4b53d453b Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 18 Nov 2019 13:01:48 +0200 Subject: [PATCH 2097/2437] MC-20684: Admin: Add/remove product from other storeviews and websites --- .../Product/UpdateProductWebsiteTest.php | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php index e2fc326dac985..646e661419292 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php @@ -9,7 +9,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\ResourceModel\Product\WebsiteFactory; +use Magento\Catalog\Model\ResourceModel\Product\Website\Link; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\ObjectManagerInterface; use Magento\Store\Api\WebsiteRepositoryInterface; @@ -26,8 +26,8 @@ class UpdateProductWebsiteTest extends TestCase /** @var ObjectManagerInterface */ private $objectManager; - /** @var WebsiteFactory */ - private $websiteProductsResourceFactory; + /** @var Link */ + private $productWebsiteLink; /** @var WebsiteRepositoryInterface */ private $websiteRepository; @@ -43,7 +43,7 @@ protected function setUp() parent::setUp(); $this->objectManager = Bootstrap::getObjectManager(); - $this->websiteProductsResourceFactory = $this->objectManager->get(WebsiteFactory::class); + $this->productWebsiteLink = $this->objectManager->get(Link::class); $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); } @@ -58,7 +58,10 @@ public function testAssignProductToWebsite(): void $defaultWebsiteId = $this->websiteRepository->get('base')->getId(); $secondWebsiteId = $this->websiteRepository->get('test')->getId(); $product = $this->updateProductWebsites('simple2', [$defaultWebsiteId, $secondWebsiteId]); - $this->assertProductWebsites((int)$product->getId(), [$defaultWebsiteId, $secondWebsiteId]); + $this->assertEquals( + [$defaultWebsiteId, $secondWebsiteId], + $this->productWebsiteLink->getWebsiteIdsByProductId($product->getId()) + ); } /** @@ -68,11 +71,9 @@ public function testAssignProductToWebsite(): void */ public function testUnassignProductFromWebsite(): void { - $product = $this->productRepository->get('simple-on-two-websites'); $secondWebsiteId = $this->websiteRepository->get('test')->getId(); - $product->setWebsiteIds([$secondWebsiteId]); - $product = $this->productRepository->save($product); - $this->assertProductWebsites((int)$product->getId(), [$secondWebsiteId]); + $product = $this->updateProductWebsites('simple-on-two-websites', [$secondWebsiteId]); + $this->assertEquals([$secondWebsiteId], $this->productWebsiteLink->getWebsiteIdsByProductId($product->getId())); } /** @@ -102,17 +103,4 @@ private function updateProductWebsites(string $productSku, array $websiteIds): P return $this->productRepository->save($product); } - - /** - * Assert that websites attribute was correctly saved - * - * @param int $productId - * @param array $expectedData - * @return void - */ - private function assertProductWebsites(int $productId, array $expectedData): void - { - $websiteResource = $this->websiteProductsResourceFactory->create(); - $this->assertEquals($expectedData, $websiteResource->getWebsites([$productId])[$productId]); - } } From 64482d1392ea7963760c57b83c818c76a2f18501 Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <bensch.rosenberger@gmail.com> Date: Mon, 18 Nov 2019 15:11:57 +0100 Subject: [PATCH 2098/2437] add indexer id to output add additional id column to bin/magento indexer:status command in order to have that information ready --- .../Magento/Indexer/Console/Command/IndexerStatusCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php index cefb070f60b74..f5125fddf34b2 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php @@ -34,7 +34,7 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $table = new Table($output); - $table->setHeaders(['Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']); + $table->setHeaders(['ID', 'Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']); $rows = []; @@ -43,6 +43,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $view = $indexer->getView(); $rowData = [ + 'ID' => $indexer->getId(), 'Title' => $indexer->getTitle(), 'Status' => $this->getStatus($indexer), 'Update On' => $indexer->isScheduled() ? 'Schedule' : 'Save', From 51526cfa1f92a31888936c9965bf3b8e839119b5 Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <bensch.rosenberger@gmail.com> Date: Mon, 18 Nov 2019 15:29:01 +0100 Subject: [PATCH 2099/2437] fix unit test to scope with new table column adapt test to contain new id column --- .../Command/IndexerStatusCommandTest.php | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php index 8498bd183af21..2aa55950e762e 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php @@ -96,7 +96,7 @@ public function testExecuteAll(array $indexers) $linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay())); - $spacer = '+----------------+------------------+-----------+-------------------------+---------------------+'; + $spacer = '+-----------+----------------+------------------+-----------+-------------------------+---------------------+'; $this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.'); $this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines"); @@ -104,39 +104,44 @@ public function testExecuteAll(array $indexers) $this->assertEquals($linesOutput[7], $spacer, "Lines 0, 2, 7 should be spacer lines"); $headerValues = array_values(array_filter(explode('|', $linesOutput[1]))); - $this->assertEquals('Title', trim($headerValues[0])); - $this->assertEquals('Status', trim($headerValues[1])); - $this->assertEquals('Update On', trim($headerValues[2])); - $this->assertEquals('Schedule Status', trim($headerValues[3])); - $this->assertEquals('Schedule Updated', trim($headerValues[4])); + $this->assertEquals('ID', trim($headerValues[0])); + $this->assertEquals('Title', trim($headerValues[1])); + $this->assertEquals('Status', trim($headerValues[2])); + $this->assertEquals('Update On', trim($headerValues[3])); + $this->assertEquals('Schedule Status', trim($headerValues[4])); + $this->assertEquals('Schedule Updated', trim($headerValues[5])); $indexer1 = array_values(array_filter(explode('|', $linesOutput[3]))); - $this->assertEquals('Title_indexer1', trim($indexer1[0])); - $this->assertEquals('Ready', trim($indexer1[1])); - $this->assertEquals('Schedule', trim($indexer1[2])); - $this->assertEquals('idle (10 in backlog)', trim($indexer1[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[4])); + $this->assertEquals('indexer_1', trim($indexer1[0])); + $this->assertEquals('Title_indexer1', trim($indexer1[1])); + $this->assertEquals('Ready', trim($indexer1[2])); + $this->assertEquals('Schedule', trim($indexer1[3])); + $this->assertEquals('idle (10 in backlog)', trim($indexer1[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[5])); $indexer2 = array_values(array_filter(explode('|', $linesOutput[4]))); - $this->assertEquals('Title_indexer2', trim($indexer2[0])); - $this->assertEquals('Reindex required', trim($indexer2[1])); - $this->assertEquals('Save', trim($indexer2[2])); - $this->assertEquals('', trim($indexer2[3])); + $this->assertEquals('indexer_2', trim($indexer2[0])); + $this->assertEquals('Title_indexer2', trim($indexer2[1])); + $this->assertEquals('Reindex required', trim($indexer2[2])); + $this->assertEquals('Save', trim($indexer2[3])); $this->assertEquals('', trim($indexer2[4])); + $this->assertEquals('', trim($indexer2[5])); $indexer3 = array_values(array_filter(explode('|', $linesOutput[5]))); - $this->assertEquals('Title_indexer3', trim($indexer3[0])); - $this->assertEquals('Processing', trim($indexer3[1])); - $this->assertEquals('Schedule', trim($indexer3[2])); - $this->assertEquals('idle (100 in backlog)', trim($indexer3[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[4])); + $this->assertEquals('indexer_3', trim($indexer3[0])); + $this->assertEquals('Title_indexer3', trim($indexer3[1])); + $this->assertEquals('Processing', trim($indexer3[2])); + $this->assertEquals('Schedule', trim($indexer3[3])); + $this->assertEquals('idle (100 in backlog)', trim($indexer3[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[5])); $indexer4 = array_values(array_filter(explode('|', $linesOutput[6]))); - $this->assertEquals('Title_indexer4', trim($indexer4[0])); - $this->assertEquals('unknown', trim($indexer4[1])); - $this->assertEquals('Schedule', trim($indexer4[2])); - $this->assertEquals('running (20 in backlog)', trim($indexer4[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[4])); + $this->assertEquals('indexer_4', trim($indexer4[0])); + $this->assertEquals('Title_indexer4', trim($indexer4[1])); + $this->assertEquals('unknown', trim($indexer4[2])); + $this->assertEquals('Schedule', trim($indexer4[3])); + $this->assertEquals('running (20 in backlog)', trim($indexer4[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[5])); } /** From c9502a2995e0b3f475ebeca45f019d7597578daf Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Mon, 18 Nov 2019 16:44:43 +0200 Subject: [PATCH 2100/2437] MC-22972: Credit memo without refunded shipment produces the negative Grand Total --- app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index d4c2e7b2d6854..95dace13d832f 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -41,7 +41,7 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $baseOrderItemTax = (double)$orderItem->getBaseTaxInvoiced(); $orderItemQty = (double)$orderItem->getQtyInvoiced(); - if ($orderItemTax && $orderItemQty) { + if ($orderItemQty) { /** * Check item tax amount */ From 21620c69868e3786148b3e8f5e0d0786a7f7540b Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Mon, 18 Nov 2019 15:58:32 +0100 Subject: [PATCH 2101/2437] Add tests --- .../tests/lib/mage/requirejs/mixins.test.js | 148 ++++++++++++++++++ lib/web/mage/requirejs/mixins.js | 2 +- 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js diff --git a/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js new file mode 100644 index 0000000000000..3d85f77b87467 --- /dev/null +++ b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js @@ -0,0 +1,148 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/* eslint max-nested-callbacks: 0 */ +require.config({ + paths: { + 'mixins': 'mage/requirejs/mixins' + } +}); + +define(['rjsResolver', 'mixins'], function (resolver, mixins) { + 'use strict'; + + var context = { + config: {} + }; + + describe('mixins module', function () { + beforeEach(function (done) { + // Wait for all modules to be loaded so they don't interfere with testing. + resolver(function () { + done(); + }); + }); + + describe('processNames method', function () { + beforeEach(function () { + spyOn(mixins, 'processNames').and.callThrough(); + spyOn(mixins, 'hasMixins').and.callThrough(); + }); + + it('gets called when module is both required and defined', function (done) { + var name = 'tests/assets/mixins/defined-module', + dependencyName = 'tests/assets/mixins/defined-module-dependency'; + + define(dependencyName, [], function () {}); + define(name, [dependencyName], function () {}); + + require([name], function () { + expect(mixins.processNames.calls.argsFor(0)[0]).toEqual([]); + expect(mixins.processNames.calls.argsFor(1)[0]).toEqual([dependencyName]); + expect(mixins.processNames.calls.argsFor(2)[0]).toEqual([name]); + done(); + }); + }); + + it('keeps name intact when it already contains another plugin', function () { + mixins.hasMixins.and.returnValue(true); + + expect(mixins.processNames('plugin!module', context)).toBe('plugin!module'); + }); + + it('keeps name intact when it has no mixins', function () { + mixins.hasMixins.and.returnValue(false); + + expect(mixins.processNames('module', context)).toBe('module'); + }); + + it('keeps names intact when they have no mixins', function () { + mixins.hasMixins.and.returnValue(false); + + expect(mixins.processNames(['module'], context)).toEqual(['module']); + }); + + it('adds prefix to name when it has mixins', function () { + mixins.hasMixins.and.returnValue(true); + + expect(mixins.processNames('module', context)).toBe('mixins!module'); + }); + + it('adds prefix to name when it contains a relative path', function () { + mixins.hasMixins.and.returnValue(false); + + expect(mixins.processNames('./module', context)).toBe('mixins!./module'); + }); + + it('adds prefix to names when they contain a relative path', function () { + mixins.hasMixins.and.returnValue(false); + + expect(mixins.processNames(['./module'], context)).toEqual(['mixins!./module']); + }); + + it('adds prefix to names when they have mixins', function () { + mixins.hasMixins.and.returnValue(true); + + expect(mixins.processNames(['module'], context)).toEqual(['mixins!module']); + }); + }); + + describe('load method', function () { + it('is not called when module has mixins', function (done) { + var name = 'tests/assets/mixins/load-not-called'; + + spyOn(mixins, 'hasMixins').and.returnValue(false); + spyOn(mixins, 'load').and.callThrough(); + + define(name, [], function () {}); + + require([name], function () { + expect(mixins.load.calls.any()).toBe(false); + done(); + }); + }); + + it('is called when module has mixins', function (done) { + var name = 'tests/assets/mixins/load-called'; + + spyOn(mixins, 'hasMixins').and.returnValue(true); + spyOn(mixins, 'load').and.callThrough(); + + define(name, [], function () {}); + + require([name], function () { + expect(mixins.load.calls.mostRecent().args[0]).toEqual(name); + done(); + }); + }); + + it('applies mixins for loaded module', function (done) { + var name = 'tests/assets/mixins/mixins-applied', + mixinName = 'tests/assets/mixins/mixins-applied-ext'; + + spyOn(mixins, 'hasMixins').and.returnValue(true); + spyOn(mixins, 'load').and.callThrough(); + spyOn(mixins, 'getMixins').and.returnValue([mixinName]); + + define(name, [], function () { + return { value: 'original' }; + }); + + define(mixinName, [], function () { + return function(module) { + module.value = 'changed'; + + return module; + }; + }); + + require([name], function (module) { + expect(module.value).toBe('changed'); + done(); + }); + }); + }); + }); +}); diff --git a/lib/web/mage/requirejs/mixins.js b/lib/web/mage/requirejs/mixins.js index acda3ebf467d7..613605038f4b9 100644 --- a/lib/web/mage/requirejs/mixins.js +++ b/lib/web/mage/requirejs/mixins.js @@ -71,7 +71,7 @@ define('mixins', [ * from a module name ignoring the fact that it may be bundled. * * @param {String} name - Name, path or alias of a module. - * @param {Object} config - Context's configuartion. + * @param {Object} config - Context's configuration. * @returns {String} */ function getPath(name, config) { From 94f223bdac946f7a38342d42defb66ad1e23e5b1 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Mon, 18 Nov 2019 16:44:01 +0200 Subject: [PATCH 2102/2437] MC-19235: Quote improvement in TestFramework --- .../Quote/Model/GetQuoteByReservedOrderId.php | 48 ++++++++++++++++++ .../quote_for_customer_and_custom_store.php | 49 +++++++++++++++++++ ...for_customer_and_custom_store_rollback.php | 29 +++++++++++ 3 files changed, 126 insertions(+) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store.php create mode 100644 dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store_rollback.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php new file mode 100644 index 0000000000000..1d2a1a92fc128 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Quote\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; + +/** + * Search and return quote by reserved order id. + */ +class GetQuoteByReservedOrderId +{ + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var CartRepositoryInterface */ + private $cartRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param CartRepositoryInterface $cartRepository + */ + public function __construct(SearchCriteriaBuilder $searchCriteriaBuilder, CartRepositoryInterface $cartRepository) + { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->cartRepository = $cartRepository; + } + + /** + * Return quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface|null + */ + public function execute(string $reservedOrderId): ?CartInterface + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)->create(); + $quote = $this->cartRepository->getList($searchCriteria)->getItems(); + + return array_shift($quote); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store.php b/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store.php new file mode 100644 index 0000000000000..6e1fba3db57cb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Customer\Api\CustomerRepositoryInterface; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$customer = $objectManager->get(CustomerRepositoryInterface::class)->get('customer@example.com'); +$store = $objectManager->get(StoreRepositoryInterface::class)->get('secondstore'); + +$addressData = include __DIR__ . '/../../Customer/Fixtures/address_data.php'; +/** @var Address $shippingAddress */ +$shippingAddress = $objectManager->create(Address::class, ['data' => $addressData[0]]); +$shippingAddress->setAddressType('shipping'); + +$billingAddress = clone $shippingAddress; +$billingAddress->setId(null) + ->setAddressType('billing'); + +/** @var Quote $quote */ +$quote = $objectManager->create( + Quote::class, + [ + 'data' => [ + 'customer_id' => $customer->getId(), + 'store_id' => $store->getId(), + 'reserved_order_id' => 'tsg-123456789', + 'is_active' => true, + 'is_multishipping' => false + ], + ] +); +$quote->setShippingAddress($shippingAddress) + ->setBillingAddress($billingAddress); + +/** @var CartRepositoryInterface $repository */ +$repository = $objectManager->get(CartRepositoryInterface::class); +$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store_rollback.php b/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store_rollback.php new file mode 100644 index 0000000000000..9d922b8bd0bd8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/_files/quote_for_customer_and_custom_store_rollback.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$quote = $objectManager->get(GetQuoteByReservedOrderId::class)->execute('tsg-123456789'); +if ($quote !== null) { + /** @var CartRepositoryInterface $cartRepository */ + $cartRepository = $objectManager->get(CartRepositoryInterface::class); + $cartRepository->delete($quote); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 385c60549e23af45faddcea86a5af960bc29d0db Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Mon, 18 Nov 2019 09:17:34 -0600 Subject: [PATCH 2103/2437] MC-23113: [Magento cloud] - Critical error in log: strpos() expects parameter 1 to be string, array given --- .../Magento/CatalogSearch/Model/Layer/Filter/Attribute.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php index 5b3d324179448..d8947ac4224a8 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -58,16 +58,18 @@ public function apply(\Magento\Framework\App\RequestInterface $request) if (empty($attributeValue) && !is_numeric($attributeValue)) { return $this; } + $attribute = $this->getAttributeModel(); /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */ $productCollection = $this->getLayer() ->getProductCollection(); $productCollection->addFieldToFilter($attribute->getAttributeCode(), $attributeValue); + $labels = []; foreach ((array) $attributeValue as $value) { - $labels[] = $this->getOptionText($value); + $labels[] = (array) $this->getOptionText($value); } - $label = implode(',', $labels); + $label = implode(',', array_unique(array_merge(...$labels))); $this->getLayer() ->getState() ->addFilter($this->_createItem($label, $attributeValue)); From 8fa5564aef18f872cfe1cfcaeec3fc1806027029 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Mon, 18 Nov 2019 18:07:51 +0200 Subject: [PATCH 2104/2437] MC-22738: Layered Navigation with different product attributes on Category page --- ...category_with_different_price_products.php | 63 ++++++++ ...with_different_price_products_rollback.php | 43 +++++ .../_files/product_boolean_attribute.php | 7 +- .../Block/Navigation/AbstractCategoryTest.php | 101 ++++++++++++ .../Category/AbstractFiltersTest.php | 124 ++++++++++++++ .../Navigation/Category/BooleanFilterTest.php | 78 +++++++++ .../Navigation/Category/DecimalFilterTest.php | 121 ++++++++++++++ .../Category/MultiselectFilterTest.php | 81 ++++++++++ .../Navigation/Category/SelectFilterTest.php | 80 +++++++++ .../Block/Navigation/CategoryTest.php | 152 ++++++++++++++++++ .../_files/product_text_swatch_attribute.php | 1 + .../product_visual_swatch_attribute.php | 70 ++++++++ ...oduct_visual_swatch_attribute_rollback.php | 25 +++ .../Category/SwatchTextFilterTest.php | 81 ++++++++++ .../Category/SwatchVisualFilterTest.php | 81 ++++++++++ 15 files changed, 1106 insertions(+), 2 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php new file mode 100644 index 0000000000000..8346f43d0dded --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterfaceFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$categoryFactory = Bootstrap::getObjectManager()->get(CategoryInterfaceFactory::class); +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +$category = $categoryFactory->create(); +$category->isObjectNew(true); +$category->setName('Category 999') + ->setParentId(2)->setPath('1/2') + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setAvailableSortBy(['position']) + ->save(); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setWebsiteIds([1]) + ->setName('Simple Product With Price 10') + ->setSku('simple1000') + ->setPrice(10) + ->setWeight(1) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([$category->getId()]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setWebsiteIds([1]) + ->setName('Simple Product With Price 20') + ->setSku('simple1001') + ->setPrice(20) + ->setWeight(1) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([$category->getId()]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php new file mode 100644 index 0000000000000..3eb09edc32943 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryCollectionFactory = $objectManager->get(CollectionFactory::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +try { + $productRepository->deleteById('simple1000'); +} catch (NoSuchEntityException $e) { +} + +try { + $productRepository->deleteById('simple1001'); +} catch (NoSuchEntityException $e) { +} + +try { + $categoryCollection = $categoryCollectionFactory->create(); + $category = $categoryCollection + ->addAttributeToFilter(CategoryInterface::KEY_NAME, 'Category 999') + ->setPageSize(1) + ->getFirstItem(); + $categoryRepository->delete($category); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php index 30900db5690ff..34dccc2284445 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); use Magento\Catalog\Setup\CategorySetup; use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Eav\Model\Entity\Attribute\Source\Boolean; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); @@ -14,7 +16,7 @@ $attributeRepository = $objectManager->get(AttributeRepositoryInterface::class); /** @var Attribute $attribute */ $attribute = $objectManager->create(Attribute::class); -/** @var $installer \Magento\Catalog\Setup\CategorySetup */ +/** @var $installer CategorySetup */ $installer = $objectManager->create(CategorySetup::class); $attribute->setData( @@ -37,7 +39,8 @@ 'used_in_product_listing' => 1, 'used_for_sort_by' => 0, 'frontend_label' => ['Boolean Attribute'], - 'backend_type' => 'int' + 'backend_type' => 'int', + 'source_model' => Boolean::class ] ); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php new file mode 100644 index 0000000000000..ceac508b7af92 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; +use Magento\Catalog\Model\ResourceModel\Category\Collection; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\LayeredNavigation\Block\Navigation; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Base class for filters block tests on category page. + */ +abstract class AbstractCategoryTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var CollectionFactory + */ + protected $categoryCollectionFactory; + + /** + * @var CategoryResource + */ + protected $categoryResource; + + /** + * @var Navigation + */ + protected $navigationBlock; + + /** + * @var LayoutInterface + */ + protected $layout; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->categoryCollectionFactory = $this->objectManager->create(CollectionFactory::class); + $this->categoryResource = $this->objectManager->get(CategoryResource::class); + $this->layout = $this->objectManager->create(LayoutInterface::class); + $this->navigationBlock = $this->objectManager->create(Category::class); + parent::setUp(); + } + + /** + * Inits navigation block. + * + * @param string $categoryName + * @param int $storeId + * @return void + */ + protected function prepareNavigationBlock( + string $categoryName, + int $storeId = Store::DEFAULT_STORE_ID + ): void { + $category = $this->loadCategory($categoryName, $storeId); + $this->navigationBlock->getLayer()->setCurrentCategory($category); + $this->navigationBlock->setLayout($this->layout); + } + + /** + * Loads category by id. + * + * @param string $categoryName + * @param int $storeId + * @return CategoryInterface + */ + protected function loadCategory(string $categoryName, int $storeId): CategoryInterface + { + /** @var Collection $categoryCollection */ + $categoryCollection = $this->categoryCollectionFactory->create(); + /** @var CategoryInterface $category */ + $category = $categoryCollection->setStoreId($storeId) + ->addAttributeToSelect('display_mode', 'left') + ->addAttributeToFilter(CategoryInterface::KEY_NAME, $categoryName) + ->setPageSize(1) + ->getFirstItem(); + $category->setStoreId($storeId); + + return $category; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php new file mode 100644 index 0000000000000..89fd1485bf439 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item; +use Magento\CatalogSearch\Model\Indexer\Fulltext\Processor; +use Magento\Framework\Search\Request\Builder; +use Magento\Framework\Search\Request\Config; +use Magento\LayeredNavigation\Block\Navigation\AbstractCategoryTest; +use Magento\Search\Model\Search; +use Magento\Store\Model\Store; + +/** + * Base class for custom filters in navigation block on category page. + */ +abstract class AbstractFiltersTest extends AbstractCategoryTest +{ + /** + * @var ProductAttributeRepositoryInterface + */ + protected $attributeRepository; + + /** + * @var ProductRepositoryInterface + */ + protected $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + } + + /** + * @inheritdoc + */ + protected function prepareNavigationBlock(string $categoryName, int $storeId = Store::DEFAULT_STORE_ID): void + { + $this->objectManager->removeSharedInstance(Config::class); + $this->objectManager->removeSharedInstance(Builder::class); + $this->objectManager->removeSharedInstance(Search::class); + $this->objectManager->create(Processor::class)->reindexAll(); + parent::prepareNavigationBlock($categoryName, $storeId); + } + + /** + * Returns filter with specified attribute. + * + * @param array $filters + * @param string $code + * @return AbstractFilter|null + */ + protected function getFilterByCode(array $filters, string $code): ?AbstractFilter + { + $filter = array_filter( + $filters, + function (AbstractFilter $filter) use ($code) { + return $filter->getData('attribute_model') + && $filter->getData('attribute_model')->getAttributeCode() == $code; + } + ); + + return array_shift($filter); + } + + /** + * Updates attribute and products data. + * + * @param string $attributeCode + * @param int $filterable + * @param array $products + * @return void + */ + protected function updateAttributeAndProducts( + string $attributeCode, + int $filterable, + array $products + ): void { + $attribute = $this->attributeRepository->get($attributeCode); + $attribute->setData('is_filterable', $filterable); + $this->attributeRepository->save($attribute); + + foreach ($products as $productSku => $stringValue) { + $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); + $product->addData( + [$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)] + ); + $this->productRepository->save($product); + } + } + + /** + * Returns filter items as array. + * + * @param AbstractFilter $filter + * @return array + */ + protected function prepareFilterItems(AbstractFilter $filter): array + { + $items = []; + /** @var Item $item */ + foreach ($filter->getItems() as $item) { + $item = [ + 'label' => $item->getData('label'), + 'count' => $item->getData('count'), + ]; + $items[] = $item; + } + + return $items; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php new file mode 100644 index 0000000000000..5a1774eab549f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; + +/** + * Provides tests for custom boolean filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class BooleanFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('boolean_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'boolean_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 'Yes', + 'simple1001' => 'Yes', + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + ['label' => 'Yes', 'count' => 2], + ] + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'simple1000' => 'Yes', + 'simple1001' => 'Yes', + ], + 'filterable' => 2, + 'expectation' => [ + ['label' => 'Yes', 'count' => 2], + ['label' => 'No', 'count' => 0], + ] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php new file mode 100644 index 0000000000000..f97977528a4e0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item; +use Magento\Store\Model\Store; + +/** + * Provides tests for custom price filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class DecimalFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('decimal_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'decimal_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * Returns filter items as array. + * + * @param AbstractFilter $filter + * @return array + */ + protected function prepareFilterItems(AbstractFilter $filter): array + { + $items = []; + /** @var Item $item */ + foreach ($filter->getItems() as $item) { + $item = [ + 'label' => __($item->getData('label'))->render(), + 'value' => $item->getData('value'), + 'count' => $item->getData('count'), + ]; + $items[] = $item; + } + + return $items; + } + + /** + * @inheritdoc + */ + protected function updateAttributeAndProducts( + string $attributeCode, + int $filterable, + array $products + ): void { + $attribute = $this->attributeRepository->get($attributeCode); + $attribute->setData('is_filterable', $filterable); + $this->attributeRepository->save($attribute); + + foreach ($products as $productSku => $value) { + $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); + $product->addData( + [$attribute->getAttributeCode() => $value] + ); + $this->productRepository->save($product); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 10.00, + 'simple1001' => 20.00, + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + [ + 'label' => '<span class="price">$10.00</span> - <span class="price">$19.99</span>', + 'value' => '10-20', + 'count' => 1, + ], + [ + 'label' => '<span class="price">$20.00</span> and above', + 'value' => '20-', + 'count' => 1, + ], + ] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php new file mode 100644 index 0000000000000..d20b5d0f6d01d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; + +/** + * Provides tests for custom multiselect filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class MultiselectFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('multiselect_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'multiselect_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ] + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => 2, + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ['label' => 'Option 3', 'count' => 0], + ['label' => 'Option 4 "!@#$%^&*', 'count' => 0], + ] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php new file mode 100644 index 0000000000000..f09018e8a707c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; + +/** + * Provides tests for custom select filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class SelectFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('dropdown_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'dropdown_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ] + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => 2, + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ['label' => 'Option 3', 'count' => 0], + ] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php new file mode 100644 index 0000000000000..9f10c28c6693e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php @@ -0,0 +1,152 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Block\Navigation; + +use Magento\Catalog\Api\Data\CategoryInterfaceFactory; +use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Provides tests for filters block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class CategoryTest extends AbstractCategoryTest +{ + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category.php + * @return void + */ + public function testCanShowBlockWithoutProducts(): void + { + $this->prepareNavigationBlock('Category 1'); + $this->assertFalse($this->navigationBlock->canShowBlock()); + $this->assertCount(0, $this->navigationBlock->getLayer()->getProductCollection()->getItems()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @return void + */ + public function testCanShowBlockWithoutFilterOptions(): void + { + $this->prepareNavigationBlock('Category 1'); + $this->assertFalse($this->navigationBlock->canShowBlock()); + $this->assertCount(1, $this->navigationBlock->getLayer()->getProductCollection()->getItems()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider canShowBlockWithDisplayModeDataProvider + * @param string $displayMode + * @param bool $canShow + * @return void + */ + public function testCanShowBlockWithDisplayMode(string $displayMode, bool $canShow): void + { + $this->updateTestCategory($displayMode, 'Category 999'); + $this->prepareNavigationBlock('Category 999'); + $this->assertEquals($canShow, $this->navigationBlock->canShowBlock()); + } + + /** + * @return array + */ + public function canShowBlockWithDisplayModeDataProvider(): array + { + return [ + 'with_mode_products' => ['mode' => CategoryModel::DM_PRODUCT, 'can_show' => true], + 'with_mode_cms_block' => ['mode' => CategoryModel::DM_PAGE, 'can_show' => false], + 'with_mode_cms_block_and_products' => ['mode' => CategoryModel::DM_MIXED, 'can_show' => true], + ]; + } + + /** + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider canShowBlockWithDisplayModeDataProviderOnStoreView + * @param string $defaultMode + * @param string $storeMode + * @param bool $canShow + * @return void + */ + public function testCanShowBlockWithDisplayModeOnStoreView( + string $defaultMode, + string $storeMode, + bool $canShow + ): void { + $secondStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId(); + $this->updateTestCategory($defaultMode, 'Category 999'); + $this->updateTestCategory($storeMode, 'Category 999', $secondStoreId); + $this->prepareNavigationBlock('Category 999', $secondStoreId); + $this->assertEquals($canShow, $this->navigationBlock->canShowBlock()); + } + + /** + * @return array + */ + public function canShowBlockWithDisplayModeDataProviderOnStoreView(): array + { + return [ + 'with_mode_products' => [ + 'default_mode' => CategoryModel::DM_PAGE, + 'store_mode' => CategoryModel::DM_PRODUCT, + 'can_show' => true, + ], + 'with_mode_cms_block' => [ + 'default_mode' => CategoryModel::DM_PRODUCT, + 'store_mode' => CategoryModel::DM_PAGE, + 'can_show' => false + ], + 'with_mode_cms_block_and_products' => [ + 'default_mode' => CategoryModel::DM_PAGE, + 'store_mode' => CategoryModel::DM_MIXED, + 'can_show' => true + ], + ]; + } + + /** + * Updates category display mode. + * + * @param string $displayMode + * @param string $categoryName + * @param int $storeId + * @return void + */ + private function updateTestCategory( + string $displayMode, + string $categoryName, + int $storeId = Store::DEFAULT_STORE_ID + ): void { + $category = $this->loadCategory($categoryName, $storeId); + $currentMode = $category->getData('display_mode'); + + if ($currentMode !== $displayMode) { + $category->setData('display_mode', $displayMode); + $this->categoryResource->save($category); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute.php index 8aaf85959fdb1..934c150049b27 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_text_swatch_attribute.php @@ -26,6 +26,7 @@ 'is_required' => '0', 'attribute_code' => 'text_swatch_attribute', 'is_global' => '1', + 'is_user_defined' => 1, 'is_unique' => '0', 'is_searchable' => '0', 'is_comparable' => '0', diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php new file mode 100644 index 0000000000000..cded9ad640a8a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Catalog\Setup\CategorySetup; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +$installer = $objectManager->create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'visual_swatch_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'frontend_label' => ['Visual swatch attribute'], + 'entity_type_id' => $entityType, + 'frontend_input' => 'select', + 'backend_type' => 'int', + 'is_required' => '0', + 'attribute_code' => 'visual_swatch_attribute', + 'is_global' => '1', + 'is_user_defined' => 1, + 'is_unique' => '0', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_filterable' => '1', + 'is_filterable_in_search' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'used_in_product_listing' => '1', + 'used_for_sort_by' => '0', + 'swatch_input_type' => 'visual', + 'swatchvisual' => [ + 'value' => [ + 'option_1' => '#555555', + 'option_2' => '#aaaaaa', + 'option_3' => '#ffffff', + ], + ], + 'optionvisual' => [ + 'value' => [ + 'option_1' => ['option 1'], + 'option_2' => ['option 2'], + 'option_3' => ['option 3'] + ], + ], + 'options' => [ + 'option' => [ + ['label' => 'Option 1', 'value' => 'option_1'], + ['label' => 'Option 2', 'value' => 'option_2'], + ['label' => 'Option 3', 'value' => 'option_3'], + ], + ], + ] + ); + $attribute->save(); + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php new file mode 100644 index 0000000000000..67157532bdb98 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('text_swatch_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php new file mode 100644 index 0000000000000..cf5c967e88481 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesLayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\LayeredNavigation\Block\Navigation\Category\AbstractFiltersTest; + +/** + * Provides tests for custom text swatch filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class SwatchTextFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('text_swatch_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'text_swatch_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ] + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'simple1000' => 'Option 1', + 'simple1001' => 'Option 2', + ], + 'filterable' => 2, + 'expectation' => [ + ['label' => 'Option 3', 'count' => 0], + ['label' => 'Option 1', 'count' => 1], + ['label' => 'Option 2', 'count' => 1], + ] + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php new file mode 100644 index 0000000000000..ce2794acff47d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesLayeredNavigation\Block\Navigation\Category; + +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\LayeredNavigation\Block\Navigation\Category\AbstractFiltersTest; + +/** + * Provides tests for custom text swatch filter in navigation block on category page. + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ +class SwatchVisualFilterTest extends AbstractFiltersTest +{ + /** + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php + * @dataProvider getFiltersWithCustomAttributeDataProvider + * @param array $products + * @param int $filterable + * @param array $expectation + * @return void + */ + public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void + { + $this->updateAttributeAndProducts('visual_swatch_attribute', $filterable, $products); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'visual_swatch_attribute'); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + + /** + * @return array + */ + public function getFiltersWithCustomAttributeDataProvider(): array + { + return [ + 'not_used_in_navigation' => [ + 'products_data' => [], + 'filterable' => 0, + 'expectation' => [] + ], + 'used_in_navigation_with_results' => [ + 'products_data' => [ + 'simple1000' => 'option 1', + 'simple1001' => 'option 2', + ], + 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, + 'expectation' => [ + ['label' => 'option 1', 'count' => 1], + ['label' => 'option 2', 'count' => 1], + ] + ], + 'used_in_navigation_without_results' => [ + 'products_data' => [ + 'simple1000' => 'option 1', + 'simple1001' => 'option 2', + ], + 'filterable' => 2, + 'expectation' => [ + ['label' => 'option 1', 'count' => 1], + ['label' => 'option 2', 'count' => 1], + ['label' => 'option 3', 'count' => 0], + ] + ], + ]; + } +} From 8eb66b564753fd3fab258189a368c05d771eb2a7 Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Mon, 18 Nov 2019 15:59:36 +0100 Subject: [PATCH 2105/2437] Test more of a public api then internal behaviour --- .../tests/lib/mage/requirejs/mixins.test.js | 199 +++++++++++------- 1 file changed, 119 insertions(+), 80 deletions(-) diff --git a/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js index 3d85f77b87467..52374a24e8c68 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js @@ -4,6 +4,8 @@ */ /* eslint max-nested-callbacks: 0 */ +// jscs:disable jsDoc + require.config({ paths: { 'mixins': 'mage/requirejs/mixins' @@ -13,136 +15,173 @@ require.config({ define(['rjsResolver', 'mixins'], function (resolver, mixins) { 'use strict'; - var context = { - config: {} - }; - describe('mixins module', function () { beforeEach(function (done) { + spyOn(mixins, 'hasMixins').and.callThrough(); + spyOn(mixins, 'getMixins').and.callThrough(); + spyOn(mixins, 'load').and.callThrough(); + // Wait for all modules to be loaded so they don't interfere with testing. resolver(function () { done(); }); }); - describe('processNames method', function () { - beforeEach(function () { - spyOn(mixins, 'processNames').and.callThrough(); - spyOn(mixins, 'hasMixins').and.callThrough(); - }); + it('does not affect modules without mixins', function (done) { + var name = 'tests/assets/mixins/no-mixins', + mixinName = 'tests/assets/mixins/no-mixins-ext'; - it('gets called when module is both required and defined', function (done) { - var name = 'tests/assets/mixins/defined-module', - dependencyName = 'tests/assets/mixins/defined-module-dependency'; + mixins.hasMixins.and.returnValue(false); - define(dependencyName, [], function () {}); - define(name, [dependencyName], function () {}); + define(name, [], function () { + return { + value: 'original' + }; + }); + + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; - require([name], function () { - expect(mixins.processNames.calls.argsFor(0)[0]).toEqual([]); - expect(mixins.processNames.calls.argsFor(1)[0]).toEqual([dependencyName]); - expect(mixins.processNames.calls.argsFor(2)[0]).toEqual([name]); - done(); - }); + return module; + }; }); - it('keeps name intact when it already contains another plugin', function () { - mixins.hasMixins.and.returnValue(true); + require([name], function (module) { + expect(module.value).toBe('original'); - expect(mixins.processNames('plugin!module', context)).toBe('plugin!module'); + done(); }); + }); + + it('does not affect modules that are loaded with plugins', function (done) { + var name = 'plugin!tests/assets/mixins/no-mixins', + mixinName = 'tests/assets/mixins/no-mixins-ext'; - it('keeps name intact when it has no mixins', function () { - mixins.hasMixins.and.returnValue(false); + mixins.hasMixins.and.returnValue(true); + mixins.getMixins.and.returnValue([mixinName]); + + define('plugin', [], function () { + return { + load: function (module, req, onLoad) { + req(module, onLoad); + } + }; + }); - expect(mixins.processNames('module', context)).toBe('module'); + define(name, [], function () { + return { + value: 'original' + }; }); - it('keeps names intact when they have no mixins', function () { - mixins.hasMixins.and.returnValue(false); + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; - expect(mixins.processNames(['module'], context)).toEqual(['module']); + return module; + }; }); - it('adds prefix to name when it has mixins', function () { - mixins.hasMixins.and.returnValue(true); + require([name], function (module) { + expect(module.value).toBe('original'); - expect(mixins.processNames('module', context)).toBe('mixins!module'); + done(); }); + }); + + it('applies mixins for normal module with mixins', function (done) { + var name = 'tests/assets/mixins/mixins-applied', + mixinName = 'tests/assets/mixins/mixins-applied-ext'; - it('adds prefix to name when it contains a relative path', function () { - mixins.hasMixins.and.returnValue(false); + mixins.hasMixins.and.returnValue(true); + mixins.getMixins.and.returnValue([mixinName]); - expect(mixins.processNames('./module', context)).toBe('mixins!./module'); + define(name, [], function () { + return { + value: 'original' + }; }); - it('adds prefix to names when they contain a relative path', function () { - mixins.hasMixins.and.returnValue(false); + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; - expect(mixins.processNames(['./module'], context)).toEqual(['mixins!./module']); + return module; + }; }); - it('adds prefix to names when they have mixins', function () { - mixins.hasMixins.and.returnValue(true); + require([name], function (module) { + expect(module.value).toBe('changed'); - expect(mixins.processNames(['module'], context)).toEqual(['mixins!module']); + done(); }); }); - describe('load method', function () { - it('is not called when module has mixins', function (done) { - var name = 'tests/assets/mixins/load-not-called'; - - spyOn(mixins, 'hasMixins').and.returnValue(false); - spyOn(mixins, 'load').and.callThrough(); + it('applies mixins for module that is a dependency', function (done) { + var name = 'tests/assets/mixins/module-with-dependency', + dependencyName = 'tests/assets/mixins/dependency-module', + mixinName = 'tests/assets/mixins/dependency-module-ext'; - define(name, [], function () {}); + mixins.hasMixins.and.returnValue(true); + mixins.getMixins.and.returnValue([mixinName]); - require([name], function () { - expect(mixins.load.calls.any()).toBe(false); - done(); - }); + define(dependencyName, [], function () { + return { + value: 'original' + }; }); - it('is called when module has mixins', function (done) { - var name = 'tests/assets/mixins/load-called'; + define(name, [dependencyName], function (module) { + expect(module.value).toBe('changed'); - spyOn(mixins, 'hasMixins').and.returnValue(true); - spyOn(mixins, 'load').and.callThrough(); + done(); - define(name, [], function () {}); + return {}; + }); - require([name], function () { - expect(mixins.load.calls.mostRecent().args[0]).toEqual(name); - done(); - }); + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; + + return module; + }; }); - it('applies mixins for loaded module', function (done) { - var name = 'tests/assets/mixins/mixins-applied', - mixinName = 'tests/assets/mixins/mixins-applied-ext'; + require([name], function () {}); + }); - spyOn(mixins, 'hasMixins').and.returnValue(true); - spyOn(mixins, 'load').and.callThrough(); - spyOn(mixins, 'getMixins').and.returnValue([mixinName]); + it('applies mixins for module that is a relative dependency', function (done) { + var name = 'tests/assets/mixins/module-with-relative-dependency', + dependencyName = 'tests/assets/mixins/relative-module', + mixinName = 'tests/assets/mixins/relative-module-ext'; - define(name, [], function () { - return { value: 'original' }; - }); + mixins.hasMixins.and.returnValue(true); + mixins.getMixins.and.returnValue([mixinName]); - define(mixinName, [], function () { - return function(module) { - module.value = 'changed'; + define(dependencyName, [], function () { + return { + value: 'original' + }; + }); - return module; - }; - }); + define(name, ['./relative-module'], function (module) { + expect(module.value).toBe('changed'); - require([name], function (module) { - expect(module.value).toBe('changed'); - done(); - }); + done(); + + return {}; + }); + + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; + + return module; + }; }); + + require([name], function () {}); }); }); }); From 53515bdd1d06206f3f126254aaa719abbbc2e8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Corr=C3=AAa=20Gomes?= <rafaelcgstz@gmail.com> Date: Mon, 18 Nov 2019 15:10:36 -0300 Subject: [PATCH 2106/2437] Telephone Widget > Template > Removing this and helper --- .../view/frontend/templates/widget/telephone.phtml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml index c444a15705909..4c3432233189b 100644 --- a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml @@ -14,18 +14,14 @@ </span> </label> <div class="control"> - <?php - $_validationClass = $block->escapeHtmlAttr( - $this->helper(\Magento\Customer\Helper\Address::class) - ->getAttributeValidationClass('telephone') - ); - ?> <input type="text" name="telephone" id="telephone" value="<?= $block->escapeHtmlAttr($block->getTelephone()) ?>" title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" - class="input-text <?= $_validationClass ?: '' ?>" + class="input-text <?= $block->escapeHtmlAttr( + $block->getAttributeValidationClass('telephone') + ) ?>" > </div> </div> From 944ebbeb0fd720be4223e9f4e4b4de5e80e160d4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 18 Nov 2019 14:11:07 -0600 Subject: [PATCH 2107/2437] MC-21948: Products index data delete process fires before indexation instead of right before insert indexed data to index tables during partial reindex --- .../Catalog/Model/Indexer/Category/Product/Action/Rows.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php index 7f4b12e0d8235..de8dc350a3c4c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php @@ -85,6 +85,7 @@ public function __construct( * @param int[] $entityIds * @param bool $useTempTable * @return $this + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(array $entityIds = [], $useTempTable = false) { From 6defcedcbf47d4c8f5ce11a6c97252856e306b4f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 18 Nov 2019 14:46:54 -0600 Subject: [PATCH 2108/2437] MC-21948: Products index data delete process fires before indexation instead of right before insert indexed data to index tables during partial reindex --- .../Catalog/Observer/CategoryProductIndexer.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Observer/CategoryProductIndexer.php b/app/code/Magento/Catalog/Observer/CategoryProductIndexer.php index bdee84762cac2..ca87efaa87490 100644 --- a/app/code/Magento/Catalog/Observer/CategoryProductIndexer.php +++ b/app/code/Magento/Catalog/Observer/CategoryProductIndexer.php @@ -8,7 +8,6 @@ namespace Magento\Catalog\Observer; use Magento\Catalog\Model\Indexer\Category\Product\Processor; -use Magento\Catalog\Model\Indexer\Category\Flat\State as FlatState; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; @@ -22,21 +21,12 @@ class CategoryProductIndexer implements ObserverInterface */ private $processor; - /** - * @var FlatState - */ - private $flatState; - /** * @param Processor $processor - * @param FlatState $flatState */ - public function __construct( - Processor $processor, - FlatState $flatState - ) { + public function __construct(Processor $processor) + { $this->processor = $processor; - $this->flatState = $flatState; } /** @@ -45,7 +35,7 @@ public function __construct( public function execute(Observer $observer): void { $productIds = $observer->getEvent()->getProductIds(); - if (!empty($productIds) && $this->processor->isIndexerScheduled() && $this->flatState->isFlatEnabled()) { + if (!empty($productIds) && $this->processor->isIndexerScheduled()) { $this->processor->markIndexerAsInvalid(); } } From 5be08d3345a79f5a6d8f62347c5da5ff4e20900a Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 18 Nov 2019 15:34:37 -0600 Subject: [PATCH 2109/2437] MC-21948: Products index data delete process fires before indexation instead of right before insert indexed data to index tables during partial reindex --- .../Elasticsearch/Controller/Adminhtml/Category/SaveTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php index b505e311b9ed0..6e21dfcab6a89 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php @@ -78,6 +78,11 @@ public function testExecute() self::equalTo(['You saved the category.']), MessageInterface::TYPE_SUCCESS ); + + $fulltextIndexer = $this->getIndexer(FulltextIndexer::INDEXER_ID); + self::assertTrue($fulltextIndexer->isInvalid(), 'Fulltext indexer should be invalidated.'); + $categoryIndexer = $this->getIndexer(CategoryIndexer::INDEXER_ID); + self::assertTrue($categoryIndexer->isInvalid(), 'Category indexer should be invalidated.'); } /** From f47033caedb36ad989933f08bf52c1d4fb6aa6ad Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Mon, 18 Nov 2019 15:51:16 -0600 Subject: [PATCH 2110/2437] MC-22813: Changing Quantity and Adding to cart through Quick Order throws exception - Add guard for getChildren --- .../Plugin/SalesRule/Model/Rule/Condition/Product.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php index 1ed4432347b7a..c97b24c295189 100644 --- a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php @@ -56,7 +56,10 @@ private function getProductToValidate( $attrCode = $subject->getAttribute(); /* Check for attributes which are not available for configurable products */ - if ($product->getTypeId() == Configurable::TYPE_CODE && !$product->hasData($attrCode)) { + if ($product->getTypeId() == Configurable::TYPE_CODE && + !$product->hasData($attrCode) && + count($model->getChildren()) + ) { /** @var \Magento\Catalog\Model\AbstractModel $childProduct */ $childProduct = current($model->getChildren())->getProduct(); if ($childProduct->hasData($attrCode)) { From 919c8e4bddd06d174a18f7c9479b191939791e5a Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 18 Nov 2019 16:23:35 -0600 Subject: [PATCH 2111/2437] MC-21948: Products index data delete process fires before indexation instead of right before insert indexed data to index tables during partial reindex --- .../Catalog/Model/Indexer/Category/Product/Action/Rows.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php index de8dc350a3c4c..5d81c1405efe0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php @@ -5,11 +5,6 @@ */ namespace Magento\Catalog\Model\Indexer\Category\Product\Action; -/** - * Reindex multiple rows action. - * - * @package Magento\Catalog\Model\Indexer\Category\Product\Action - */ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Indexer\CacheContext; use Magento\Framework\Event\ManagerInterface as EventManagerInterface; From 8a4be0dba34b7895147faf1f72520ca6c2f0ef4d Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Mon, 18 Nov 2019 17:01:56 -0600 Subject: [PATCH 2112/2437] MC-23192: 'Preview template' functionality places the whole email template into GET parameter - fixed --- .../Newsletter/Controller/Adminhtml/Template/Preview.php | 6 +++--- .../Newsletter/view/adminhtml/templates/template/edit.phtml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php index 9fd9f4335b5c5..784a62f86b145 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php @@ -6,12 +6,12 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Template; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * View a rendered template. */ -class Preview extends \Magento\Newsletter\Controller\Adminhtml\Template implements HttpGetActionInterface +class Preview extends \Magento\Newsletter\Controller\Adminhtml\Template implements HttpPostActionInterface { /** * Preview Newsletter template @@ -25,7 +25,7 @@ public function execute() $data = $this->getRequest()->getParams(); $isEmptyRequestData = empty($data) || !isset($data['id']); $isEmptyPreviewData = !$this->_getSession()->hasPreviewData() || empty($this->_getSession()->getPreviewData()); - + if ($isEmptyRequestData && $isEmptyPreviewData) { $this->_forward('noroute'); return $this; diff --git a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml index c49a5c61a7172..abc56070b6892 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml @@ -17,12 +17,13 @@ use Magento\Framework\App\TemplateTypesInterface; </div> <?= /* @noEscape */ $block->getForm() ?> </form> -<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="get" id="newsletter_template_preview_form" target="_blank"> +<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="post" id="newsletter_template_preview_form" target="_blank"> <div class="no-display"> <input type="hidden" id="preview_type" name="type" value="<?= /* @noEscape */ $block->isTextType() ? 1 : 2 ?>" /> <input type="hidden" id="preview_text" name="text" value="" /> <input type="hidden" id="preview_styles" name="styles" value="" /> <input type="hidden" id="preview_id" name="id" value="" /> + <input type="hidden" name="form_key" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" > </div> </form> <script> From e2596bb7a5945cafba61fadcfc71669ad3a3df62 Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <bensch.rosenberger@gmail.com> Date: Tue, 19 Nov 2019 06:58:34 +0100 Subject: [PATCH 2113/2437] fix code styling errors --- .../Console/Command/IndexerStatusCommand.php | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php index f5125fddf34b2..561ff759a4a59 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php @@ -5,11 +5,11 @@ */ namespace Magento\Indexer\Console\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Magento\Framework\Indexer; use Magento\Framework\Mview; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; /** * Command for displaying status of indexers. @@ -17,7 +17,7 @@ class IndexerStatusCommand extends AbstractIndexerManageCommand { /** - * {@inheritdoc} + * @inheritdoc */ protected function configure() { @@ -29,7 +29,9 @@ protected function configure() } /** - * {@inheritdoc} + * @inheritdoc + * @param InputInterface $input + * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -60,15 +62,20 @@ protected function execute(InputInterface $input, OutputInterface $output) $rows[] = $rowData; } - usort($rows, function ($comp1, $comp2) { - return strcmp($comp1['Title'], $comp2['Title']); - }); + usort( + $rows, + function ($comp1, $comp2) { + return strcmp($comp1['Title'], $comp2['Title']); + } + ); $table->addRows($rows); $table->render(); } /** + * Returns the current status of the indexer + * * @param Indexer\IndexerInterface $indexer * @return string */ @@ -90,6 +97,8 @@ private function getStatus(Indexer\IndexerInterface $indexer) } /** + * Returns the pending count of the view + * * @param Mview\ViewInterface $view * @return string */ From ecfc973d69d025954e81cf52451c32764c2afb33 Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <bensch.rosenberger@gmail.com> Date: Tue, 19 Nov 2019 06:59:37 +0100 Subject: [PATCH 2114/2437] fix code stylings --- .../Test/Unit/Console/Command/IndexerStatusCommandTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php index 2aa55950e762e..963a0b21c1f96 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php @@ -96,7 +96,8 @@ public function testExecuteAll(array $indexers) $linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay())); - $spacer = '+-----------+----------------+------------------+-----------+-------------------------+---------------------+'; + $spacer = '+-----------+----------------+------------------+-----------+-------------------------+' + . '---------------------+'; $this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.'); $this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines"); From d1aaf740493b4d57bcf6710e2433a0584d0164a2 Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov <serhii.voloshkov@transoftgroup.com> Date: Tue, 19 Nov 2019 10:47:58 +0200 Subject: [PATCH 2115/2437] MC-19235: Quote improvement in TestFramework --- .../TestFramework/Quote/Model/GetQuoteByReservedOrderId.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php index 1d2a1a92fc128..bd83bc595692f 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php @@ -41,8 +41,8 @@ public function __construct(SearchCriteriaBuilder $searchCriteriaBuilder, CartRe public function execute(string $reservedOrderId): ?CartInterface { $searchCriteria = $this->searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)->create(); - $quote = $this->cartRepository->getList($searchCriteria)->getItems(); + $quotes = $this->cartRepository->getList($searchCriteria)->getItems(); - return array_shift($quote); + return array_shift($quotes); } } From fa416396e8fa14426077bc0de5150d1ca8d9fc45 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Nov 2019 10:53:47 +0200 Subject: [PATCH 2116/2437] MC-23193: Tier Prices error on product update --- .../Product/Attribute/Backend/TierPrice/UpdateHandler.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index f1943bc108878..0daa1dfb5c8eb 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -96,12 +96,7 @@ public function execute($entity, $arguments = []) $productId = (int)$entity->getData($identifierField); // prepare original data to compare - $origPrices = []; - $originalId = $entity->getOrigData($identifierField); - if (empty($originalId) || $entity->getData($identifierField) == $originalId) { - $origPrices = $entity->getOrigData($attribute->getName()); - } - + $origPrices = $entity->getOrigData($attribute->getName()); $old = $this->prepareOldTierPriceToCompare($origPrices); // prepare data for save $new = $this->prepareNewDataForSave($priceRows, $isGlobal); From 4b0538fbde07487ee6eebe899872b23c7c42a17d Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Tue, 19 Nov 2019 12:07:43 +0200 Subject: [PATCH 2117/2437] MC-22738: Layered Navigation with different product attributes on Category page --- .../_files/category_with_different_price_products.php | 2 +- .../category_with_different_price_products_rollback.php | 3 +++ .../Block/Navigation/AbstractCategoryTest.php | 2 +- .../Block/Navigation/Category/SelectFilterTest.php | 6 +++--- .../Swatches/_files/product_visual_swatch_attribute.php | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php index 8346f43d0dded..2500b546666a1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php @@ -15,7 +15,7 @@ use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); -$categoryFactory = Bootstrap::getObjectManager()->get(CategoryInterfaceFactory::class); +$categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); $productFactory = $objectManager->get(ProductInterfaceFactory::class); $productRepository = $objectManager->get(ProductRepositoryInterface::class); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php index 3eb09edc32943..4658a5b9bc8af 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_rollback.php @@ -23,11 +23,13 @@ try { $productRepository->deleteById('simple1000'); } catch (NoSuchEntityException $e) { + //Already deleted. } try { $productRepository->deleteById('simple1001'); } catch (NoSuchEntityException $e) { + //Already deleted. } try { @@ -38,6 +40,7 @@ ->getFirstItem(); $categoryRepository->delete($category); } catch (NoSuchEntityException $e) { + //Already deleted. } $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php index ceac508b7af92..aad0fcd08656f 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractCategoryTest.php @@ -56,7 +56,7 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); $this->categoryCollectionFactory = $this->objectManager->create(CollectionFactory::class); $this->categoryResource = $this->objectManager->get(CategoryResource::class); - $this->layout = $this->objectManager->create(LayoutInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); $this->navigationBlock = $this->objectManager->create(Category::class); parent::setUp(); } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php index f09018e8a707c..b9fc8865b4354 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php @@ -50,7 +50,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -61,7 +61,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'expectation' => [ ['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1], - ] + ], ], 'used_in_navigation_without_results' => [ 'products_data' => [ @@ -73,7 +73,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array ['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1], ['label' => 'Option 3', 'count' => 0], - ] + ], ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php index cded9ad640a8a..e567e12ff4782 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/_files/product_visual_swatch_attribute.php @@ -60,7 +60,7 @@ ], ] ); - $attribute->save(); + $attributeRepository->save($attribute); $installer->addAttributeToGroup( ProductAttributeInterface::ENTITY_TYPE_CODE, 'Default', From a9ca041d177e32745e24f0de4e68ed64985a17e3 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Tue, 19 Nov 2019 12:42:18 +0200 Subject: [PATCH 2118/2437] MC-18057: Product doesn'y match by "Date" attribute condition --- .../product_simple_with_date_attribute.php | 40 +++++++++++++++++-- ...ct_simple_with_date_attribute_rollback.php | 12 ++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php index 2c78d9b74f969..46745c213845a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php @@ -10,10 +10,11 @@ $attribute->setData('is_used_for_promo_rules', 1); -/** @var $product \Magento\Catalog\Model\Product */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(Magento\Catalog\Model\ProductFactory::class); +$product = $productFactory->create(); $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setAttributeSetId(4) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) ->setWebsiteIds([1]) ->setName('Simple Product with date') ->setSku('simple_with_date') @@ -24,4 +25,37 @@ ->setCategoryIds([2]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->setDateAttribute(date('Y-m-d')) + ->setUrlKey('simple_with_date') + ->save(); + +$product2 = $productFactory->create(); +$product2->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setWebsiteIds([1]) + ->setName('Simple Product with date -1') + ->setSku('simple_with_date2') + ->setPrice(10) + ->setDescription('Description with <b>html tag</b>') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d', strtotime(date('Y-m-d') . '-1 day'))) + ->setUrlKey('simple_with_date2') + ->save(); + +$product3 = $productFactory->create(); +$product3->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setWebsiteIds([1]) + ->setName('Simple Product with date +1') + ->setSku('simple_with_date3') + ->setPrice(10) + ->setDescription('Description with <b>html tag</b>') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d', strtotime(date('Y-m-d') . '+1 day'))) + ->setUrlKey('simple_with_date3') ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php index 0f155926dd6db..da61eb1d2332b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php @@ -26,6 +26,18 @@ } catch (NoSuchEntityException $e) { } +try { + $product = $productRepository->get('simple_with_date2', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +try { + $product = $productRepository->get('simple_with_date3', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); From ae51778a27d17970cdd916af36b1cb435b6821ec Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Tue, 19 Nov 2019 13:17:46 +0200 Subject: [PATCH 2119/2437] MC-22738: Layered Navigation with different product attributes on Category page --- .../Block/Navigation/Category/BooleanFilterTest.php | 6 +++--- .../Block/Navigation/Category/DecimalFilterTest.php | 4 ++-- .../Block/Navigation/Category/MultiselectFilterTest.php | 6 +++--- .../Block/Navigation/Category/SwatchTextFilterTest.php | 6 +++--- .../Block/Navigation/Category/SwatchVisualFilterTest.php | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php index 5a1774eab549f..b8edd7f460862 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php @@ -50,7 +50,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -60,7 +60,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS, 'expectation' => [ ['label' => 'Yes', 'count' => 2], - ] + ], ], 'used_in_navigation_without_results' => [ 'products_data' => [ @@ -71,7 +71,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'expectation' => [ ['label' => 'Yes', 'count' => 2], ['label' => 'No', 'count' => 0], - ] + ], ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php index f97977528a4e0..dcb69b9a93c26 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php @@ -95,7 +95,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -114,7 +114,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'value' => '20-', 'count' => 1, ], - ] + ], ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php index d20b5d0f6d01d..04ccfc2f04b8b 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php @@ -50,7 +50,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -61,7 +61,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'expectation' => [ ['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1], - ] + ], ], 'used_in_navigation_without_results' => [ 'products_data' => [ @@ -74,7 +74,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array ['label' => 'Option 2', 'count' => 1], ['label' => 'Option 3', 'count' => 0], ['label' => 'Option 4 "!@#$%^&*', 'count' => 0], - ] + ], ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php index cf5c967e88481..d7c47fd98ce4a 100644 --- a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php @@ -51,7 +51,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -62,7 +62,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'expectation' => [ ['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1], - ] + ], ], 'used_in_navigation_without_results' => [ 'products_data' => [ @@ -74,7 +74,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array ['label' => 'Option 3', 'count' => 0], ['label' => 'Option 1', 'count' => 1], ['label' => 'Option 2', 'count' => 1], - ] + ], ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php index ce2794acff47d..06838b03d9f76 100644 --- a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php @@ -51,7 +51,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'not_used_in_navigation' => [ 'products_data' => [], 'filterable' => 0, - 'expectation' => [] + 'expectation' => [], ], 'used_in_navigation_with_results' => [ 'products_data' => [ @@ -62,7 +62,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array 'expectation' => [ ['label' => 'option 1', 'count' => 1], ['label' => 'option 2', 'count' => 1], - ] + ], ], 'used_in_navigation_without_results' => [ 'products_data' => [ @@ -74,7 +74,7 @@ public function getFiltersWithCustomAttributeDataProvider(): array ['label' => 'option 1', 'count' => 1], ['label' => 'option 2', 'count' => 1], ['label' => 'option 3', 'count' => 0], - ] + ], ], ]; } From f2bfadee7b32e72800025f3f74e5ee7b97e7f6db Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Nov 2019 13:37:11 +0200 Subject: [PATCH 2120/2437] MC-23193: Tier Prices error on product update --- .../Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php index cce00c50d37af..fde793d5c5f89 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php @@ -108,6 +108,7 @@ public function testExecute(): void ]; $linkField = 'entity_id'; $productId = 10; + $originalProductId = 11; /** @var \PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) @@ -124,7 +125,7 @@ public function testExecute(): void ->willReturnMap( [ ['tier_price', $originalTierPrices], - ['entity_id', $productId] + ['entity_id', $originalProductId] ] ); $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(0); From cb1b7749c0520cb45cd47ec1560c62a06dbb172c Mon Sep 17 00:00:00 2001 From: nathanm <nathanm@fisheyehq.com> Date: Tue, 19 Nov 2019 15:13:17 +0000 Subject: [PATCH 2121/2437] Add escaping on meta properties for open graph Fixes issue where double quotes can bleed though the html attribute --- .../frontend/templates/product/view/opengraph/general.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml index eb2bde647f9b1..4d4a34c6239d4 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml @@ -9,11 +9,11 @@ <meta property="og:type" content="product" /> <meta property="og:title" - content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getName()) ?>" /> + content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getName())) ?>" /> <meta property="og:image" content="<?= $block->escapeUrl($block->getImage($block->getProduct(), 'product_base_image')->getImageUrl()) ?>" /> <meta property="og:description" - content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getShortDescription()) ?>" /> + content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getShortDescription())) ?>" /> <meta property="og:url" content="<?= $block->escapeUrl($block->getProduct()->getProductUrl()) ?>" /> <?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()) :?> <meta property="product:price:amount" content="<?= $block->escapeHtmlAttr($priceAmount) ?>"/> From 95cf76c4aa42b5125b563adb34af565f5b4ef42a Mon Sep 17 00:00:00 2001 From: Alastair Mucklow <amucklow@strangerpixel.com> Date: Tue, 19 Nov 2019 16:00:37 +0000 Subject: [PATCH 2122/2437] Uses > Used for Yes/No column --- .../Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php index bf92e21827a01..b021b3e539a9d 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php @@ -100,7 +100,7 @@ protected function _prepareColumns() $this->addColumn( 'used', [ - 'header' => __('Uses'), + 'header' => __('Used'), 'index' => 'times_used', 'width' => '100', 'type' => 'options', From 8d9e1991789089c04162d5367db929e06227f3ce Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 19 Nov 2019 12:49:51 -0600 Subject: [PATCH 2123/2437] MC-19926: Implement CSP --- .../Collector/Config/FetchPolicyReader.php | 3 +- .../Collector/CspWhitelistXmlCollector.php | 4 +- .../Csp/Model/Collector/FetchPolicyMerger.php | 47 +++++ .../Csp/Model/Collector/FlagPolicyMerger.php | 33 ++++ .../Csp/Model/Collector/MergerInterface.php | 34 ++++ .../Collector/PluginTypesPolicyMerger.php | 35 ++++ .../Model/Collector/SandboxPolicyMerger.php | 47 +++++ .../Csp/Model/CompositePolicyCollector.php | 42 ++++- .../Magento/Csp/Model/Policy/FetchPolicy.php | 23 ++- app/code/Magento/Csp/etc/config.xml | 21 +++ app/code/Magento/Csp/etc/di.xml | 6 + .../Model/Collector/ConfigCollectorTest.php | 3 +- .../Model/CompositePolicyCollectorTest.php | 177 ++++++++++++++++++ .../Csp/Model/Mode/ConfigManagerTest.php | 86 +++++++++ .../SimplePolicyHeaderRendererTest.php | 21 ++- .../HTTP/PhpEnvironment/Response.php | 36 +++- 16 files changed, 600 insertions(+), 18 deletions(-) create mode 100644 app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php create mode 100644 app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php create mode 100644 app/code/Magento/Csp/Model/Collector/MergerInterface.php create mode 100644 app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php create mode 100644 app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php create mode 100644 app/code/Magento/Csp/etc/config.xml create mode 100644 dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php diff --git a/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php index 8728755c594e6..8699d9588b909 100644 --- a/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php +++ b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php @@ -30,7 +30,8 @@ public function read(string $id, $value): PolicyInterface !empty($value['eval']), [], [], - !empty($value['dynamic']) + !empty($value['dynamic']), + !empty($value['event_handlers']) ); } diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php index f4a75f120dab9..9f19a5299c063 100644 --- a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php @@ -47,7 +47,9 @@ public function collect(array $defaultPolicies = []): array false, false, [], - $values['hashes'] + $values['hashes'], + false, + false ); } diff --git a/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php new file mode 100644 index 0000000000000..2a8f6c278b078 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * @inheritDoc + */ +class FetchPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var FetchPolicy $policy1 */ + /** @var FetchPolicy $policy2 */ + return new FetchPolicy( + $policy1->getId(), + $policy1->isNoneAllowed() || $policy2->isNoneAllowed(), + array_unique(array_merge($policy1->getHostSources(), $policy2->getHostSources())), + array_unique(array_merge($policy1->getSchemeSources(), $policy2->getSchemeSources())), + $policy1->isSelfAllowed() || $policy2->isSelfAllowed(), + $policy1->isInlineAllowed() || $policy2->isInlineAllowed(), + $policy1->isEvalAllowed() || $policy2->isEvalAllowed(), + array_unique(array_merge($policy1->getNonceValues(), $policy2->getNonceValues())), + array_merge($policy1->getHashes(), $policy2->getHashes()), + $policy1->isDynamicAllowed() || $policy2->isDynamicAllowed(), + $policy1->areEventHandlersAllowed() || $policy2->areEventHandlersAllowed() + ); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof FetchPolicy) && ($policy2 instanceof FetchPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php new file mode 100644 index 0000000000000..a734feeab1281 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FlagPolicy; + +/** + * @inheritDoc + */ +class FlagPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + return $policy1; + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof FlagPolicy) && ($policy2 instanceof FlagPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/MergerInterface.php b/app/code/Magento/Csp/Model/Collector/MergerInterface.php new file mode 100644 index 0000000000000..4a8d78e7b8f4b --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/MergerInterface.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Merges policies with the same ID in order to have only 1 policy DTO-per-policy. + */ +interface MergerInterface +{ + /** + * Merges 2 found policies into 1. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return PolicyInterface + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface; + + /** + * Whether current merger can merge given 2 policies. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return bool + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool; +} diff --git a/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php new file mode 100644 index 0000000000000..58f2128657788 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\PluginTypesPolicy; + +/** + * @inheritDoc + */ +class PluginTypesPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var PluginTypesPolicy $policy1 */ + /** @var PluginTypesPolicy $policy2 */ + return new PluginTypesPolicy(array_unique(array_merge($policy1->getTypes(), $policy2->getTypes()))); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof PluginTypesPolicy) && ($policy2 instanceof PluginTypesPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php new file mode 100644 index 0000000000000..1c398a4cfd477 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\SandboxPolicy; + +/** + * @inheritDoc + */ +class SandboxPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var SandboxPolicy $policy1 */ + /** @var SandboxPolicy $policy2 */ + return new SandboxPolicy( + $policy1->isFormAllowed() || $policy2->isFormAllowed(), + $policy1->isModalsAllowed() || $policy2->isModalsAllowed(), + $policy1->isOrientationLockAllowed() || $policy2->isOrientationLockAllowed(), + $policy1->isPointerLockAllowed() || $policy2->isPointerLockAllowed(), + $policy1->isPopupsAllowed() || $policy2->isPopupsAllowed(), + $policy1->isPopupsToEscapeSandboxAllowed() || $policy2->isPopupsToEscapeSandboxAllowed(), + $policy1->isPresentationAllowed() || $policy2->isPresentationAllowed(), + $policy1->isSameOriginAllowed() || $policy2->isSameOriginAllowed(), + $policy1->isScriptsAllowed() || $policy2->isScriptsAllowed(), + $policy1->isTopNavigationAllowed() || $policy2->isTopNavigationAllowed(), + $policy1->isTopNavigationByUserActivationAllowed() || $policy2->isTopNavigationByUserActivationAllowed() + ); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof SandboxPolicy) && ($policy2 instanceof SandboxPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/CompositePolicyCollector.php b/app/code/Magento/Csp/Model/CompositePolicyCollector.php index 31da34960ea75..b775c91b4e1ef 100644 --- a/app/code/Magento/Csp/Model/CompositePolicyCollector.php +++ b/app/code/Magento/Csp/Model/CompositePolicyCollector.php @@ -7,7 +7,9 @@ namespace Magento\Csp\Model; +use Magento\Csp\Api\Data\PolicyInterface; use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\MergerInterface; /** * Delegates collecting to multiple collectors. @@ -19,12 +21,38 @@ class CompositePolicyCollector implements PolicyCollectorInterface */ private $collectors; + /** + * @var MergerInterface[] + */ + private $mergers; + /** * @param PolicyCollectorInterface[] $collectors + * @param MergerInterface[] $mergers */ - public function __construct(array $collectors) + public function __construct(array $collectors, array $mergers) { $this->collectors = $collectors; + $this->mergers = $mergers; + } + + /** + * Merge 2 policies with the same ID. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return PolicyInterface + * @throws \RuntimeException When failed to merge. + */ + private function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + foreach ($this->mergers as $merger) { + if ($merger->canMerge($policy1, $policy2)) { + return $merger->merge($policy1, $policy2); + } + } + + throw new \RuntimeException(sprintf('Merge for policies #%s was not found', $policy1->getId())); } /** @@ -36,7 +64,17 @@ public function collect(array $defaultPolicies = []): array foreach ($this->collectors as $collector) { $collected = $collector->collect($collected); } + //Merging policies. + /** @var PolicyInterface[] $result */ + $result = []; + foreach ($collected as $policy) { + if (array_key_exists($policy->getId(), $result)) { + $result[$policy->getId()] = $this->merge($result[$policy->getId()], $policy); + } else { + $result[$policy->getId()] = $policy; + } + } - return $collected; + return array_values($result); } } diff --git a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php index 76d17e3cb96c7..cb44c20ab2fd4 100644 --- a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php +++ b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php @@ -82,6 +82,11 @@ class FetchPolicy implements SimplePolicyInterface */ private $dynamicAllowed; + /** + * @var bool + */ + private $eventHandlersAllowed; + /** * @param string $id * @param bool $noneAllowed @@ -93,6 +98,7 @@ class FetchPolicy implements SimplePolicyInterface * @param string[] $nonceValues * @param string[] $hashValues * @param bool $dynamicAllowed + * @param bool $eventHandlersAllowed * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -105,7 +111,8 @@ public function __construct( bool $evalAllowed = false, array $nonceValues = [], array $hashValues = [], - bool $dynamicAllowed = false + bool $dynamicAllowed = false, + bool $eventHandlersAllowed = false ) { $this->id = $id; $this->noneAllowed = $noneAllowed; @@ -117,6 +124,7 @@ public function __construct( $this->nonceValues = array_unique($nonceValues); $this->hashes = $hashValues; $this->dynamicAllowed = $dynamicAllowed; + $this->eventHandlersAllowed = $eventHandlersAllowed; } /** @@ -213,6 +221,9 @@ public function getValue(): string if ($this->isDynamicAllowed()) { $sources[] = '\'strict-dynamic\''; } + if ($this->areEventHandlersAllowed()) { + $sources[] = '\'unsafe-hashes\''; + } foreach ($this->getNonceValues() as $nonce) { $sources[] = '\'nonce-' .base64_encode($nonce) .'\''; } @@ -257,4 +268,14 @@ public function isDynamicAllowed(): bool { return $this->dynamicAllowed; } + + /** + * Allows to whitelist event handlers (but not javascript: URLs) with hashes. + * + * @return bool + */ + public function areEventHandlersAllowed(): bool + { + return $this->eventHandlersAllowed; + } } diff --git a/app/code/Magento/Csp/etc/config.xml b/app/code/Magento/Csp/etc/config.xml new file mode 100644 index 0000000000000..e45f6b223ed22 --- /dev/null +++ b/app/code/Magento/Csp/etc/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <csp> + <mode> + <storefront> + <report_only>1</report_only> + </storefront> + <admin> + <report_only>1</report_only> + </admin> + </mode> + </csp> + </default> +</config> diff --git a/app/code/Magento/Csp/etc/di.xml b/app/code/Magento/Csp/etc/di.xml index f18453c91791d..0804f6d579137 100644 --- a/app/code/Magento/Csp/etc/di.xml +++ b/app/code/Magento/Csp/etc/di.xml @@ -21,6 +21,12 @@ <item name="config" xsi:type="object">Magento\Csp\Model\Collector\ConfigCollector</item> <item name="csp_whitelist" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXmlCollector</item> </argument> + <argument name="mergers" xsi:type="array"> + <item name="fetch" xsi:type="object">Magento\Csp\Model\Collector\FetchPolicyMerger</item> + <item name="flag" xsi:type="object">Magento\Csp\Model\Collector\FlagPolicyMerger</item> + <item name="plugins" xsi:type="object">Magento\Csp\Model\Collector\PluginTypesPolicyMerger</item> + <item name="sandbox" xsi:type="object">Magento\Csp\Model\Collector\SandboxPolicyMerger</item> + </argument> </arguments> </type> <type name="Magento\Csp\Model\Collector\Config\PolicyReaderPool"> diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php index ae8013d065194..54f6a4d8fd33f 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -68,7 +68,7 @@ private function getExpectedPolicies(): array 'manifest-src' => new FetchPolicy('manifest-src', false, [], [], true), 'media-src' => new FetchPolicy('media-src', false, [], [], true), 'object-src' => new FetchPolicy('object-src', false, [], [], true), - 'script-src' => new FetchPolicy('script-src', false, [], [], true), + 'script-src' => new FetchPolicy('script-src', false, [], [], true, false, false, [], [], false, true), 'style-src' => new FetchPolicy('style-src', false, [], [], true), 'base-uri' => new FetchPolicy('base-uri', false, [], [], true), 'plugin-types' => new PluginTypesPolicy( @@ -126,6 +126,7 @@ private function getExpectedPolicies(): array * @magentoConfigFixture default_store csp/policies/storefront/script_src/policy_id script-src * @magentoConfigFixture default_store csp/policies/storefront/script_src/none 0 * @magentoConfigFixture default_store csp/policies/storefront/script_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/event_handlers 1 * @magentoConfigFixture default_store csp/policies/storefront/base_uri/policy_id base-uri * @magentoConfigFixture default_store csp/policies/storefront/base_uri/none 0 * @magentoConfigFixture default_store csp/policies/storefront/base_uri/self 1 diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php new file mode 100644 index 0000000000000..fd0c58235de1d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php @@ -0,0 +1,177 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Policy\FetchPolicy; +use Magento\Csp\Model\Policy\FlagPolicy; +use Magento\Csp\Model\Policy\PluginTypesPolicy; +use Magento\Csp\Model\Policy\SandboxPolicy; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test that composite collector properly calls other collectors and merges results. + */ +class CompositePolicyCollectorTest extends TestCase +{ + /** + * Create mock collectors that will populate policies. + * + * @return PolicyCollectorInterface[] + */ + private function createMockCollectors(): array + { + $mockCollector1 = $this->getMockForAbstractClass(PolicyCollectorInterface::class); + $mockCollector1->method('collect') + ->willReturnCallback( + function (array $prevPolicies) { + return array_merge( + $prevPolicies, + [ + new FetchPolicy( + 'script-src', + false, + ['https://magento.com'], + ['https'], + true, + false, + true, + ['569403695046645'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], + false, + true + ), + new FetchPolicy('script-src', false, ['https://devdocs.magento.com']), + new FlagPolicy('upgrade-insecure-requests'), + new PluginTypesPolicy(['application/x-shockwave-flash']), + new SandboxPolicy(false, true, false, true, false, true, false, true, false, true, false) + ] + ); + } + ); + $mockCollector2 = $this->getMockForAbstractClass(PolicyCollectorInterface::class); + $mockCollector2->method('collect') + ->willReturnCallback( + function (array $prevPolicies) { + return array_merge( + $prevPolicies, + [ + new FetchPolicy( + 'script-src', + true, + ['http://magento.com'], + ['http'], + false, + false, + false, + ['5694036950466451'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF7=' => 'sha256'], + true, + false + ), + new FetchPolicy('default-src', false, [], [], true), + new FlagPolicy('upgrade-insecure-requests'), + new PluginTypesPolicy(['application/x-java-applet']), + new SandboxPolicy(true, false, true, false, true, false, true, false, true, false, false) + ] + ); + } + ); + + return [$mockCollector1, $mockCollector2]; + } + + /** + * Test collect method. + * + * Supply fake collectors, check results. + * + * @return void + */ + public function testCollect(): void + { + /** @var CompositePolicyCollector $collector */ + $collector = Bootstrap::getObjectManager()->create( + CompositePolicyCollector::class, + ['collectors' => $this->createMockCollectors()] + ); + + $collected = $collector->collect([]); + /** @var FetchPolicy[]|FlagPolicy[]|PluginTypesPolicy[]|SandboxPolicy[] $policies */ + $policies = []; + /** @var \Magento\Csp\Api\Data\PolicyInterface $policy */ + foreach ($collected as $policy) { + $policies[$policy->getId()] = $policy; + } + //Comparing resulting policies + $this->assertArrayHasKey('script-src', $policies); + $this->assertTrue($policies['script-src']->isNoneAllowed()); + $this->assertTrue($policies['script-src']->isSelfAllowed()); + $this->assertFalse($policies['script-src']->isInlineAllowed()); + $this->assertTrue($policies['script-src']->isEvalAllowed()); + $this->assertTrue($policies['script-src']->isDynamicAllowed()); + $this->assertTrue($policies['script-src']->areEventHandlersAllowed()); + $foundHosts = $policies['script-src']->getHostSources(); + $hosts = ['http://magento.com', 'https://magento.com', 'https://devdocs.magento.com']; + sort($foundHosts); + sort($hosts); + $this->assertEquals($hosts, $foundHosts); + $foundSchemes = $policies['script-src']->getSchemeSources(); + $schemes = ['https', 'http']; + sort($foundSchemes); + sort($schemes); + $this->assertEquals($schemes, $foundSchemes); + $foundNonceValues = $policies['script-src']->getNonceValues(); + $nonceValues = ['5694036950466451', '569403695046645']; + sort($foundNonceValues); + sort($nonceValues); + $this->assertEquals($nonceValues, $foundNonceValues); + $foundHashes = $policies['script-src']->getHashes(); + $hashes = [ + 'B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF7=' => 'sha256', + 'B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256' + ]; + $this->assertEquals($hashes, $foundHashes); + + $this->assertArrayHasKey('default-src', $policies); + $this->assertFalse($policies['default-src']->isNoneAllowed()); + $this->assertTrue($policies['default-src']->isSelfAllowed()); + $this->assertFalse($policies['default-src']->isInlineAllowed()); + $this->assertFalse($policies['default-src']->isEvalAllowed()); + $this->assertFalse($policies['default-src']->isDynamicAllowed()); + $this->assertFalse($policies['default-src']->areEventHandlersAllowed()); + $this->assertEmpty($policies['default-src']->getHashes()); + $this->assertEmpty($policies['default-src']->getNonceValues()); + $this->assertEmpty($policies['default-src']->getHostSources()); + $this->assertEmpty($policies['default-src']->getSchemeSources()); + + $this->assertArrayHasKey('upgrade-insecure-requests', $policies); + $this->assertInstanceOf(FlagPolicy::class, $policies['upgrade-insecure-requests']); + + $this->assertArrayHasKey('plugin-types', $policies); + $types = ['application/x-java-applet', 'application/x-shockwave-flash']; + $foundTypes = $policies['plugin-types']->getTypes(); + sort($types); + sort($foundTypes); + $this->assertEquals($types, $foundTypes); + + $this->assertArrayHasKey('sandbox', $policies); + $this->assertTrue($policies['sandbox']->isFormAllowed()); + $this->assertTrue($policies['sandbox']->isModalsAllowed()); + $this->assertTrue($policies['sandbox']->isOrientationLockAllowed()); + $this->assertTrue($policies['sandbox']->isPointerLockAllowed()); + $this->assertTrue($policies['sandbox']->isPopupsAllowed()); + $this->assertTrue($policies['sandbox']->isPopupsToEscapeSandboxAllowed()); + $this->assertTrue($policies['sandbox']->isScriptsAllowed()); + $this->assertFalse($policies['sandbox']->isTopNavigationByUserActivationAllowed()); + $this->assertTrue($policies['sandbox']->isTopNavigationAllowed()); + $this->assertTrue($policies['sandbox']->isSameOriginAllowed()); + $this->assertTrue($policies['sandbox']->isPresentationAllowed()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php new file mode 100644 index 0000000000000..44790ef9dbc94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Mode; + +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test config manager. + */ +class ConfigManagerTest extends TestCase +{ + /** + * @var ConfigManager + */ + private $manager; + + /** + * @inheritDoc + */ + public function setUp() + { + $this->manager = Bootstrap::getObjectManager()->get(ConfigManager::class); + } + + /** + * Check the default configurations of CSP. + * + * @magentoAppArea frontend + * @return void + */ + public function testStorefrontDefault(): void + { + $config = $this->manager->getConfigured(); + $this->assertTrue($config->isReportOnly()); + $this->assertNull($config->getReportUri()); + } + + /** + * Check the default configurations of CSP. + * + * @magentoAppArea adminhtml + * @return void + */ + public function testAdminDefault(): void + { + $config = $this->manager->getConfigured(); + $this->assertTrue($config->isReportOnly()); + $this->assertNull($config->getReportUri()); + } + + /** + * Check that class returns correct configurations. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri https://magento.com + * @return void + */ + public function testFrontendConfigured(): void + { + $config = $this->manager->getConfigured(); + $this->assertFalse($config->isReportOnly()); + $this->assertEquals('https://magento.com', $config->getReportUri()); + } + + /** + * Check that class returns correct configurations. + * + * @magentoAppArea adminhtml + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @magentoConfigFixture default_store csp/mode/admin/report_uri https://magento.com + * @return void + */ + public function testAdminConfigured(): void + { + $config = $this->manager->getConfigured(); + $this->assertFalse($config->isReportOnly()); + $this->assertEquals('https://magento.com', $config->getReportUri()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php index eed72ffef93b1..a67665c6d3c48 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -93,13 +93,30 @@ public function testRenderRestrictWithReportingMode(): void */ public function testRenderReportMode(): void { - $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + $policy = new FetchPolicy( + 'default-src', + false, + ['https://magento.com'], + ['https'], + true, + true, + true, + ['5749837589457695'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], + true, + true + ); $this->renderer->render($policy, $this->response); $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy-Report-Only')); $this->assertEmpty($this->response->getHeader('Content-Security-Policy')); - $this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue()); + $this->assertEquals( + 'default-src https://magento.com https: \'self\' \'unsafe-inline\' \'unsafe-eval\' \'strict-dynamic\'' + . ' \'unsafe-hashes\' \'nonce-'.base64_encode($policy->getNonceValues()[0]).'\'' + . ' \'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=\';', + $header->getFieldValue() + ); } /** diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php index db33b0feaf783..dc3e63fcc7df8 100644 --- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php +++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Response.php @@ -3,9 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Framework\HTTP\PhpEnvironment; +declare(strict_types=1); -use Zend\Http\Header\GenericMultiHeader; +namespace Magento\Framework\HTTP\PhpEnvironment; /** * Base HTTP response object @@ -79,18 +79,11 @@ public function setHeader($name, $value, $replace = false) { $value = (string)$value; - $headers = $this->getHeaders(); if ($replace) { - $headers->addHeaderLine($name, $value); $this->clearHeader($name); - } else { - //Zend framework will only force multiple headers for header objects - //extending MultiHeader interface. - $pluginKey = str_replace('-', '', mb_strtolower($name)); - $headers->getPluginClassLoader()->registerPlugin($pluginKey, GenericMultiHeader::class); - $headers->addHeader(new GenericMultiHeader($name, $value)); } + $this->getHeaders()->addHeaderLine($name, $value); return $this; } @@ -190,4 +183,27 @@ public function __sleep() { return ['content', 'isRedirect', 'statusCode']; } + + /** + * Sending provided headers. + * + * Had to be overridden because the original did not work correctly with multi-headers. + */ + public function sendHeaders() + { + if ($this->headersSent()) { + return $this; + } + + $status = $this->renderStatusLine(); + header($status); + + /** @var \Zend\Http\Header\HeaderInterface $header */ + foreach ($this->getHeaders() as $header) { + header($header->toString(), false); + } + + $this->headersSent = true; + return $this; + } } From 83d62dc78cf1152945f2b5d0a8c7221d79b584d1 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 19 Nov 2019 13:05:31 -0600 Subject: [PATCH 2124/2437] MC-19926: Implement CSP --- .../Csp/Model/Collector/ConfigCollector.php | 14 +++++++------- app/code/Magento/Csp/etc/csp_whitelist.xsd | 2 +- .../Csp/Model/Collector/ConfigCollectorTest.php | 8 +++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php index f7b63e7549133..34711fe5d8a22 100644 --- a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php +++ b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php @@ -12,8 +12,8 @@ use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\State; +use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Model\ScopeInterface; -use Magento\Store\Model\Store; /** * Reads Magento config. @@ -36,26 +36,26 @@ class ConfigCollector implements PolicyCollectorInterface private $state; /** - * @var Store + * @var StoreManagerInterface */ - private $storeModel; + private $storeManager; /** * @param ScopeConfigInterface $config * @param PolicyReaderPool $readersPool * @param State $state - * @param Store $storeModel + * @param StoreManagerInterface $storeManager */ public function __construct( ScopeConfigInterface $config, PolicyReaderPool $readersPool, State $state, - Store $storeModel + StoreManagerInterface $storeManager ) { $this->config = $config; $this->readersPool = $readersPool; $this->state = $state; - $this->storeModel = $storeModel; + $this->storeManager = $storeManager; } /** @@ -77,7 +77,7 @@ public function collect(array $defaultPolicies = []): array $policiesConfig = $this->config->getValue( 'csp/policies/' . $configArea, ScopeInterface::SCOPE_STORE, - $this->storeModel->getStore() + $this->storeManager->getStore() ); if (is_array($policiesConfig) && $policiesConfig) { foreach ($policiesConfig as $policyConfig) { diff --git a/app/code/Magento/Csp/etc/csp_whitelist.xsd b/app/code/Magento/Csp/etc/csp_whitelist.xsd index 8b4b78008fe99..e68e596e21c79 100644 --- a/app/code/Magento/Csp/etc/csp_whitelist.xsd +++ b/app/code/Magento/Csp/etc/csp_whitelist.xsd @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Structure description for webapi.xml configuration files. + * Structure description for csp_whitelist.xml configuration files. * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php index 54f6a4d8fd33f..6d8876012df1e 100644 --- a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -160,11 +160,17 @@ private function getExpectedPolicies(): array */ public function testCollecting(): void { - $policies = $this->collector->collect([]); + $policies = $this->collector->collect([new FlagPolicy('upgrade-insecure-requests')]); $checked = []; $expectedPolicies = $this->getExpectedPolicies(); + //Policies were collected $this->assertNotEmpty($policies); + //Default policies are being kept + /** @var PolicyInterface $defaultPolicy */ + $defaultPolicy = array_shift($policies); + $this->assertEquals('upgrade-insecure-requests', $defaultPolicy->getId()); + //Comparing collected with configured /** @var PolicyInterface $policy */ foreach ($policies as $policy) { $id = $policy->getId(); From 471eff436ddb7c4b792a28309e268b889a865ba2 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Tue, 19 Nov 2019 14:31:05 -0600 Subject: [PATCH 2125/2437] MC-19926: Implement CSP --- app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php | 1 + app/code/Magento/Csp/Model/Policy/FetchPolicy.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php index 1c398a4cfd477..3e3f05b1bc845 100644 --- a/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php +++ b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php @@ -17,6 +17,7 @@ class SandboxPolicyMerger implements MergerInterface { /** * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface { diff --git a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php index cb44c20ab2fd4..7350cbe80aecb 100644 --- a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php +++ b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php @@ -199,6 +199,8 @@ public function isNoneAllowed(): bool /** * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function getValue(): string { From 4a287d5b01acde9e244c72c71ea00bd7a0952788 Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Tue, 19 Nov 2019 15:49:47 -0600 Subject: [PATCH 2126/2437] MC-22813: Changing Quantity and Adding to cart throws exception - Add test for guard --- .../Model/Rule/Condition/ProductTest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php index 80979148c4959..1ac70f31bde87 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php @@ -223,4 +223,38 @@ public function testChildIsNotUsedForValidation() $this->validatorPlugin->beforeValidate($this->validator, $item); } + + /** + * Test for Configurable product in invalid state with no children does not raise error + */ + public function testChildIsNotUsedForValidationWhenConfigurableProductIsMissingChildren() + { + $configurableProductMock = $this->createProductMock(); + $configurableProductMock + ->expects($this->any()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); + + $configurableProductMock + ->expects($this->any()) + ->method('hasData') + ->with($this->equalTo('special_price')) + ->willReturn(false); + + /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */ + $item = $this->getMockBuilder(AbstractItem::class) + ->disableOriginalConstructor() + ->setMethods(['setProduct', 'getProduct', 'getChildren']) + ->getMockForAbstractClass(); + $item->expects($this->any()) + ->method('getProduct') + ->willReturn($configurableProductMock); + $item->expects($this->any()) + ->method('getChildren') + ->willReturn([]); + + $this->validator->setAttribute('special_price'); + + $this->validatorPlugin->beforeValidate($this->validator, $item); + } } From b011040ca0c09b1feebd270e875e3faa142ecd2d Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <rosenberger@e-conomix.at> Date: Wed, 20 Nov 2019 06:23:33 +0100 Subject: [PATCH 2127/2437] :ok_hand: adding type hints as suggested by review --- .../Magento/Indexer/Console/Command/IndexerStatusCommand.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php index 561ff759a4a59..4ecacb68705be 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php @@ -65,6 +65,10 @@ protected function execute(InputInterface $input, OutputInterface $output) usort( $rows, function ($comp1, $comp2) { + /** + * @var array $comp1 + * @var array $comp2 + */ return strcmp($comp1['Title'], $comp2['Title']); } ); From 3020ac7ed08cdc07562fdede078e1ff27b779de0 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 20 Nov 2019 14:32:25 +0700 Subject: [PATCH 2128/2437] Resolve Wrong message in confirmation popup when delete customer group issue25661 --- .../Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml | 4 +++- .../Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml | 2 +- .../Customer/Ui/Component/Listing/Column/GroupActions.php | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml index ab5ae53fd4caa..8171b97258157 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="customerGroupName" type="string"/> </arguments> - + <amOnPage url="{{AdminCustomerGroupsIndexPage.url}}" stepKey="goToAdminCustomerGroupIndexPage"/> <waitForPageLoad time="30" stepKey="waitForCustomerGroupIndexPageLoad"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomerGroupIndexPage"/> @@ -24,6 +24,8 @@ <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> <click selector="{{AdminCustomerGroupGridActionsSection.selectButton(customerGroupName)}}" stepKey="clickSelectButton"/> <click selector="{{AdminCustomerGroupGridActionsSection.deleteAction(customerGroupName)}}" stepKey="clickOnDeleteItem"/> + <waitForElementVisible selector="{{AdminGridConfirmActionSection.message}}" stepKey="waitForConfirmModal"/> + <see selector="{{AdminGridConfirmActionSection.message}}" userInput="Are you sure you want to delete a {{customerGroupName}} record?" stepKey="seeRemoveMessage"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDeleteCustomerGroup"/> <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml index 28305d37cf77b..68e4090d64910 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -30,7 +30,7 @@ </array> </entity> <entity name="CustomCustomerGroup" type="customerGroup"> - <data key="code" unique="suffix">Group </data> + <data key="code" unique="suffix">Group-</data> <data key="tax_class_id">3</data> <data key="tax_class_name">Retail Customer</data> </entity> diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php index 12f6f2705125b..5d974088b0d54 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php @@ -97,10 +97,10 @@ public function prepareDataSource(array $dataSource) ), 'label' => __('Delete'), 'confirm' => [ - 'title' => __('Delete %1', $this->escaper->escapeJs($title)), + 'title' => __('Delete %1', $this->escaper->escapeHtml($title)), 'message' => __( 'Are you sure you want to delete a %1 record?', - $this->escaper->escapeJs($title) + $this->escaper->escapeHtml($title) ) ], 'post' => true, From 64de08292cd47cc550be9e6cba41ff7208a59ee4 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Wed, 20 Nov 2019 09:59:20 +0200 Subject: [PATCH 2129/2437] MC-22738: Layered Navigation with different product attributes on Category page --- .../Category/AbstractFiltersTest.php | 72 ++++++++++++++----- .../Navigation/Category/BooleanFilterTest.php | 11 +-- .../Navigation/Category/DecimalFilterTest.php | 25 ++----- .../Category/MultiselectFilterTest.php | 11 +-- .../Navigation/Category/SelectFilterTest.php | 11 +-- .../Block/Navigation/CategoryTest.php | 17 +++-- .../Category/SwatchTextFilterTest.php | 11 +-- .../Category/SwatchVisualFilterTest.php | 11 +-- 8 files changed, 72 insertions(+), 97 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php index 89fd1485bf439..be97c5433bd69 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php @@ -43,6 +43,34 @@ protected function setUp() $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); } + /** + * Tests getFilters method from navigation block. + * + * @param array $products + * @param int $filterable + * @param array $expectation + * @param string $attributeCode + * @return void + */ + protected function getFiltersAndAssert( + array $products, + int $filterable, + array $expectation, + string $attributeCode + ): void { + $this->updateAttribute($attributeCode, $filterable); + $this->updateProducts($products, $attributeCode); + $this->prepareNavigationBlock('Category 999'); + $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), $attributeCode); + + if ($filterable) { + $this->assertNotNull($filter); + $this->assertEquals($expectation, $this->prepareFilterItems($filter)); + } else { + $this->assertNull($filter); + } + } + /** * @inheritdoc */ @@ -58,7 +86,7 @@ protected function prepareNavigationBlock(string $categoryName, int $storeId = S /** * Returns filter with specified attribute. * - * @param array $filters + * @param AbstractFilter[] $filters * @param string $code * @return AbstractFilter|null */ @@ -68,7 +96,7 @@ protected function getFilterByCode(array $filters, string $code): ?AbstractFilte $filters, function (AbstractFilter $filter) use ($code) { return $filter->getData('attribute_model') - && $filter->getData('attribute_model')->getAttributeCode() == $code; + && $filter->getData('attribute_model')->getAttributeCode() === $code; } ); @@ -76,29 +104,19 @@ function (AbstractFilter $filter) use ($code) { } /** - * Updates attribute and products data. + * Updates attribute data. * * @param string $attributeCode * @param int $filterable - * @param array $products * @return void */ - protected function updateAttributeAndProducts( + protected function updateAttribute( string $attributeCode, - int $filterable, - array $products + int $filterable ): void { $attribute = $this->attributeRepository->get($attributeCode); $attribute->setData('is_filterable', $filterable); $this->attributeRepository->save($attribute); - - foreach ($products as $productSku => $stringValue) { - $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); - $product->addData( - [$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)] - ); - $this->productRepository->save($product); - } } /** @@ -112,13 +130,33 @@ protected function prepareFilterItems(AbstractFilter $filter): array $items = []; /** @var Item $item */ foreach ($filter->getItems() as $item) { - $item = [ + $itemArray = [ 'label' => $item->getData('label'), 'count' => $item->getData('count'), ]; - $items[] = $item; + $items[] = $itemArray; } return $items; } + + /** + * Update products data by attribute. + * + * @param array $products + * @param string $attributeCode + * @return void + */ + protected function updateProducts(array $products, string $attributeCode): void + { + $attribute = $this->attributeRepository->get($attributeCode); + + foreach ($products as $productSku => $stringValue) { + $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); + $product->addData( + [$attribute->getAttributeCode() => $attribute->getSource()->getOptionId($stringValue)] + ); + $this->productRepository->save($product); + } + } } diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php index b8edd7f460862..83636ff55dce8 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/BooleanFilterTest.php @@ -29,16 +29,7 @@ class BooleanFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('boolean_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'boolean_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'boolean_attribute'); } /** diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php index dcb69b9a93c26..e79b521f7f13c 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/DecimalFilterTest.php @@ -31,23 +31,11 @@ class DecimalFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('decimal_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'decimal_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'decimal_attribute'); } /** - * Returns filter items as array. - * - * @param AbstractFilter $filter - * @return array + * @inheritdoc */ protected function prepareFilterItems(AbstractFilter $filter): array { @@ -68,14 +56,9 @@ protected function prepareFilterItems(AbstractFilter $filter): array /** * @inheritdoc */ - protected function updateAttributeAndProducts( - string $attributeCode, - int $filterable, - array $products - ): void { + protected function updateProducts(array $products, string $attributeCode): void + { $attribute = $this->attributeRepository->get($attributeCode); - $attribute->setData('is_filterable', $filterable); - $this->attributeRepository->save($attribute); foreach ($products as $productSku => $value) { $product = $this->productRepository->get($productSku, false, Store::DEFAULT_STORE_ID, true); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php index 04ccfc2f04b8b..14d121eb15b79 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/MultiselectFilterTest.php @@ -29,16 +29,7 @@ class MultiselectFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('multiselect_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'multiselect_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'multiselect_attribute'); } /** diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php index b9fc8865b4354..c0677bbc6b72b 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php @@ -29,16 +29,7 @@ class SelectFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('dropdown_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'dropdown_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'dropdown_attribute'); } /** diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php index 9f10c28c6693e..3a2de697bd5ef 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/CategoryTest.php @@ -66,7 +66,7 @@ public function testCanShowBlockWithoutFilterOptions(): void */ public function testCanShowBlockWithDisplayMode(string $displayMode, bool $canShow): void { - $this->updateTestCategory($displayMode, 'Category 999'); + $this->updateCategoryDisplayMode('Category 999', $displayMode); $this->prepareNavigationBlock('Category 999'); $this->assertEquals($canShow, $this->navigationBlock->canShowBlock()); } @@ -98,8 +98,8 @@ public function testCanShowBlockWithDisplayModeOnStoreView( bool $canShow ): void { $secondStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId(); - $this->updateTestCategory($defaultMode, 'Category 999'); - $this->updateTestCategory($storeMode, 'Category 999', $secondStoreId); + $this->updateCategoryDisplayMode('Category 999', $defaultMode); + $this->updateCategoryDisplayMode('Category 999', $storeMode, $secondStoreId); $this->prepareNavigationBlock('Category 999', $secondStoreId); $this->assertEquals($canShow, $this->navigationBlock->canShowBlock()); } @@ -131,21 +131,20 @@ public function canShowBlockWithDisplayModeDataProviderOnStoreView(): array /** * Updates category display mode. * - * @param string $displayMode * @param string $categoryName + * @param string $displayMode * @param int $storeId * @return void */ - private function updateTestCategory( - string $displayMode, + private function updateCategoryDisplayMode( string $categoryName, + string $displayMode, int $storeId = Store::DEFAULT_STORE_ID ): void { $category = $this->loadCategory($categoryName, $storeId); - $currentMode = $category->getData('display_mode'); + $category->setData('display_mode', $displayMode); - if ($currentMode !== $displayMode) { - $category->setData('display_mode', $displayMode); + if ($category->dataHasChangedFor('display_mode')) { $this->categoryResource->save($category); } } diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php index d7c47fd98ce4a..345d11e9948d2 100644 --- a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchTextFilterTest.php @@ -30,16 +30,7 @@ class SwatchTextFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('text_swatch_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'text_swatch_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'text_swatch_attribute'); } /** diff --git a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php index 06838b03d9f76..6f34a7bad9ebc 100644 --- a/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/SwatchesLayeredNavigation/Block/Navigation/Category/SwatchVisualFilterTest.php @@ -30,16 +30,7 @@ class SwatchVisualFilterTest extends AbstractFiltersTest */ public function testGetFiltersWithCustomAttribute(array $products, int $filterable, array $expectation): void { - $this->updateAttributeAndProducts('visual_swatch_attribute', $filterable, $products); - $this->prepareNavigationBlock('Category 999'); - $filter = $this->getFilterByCode($this->navigationBlock->getFilters(), 'visual_swatch_attribute'); - - if ($filterable) { - $this->assertNotNull($filter); - $this->assertEquals($expectation, $this->prepareFilterItems($filter)); - } else { - $this->assertNull($filter); - } + $this->getFiltersAndAssert($products, $filterable, $expectation, 'visual_swatch_attribute'); } /** From f97f68d88009eae7aef52c3404df84635e1a659b Mon Sep 17 00:00:00 2001 From: Alexey Rakitin <alexey.rakitin@babenkocommerce.com> Date: Wed, 20 Nov 2019 12:42:16 +0200 Subject: [PATCH 2130/2437] magento/magento2#25652: Arrows (.fotorama__thumb__arr) don't work in product gallery - Fotorama disabled arrows are hidden by opacity:0 - Internal ticket: MC-28947 --- lib/web/mage/gallery/gallery.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 1608ef4ba83e9..10fba50fe239b 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -418,9 +418,8 @@ } .fotorama__arr--disabled { - *display: none; cursor: default; - opacity: 0.1; + opacity: 0; pointer-events: none; } From 14468af3f91c0b91d4ad202f67ccdce459aecc44 Mon Sep 17 00:00:00 2001 From: Alastair Mucklow <amucklow@strangerpixel.com> Date: Wed, 20 Nov 2019 10:48:45 +0000 Subject: [PATCH 2131/2437] Update translation file --- app/code/Magento/SalesRule/i18n/en_US.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/i18n/en_US.csv b/app/code/Magento/SalesRule/i18n/en_US.csv index 7511d147ae224..cd1e471516ac9 100644 --- a/app/code/Magento/SalesRule/i18n/en_US.csv +++ b/app/code/Magento/SalesRule/i18n/en_US.csv @@ -22,7 +22,7 @@ Conditions,Conditions Generate,Generate "Coupon Code","Coupon Code" Created,Created -Uses,Uses +Used,Used No,No Yes,Yes "Times Used","Times Used" From 377b013c4f91c31586032147bf7722d832362536 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Wed, 20 Nov 2019 12:49:57 +0200 Subject: [PATCH 2132/2437] MC-18057: Product doesn'y match by "Date" attribute condition --- .../Catalog/_files/product_simple_with_date_attribute.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php index 46745c213845a..28a89ff883c4f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php @@ -10,11 +10,11 @@ $attribute->setData('is_used_for_promo_rules', 1); -/** @var ProductFactory $productFactory */ +/** @var \Magento\Catalog\Model\ProductFactory $productFactory */ $productFactory = $objectManager->get(Magento\Catalog\Model\ProductFactory::class); $product = $productFactory->create(); $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) ->setName('Simple Product with date') ->setSku('simple_with_date') @@ -30,7 +30,7 @@ $product2 = $productFactory->create(); $product2->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) ->setName('Simple Product with date -1') ->setSku('simple_with_date2') @@ -46,7 +46,7 @@ $product3 = $productFactory->create(); $product3->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setAttributeSetId($installer->getAttributeSetId('catalog_product', 'Default')) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) ->setName('Simple Product with date +1') ->setSku('simple_with_date3') From 2e27790a4330a8017f6cb2154d9f48a1a550dac8 Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <rosenberger@e-conomix.at> Date: Wed, 20 Nov 2019 12:25:35 +0100 Subject: [PATCH 2133/2437] :ok_hand: change type hint to parameter type --- .../Indexer/Console/Command/IndexerStatusCommand.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php index 4ecacb68705be..26feb38392e5f 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php @@ -64,11 +64,7 @@ protected function execute(InputInterface $input, OutputInterface $output) usort( $rows, - function ($comp1, $comp2) { - /** - * @var array $comp1 - * @var array $comp2 - */ + function (array $comp1, array $comp2) { return strcmp($comp1['Title'], $comp2['Title']); } ); From 701401f6157833f4df02d0c905ebab870dcec406 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Wed, 20 Nov 2019 14:08:45 +0200 Subject: [PATCH 2134/2437] MC-22738: Layered Navigation with different product attributes on Category page --- ...category_with_different_price_products.php | 32 ++++++++++++------- .../Category/AbstractFiltersTest.php | 3 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php index 2500b546666a1..f10b09f603455 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php @@ -5,6 +5,7 @@ */ declare(strict_types=1); +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\ProductInterfaceFactory; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Api\Data\CategoryInterfaceFactory; @@ -12,25 +13,32 @@ use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\Product\Visibility; use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); +$storeManager = $objectManager->get(StoreManagerInterface::class); $categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); $productFactory = $objectManager->get(ProductInterfaceFactory::class); $productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +$currentStoreId = $storeManager->getStore()->getId(); -$category = $categoryFactory->create(); -$category->isObjectNew(true); -$category->setName('Category 999') - ->setParentId(2)->setPath('1/2') - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1) - ->setStoreId(Store::DEFAULT_STORE_ID) - ->setAvailableSortBy(['position']) - ->save(); +try { + $storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); + $category = $categoryFactory->create(); + $category->isObjectNew(true); + $category->setName('Category 999') + ->setParentId(2) + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); + $category = $categoryRepository->save($category); +} finally { + $storeManager->setCurrentStore($currentStoreId); +} $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php index be97c5433bd69..00c14b6a72d04 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/AbstractFiltersTest.php @@ -130,11 +130,10 @@ protected function prepareFilterItems(AbstractFilter $filter): array $items = []; /** @var Item $item */ foreach ($filter->getItems() as $item) { - $itemArray = [ + $items[] = [ 'label' => $item->getData('label'), 'count' => $item->getData('count'), ]; - $items[] = $itemArray; } return $items; From eb7643d8d6dcb0a99a5c4a0ace73aa15a75dfd03 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Wed, 20 Nov 2019 14:20:46 +0200 Subject: [PATCH 2135/2437] MC-22738: Layered Navigation with different product attributes on Category page --- ...category_with_different_price_products.php | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php index f10b09f603455..2e87e1e820f86 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products.php @@ -24,21 +24,18 @@ $categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); $currentStoreId = $storeManager->getStore()->getId(); -try { - $storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); - $category = $categoryFactory->create(); - $category->isObjectNew(true); - $category->setName('Category 999') - ->setParentId(2) - ->setLevel(2) - ->setAvailableSortBy('name') - ->setDefaultSortBy('name') - ->setIsActive(true) - ->setPosition(1); - $category = $categoryRepository->save($category); -} finally { - $storeManager->setCurrentStore($currentStoreId); -} +$storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); +$category = $categoryFactory->create(); +$category->isObjectNew(true); +$category->setName('Category 999') + ->setParentId(2) + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); +$category = $categoryRepository->save($category); +$storeManager->setCurrentStore($currentStoreId); $product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) From dc1ab99136f06a550a12580832bca6cd726b0440 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 20 Nov 2019 17:13:21 +0200 Subject: [PATCH 2136/2437] Remove duplicated fireEcent, that prevents to duble initialization --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index df691601eccb9..6cc53e3d40d92 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -293,7 +293,6 @@ define([ }; varienGlobalEvents.fireEvent('open_browser_callback', payload); - this.eventBus.fireEvent('open_browser_callback', payload); }.bind(this); } From b5fde42dacbca4239e9276f871fc6b48840a28a7 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 20 Nov 2019 10:08:58 -0600 Subject: [PATCH 2137/2437] MAGETWO-55858: Output escaping methods shouldn't be part of AbstractBlock --- .../frontend/templates/product/list.phtml | 32 +++++----- .../Framework/View/TemplateEngine/PhpTest.php | 58 +++++++++++++++++++ .../Framework/View/_files/test_template.phtml | 18 ++++++ .../Framework/View/Element/AbstractBlock.php | 6 ++ .../Framework/View/TemplateEngine/Php.php | 20 ++++++- 5 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/TemplateEngine/PhpTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/View/_files/test_template.phtml diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index 8c32302cf7c29..554caf6026001 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -13,14 +13,16 @@ use Magento\Framework\App\Action\Action; * Product list template * * @var $block \Magento\Catalog\Block\Product\ListProduct + * @var \Magento\Framework\Escaper $escaper */ ?> <?php $_productCollection = $block->getLoadedProductCollection(); +/** @var \Magento\Catalog\Helper\Output $_helper */ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <?php if (!$_productCollection->count()) :?> - <div class="message info empty"><div><?= $block->escapeHtml(__('We can\'t find products matching the selection.')) ?></div></div> + <div class="message info empty"><div><?= $escaper->escapeHtml(__('We can\'t find products matching the selection.')) ?></div></div> <?php else :?> <?= $block->getToolbarHtml() ?> <?= $block->getAdditionalHtml() ?> @@ -55,7 +57,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); } ?> <?php // Product Image ?> - <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" class="product photo product-item-photo" tabindex="-1"> <?= $productImage->toHtml() ?> @@ -66,7 +68,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <strong class="product name product-item-name"> <a class="product-item-link" - href="<?= $block->escapeUrl($_product->getProductUrl()) ?>"> + href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>"> <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name') ?> </a> </strong> @@ -77,13 +79,13 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); <?php endif; ?> <div class="product-item-inner"> - <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $block->escapeHtmlAttr($position) : '' ?>> - <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $block->escapeHtmlAttr($position) : '' ?>> + <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $escaper->escapeHtmlAttr($position) : '' ?>> + <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $escaper->escapeHtmlAttr($position) : '' ?>> <?php if ($_product->isSaleable()) :?> <?php $postParams = $block->getAddToCartPostParams($_product); ?> <form data-role="tocart-form" - data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" - action="<?= $block->escapeUrl($postParams['action']) ?>" + data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>" + action="<?= $escaper->escapeUrl($postParams['action']) ?>" method="post"> <input type="hidden" name="product" @@ -92,20 +94,20 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" - title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>" class="action tocart primary"> - <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + <span><?= $escaper->escapeHtml(__('Add to Cart')) ?></span> </button> </form> <?php else :?> <?php if ($_product->isAvailable()) :?> - <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <div class="stock available"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div> <?php else :?> - <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> + <div class="stock unavailable"><span><?= $escaper->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> - <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $block->escapeHtmlAttr($position) : '' ?>> + <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $escaper->escapeHtmlAttr($position) : '' ?>> <?php if ($addToBlock = $block->getChildBlock('addto')) :?> <?= $addToBlock->setProduct($_product)->getChildHtml() ?> <?php endif; ?> @@ -114,9 +116,9 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); <?php if ($showDescription) :?> <div class="product description product-item-description"> <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?> - <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" title="<?= /* @noEscape */ $_productNameStripped ?>" - class="action more"><?= $block->escapeHtml(__('Learn More')) ?></a> + class="action more"><?= $escaper->escapeHtml(__('Learn More')) ?></a> </div> <?php endif; ?> </div> @@ -132,7 +134,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); { "[data-role=tocart-form], .form.map.checkout": { "catalogAddToCart": { - "product_sku": "<?= $block->escapeJs($_product->getSku()) ?>" + "product_sku": "<?= $escaper->escapeJs($_product->getSku()) ?>" } } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/TemplateEngine/PhpTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/TemplateEngine/PhpTest.php new file mode 100644 index 0000000000000..f4227ee41b53d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/TemplateEngine/PhpTest.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Framework\View\TemplateEngine; + +use Magento\Framework\View\Element\BlockInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Testing .phtml templating. + */ +class PhpTest extends TestCase +{ + /** + * @var Php + */ + private $templateEngine; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->templateEngine = $objectManager->get(Php::class); + } + + /** + * See that templates get access to certain variables. + * + * @return void + */ + public function testVariablesAvailable(): void + { + $block = new class implements BlockInterface { + /** + * @inheritDoc + */ + public function toHtml() + { + return '<b>BLOCK</b>'; + } + }; + + $rendered = $this->templateEngine->render($block, __DIR__ .'/../_files/test_template.phtml'); + $this->assertEquals( + '<p>This template has access to <b>$escaper</b> and $block "<b>BLOCK</b>"</p>' + .PHP_EOL, + $rendered + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/_files/test_template.phtml b/dev/tests/integration/testsuite/Magento/Framework/View/_files/test_template.phtml new file mode 100644 index 0000000000000..e41d495327a5d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/View/_files/test_template.phtml @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<?php +// phpcs:disable +?> +<?php +/** + * Template meant for testing. + * + * @var \Magento\Framework\View\Element\BlockInterface $block + * @var \Magento\Framework\Escaper $escaper + */ +?> +<p>This template has access to <?= $escaper->escapeHtml('<b>$escaper</b>') ?> and $block "<?= $block->toHtml() ?>"</p> diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 4df1ac515a87b..628e595229137 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\View\Element; @@ -887,6 +888,7 @@ public static function extractModuleName($className) * @param string|array $data * @param array|null $allowedTags * @return string + * @deprecated Use $escaper directly in templates and in blocks. */ public function escapeHtml($data, $allowedTags = null) { @@ -899,6 +901,7 @@ public function escapeHtml($data, $allowedTags = null) * @param string $string * @return string * @since 100.2.0 + * @deprecated Use $escaper directly in templates and in blocks. */ public function escapeJs($string) { @@ -912,6 +915,7 @@ public function escapeJs($string) * @param boolean $escapeSingleQuote * @return string * @since 100.2.0 + * @deprecated Use $escaper directly in templates and in blocks. */ public function escapeHtmlAttr($string, $escapeSingleQuote = true) { @@ -924,6 +928,7 @@ public function escapeHtmlAttr($string, $escapeSingleQuote = true) * @param string $string * @return string * @since 100.2.0 + * @deprecated Use $escaper directly in templates and in blocks. */ public function escapeCss($string) { @@ -951,6 +956,7 @@ public function stripTags($data, $allowableTags = null, $allowHtmlEntities = fal * * @param string $string * @return string + * @deprecated Use $escaper directly in templates and in blocks. */ public function escapeUrl($string) { diff --git a/lib/internal/Magento/Framework/View/TemplateEngine/Php.php b/lib/internal/Magento/Framework/View/TemplateEngine/Php.php index 8c51433779424..0437386e4fc2b 100644 --- a/lib/internal/Magento/Framework/View/TemplateEngine/Php.php +++ b/lib/internal/Magento/Framework/View/TemplateEngine/Php.php @@ -3,8 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\View\TemplateEngine; +use Magento\Framework\Escaper; use Magento\Framework\View\Element\BlockInterface; use Magento\Framework\View\TemplateEngineInterface; @@ -27,14 +30,23 @@ class Php implements TemplateEngineInterface */ protected $_helperFactory; + /** + * @var Escaper + */ + private $escaper; + /** * Constructor * * @param \Magento\Framework\ObjectManagerInterface $helperFactory + * @param Escaper|null $escaper */ - public function __construct(\Magento\Framework\ObjectManagerInterface $helperFactory) - { + public function __construct( + \Magento\Framework\ObjectManagerInterface $helperFactory, + ?Escaper $escaper = null + ) { $this->_helperFactory = $helperFactory; + $this->escaper = $escaper ?? $helperFactory->get(Escaper::class); } /** @@ -48,6 +60,7 @@ public function __construct(\Magento\Framework\ObjectManagerInterface $helperFac * @param array $dictionary * @return string * @throws \Exception + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function render(BlockInterface $block, $fileName, array $dictionary = []) { @@ -56,6 +69,9 @@ public function render(BlockInterface $block, $fileName, array $dictionary = []) $tmpBlock = $this->_currentBlock; $this->_currentBlock = $block; extract($dictionary, EXTR_SKIP); + //So it can be used in the template. + $escaper = $this->escaper; + // phpcs:ignore include $fileName; $this->_currentBlock = $tmpBlock; } catch (\Exception $exception) { From ca0e7094e3fb16065b566fd623e06301f52ac0b8 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky <firster@atwix.com> Date: Wed, 20 Nov 2019 18:19:29 +0200 Subject: [PATCH 2138/2437] =?UTF-8?q?magento/magento2#:=20Remove=20redunda?= =?UTF-8?q?nt=20use=3D=E2=80=9Coptional=E2=80=9D=20from=20INDEXER=20XSD=20?= =?UTF-8?q?schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Magento/Framework/Indexer/etc/indexer.xsd | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd b/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd index fc38a5206e855..ca14e5d56fb76 100644 --- a/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd +++ b/lib/internal/Magento/Framework/Indexer/etc/indexer.xsd @@ -14,19 +14,19 @@ </xs:annotation> <xs:sequence> <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element name="title" type="translatableType" /> - <xs:element name="description" type="translatableType" /> - <xs:element name="saveHandler" type="saveHandlerType" /> - <xs:element name="structure" type="structureType" /> + <xs:element name="title" type="translatableType"/> + <xs:element name="description" type="translatableType"/> + <xs:element name="saveHandler" type="saveHandlerType"/> + <xs:element name="structure" type="structureType"/> <xs:group ref="fieldset"/> </xs:choice> <xs:group ref="dependencies"/> </xs:sequence> - <xs:attribute name="id" type="xs:string" use="required" /> - <xs:attribute name="primary" type="nameType" use="optional" /> - <xs:attribute name="view_id" type="viewIdType" use="optional" /> - <xs:attribute name="class" type="classType" use="optional" /> - <xs:attribute name="shared_index" type="xs:string" use="optional" /> + <xs:attribute name="id" type="xs:string" use="required"/> + <xs:attribute name="primary" type="nameType"/> + <xs:attribute name="view_id" type="viewIdType"/> + <xs:attribute name="class" type="classType"/> + <xs:attribute name="shared_index" type="xs:string"/> </xs:complexType> <xs:group name="fieldset"> @@ -48,7 +48,7 @@ <xs:complexType name="translatableType"> <xs:simpleContent> <xs:extension base="xs:string"> - <xs:attribute name="translate" use="optional" fixed="true" type="xs:boolean"/> + <xs:attribute name="translate" fixed="true" type="xs:boolean"/> </xs:extension> </xs:simpleContent> </xs:complexType> @@ -60,7 +60,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9_]+" /> + <xs:pattern value="[a-zA-Z0-9_]+"/> </xs:restriction> </xs:simpleType> @@ -71,7 +71,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z|\\]+[a-zA-Z0-9\\]+" /> + <xs:pattern value="[a-zA-Z|\\]+[a-zA-Z0-9\\]+"/> </xs:restriction> </xs:simpleType> @@ -82,7 +82,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9_]+" /> + <xs:pattern value="[a-zA-Z0-9_]+"/> </xs:restriction> </xs:simpleType> @@ -93,7 +93,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9_]+" /> + <xs:pattern value="[a-zA-Z0-9_]+"/> </xs:restriction> </xs:simpleType> @@ -104,7 +104,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9_]+" /> + <xs:pattern value="[a-zA-Z0-9_]+"/> </xs:restriction> </xs:simpleType> @@ -115,7 +115,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9_]+" /> + <xs:pattern value="[a-zA-Z0-9_]+"/> </xs:restriction> </xs:simpleType> @@ -126,8 +126,8 @@ </xs:documentation> </xs:annotation> <xs:attribute name="name" type="nameType" use="required"/> - <xs:attribute name="handler" type="classType" use="optional"/> - <xs:attribute name="origin" type="originType" use="optional"/> + <xs:attribute name="handler" type="classType"/> + <xs:attribute name="origin" type="originType"/> </xs:complexType> <xs:complexType name="fieldsetType" mixed="true"> @@ -137,11 +137,11 @@ </xs:documentation> </xs:annotation> <xs:sequence> - <xs:element type="referenceType" name="reference" minOccurs="0" maxOccurs="unbounded" /> + <xs:element type="referenceType" name="reference" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="field" type="fieldType" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> - <xs:attribute name="source" type="classType" use="optional"/> - <xs:attribute name="provider" type="classType" use="optional"/> + <xs:attribute name="source" type="classType"/> + <xs:attribute name="provider" type="classType"/> <xs:attribute name="name" type="nameType" use="required"/> </xs:complexType> @@ -149,9 +149,9 @@ <xs:complexContent> <xs:extension base="fieldTypeAbstract"> <xs:choice> - <xs:element type="filterType" name="filter" minOccurs="0" maxOccurs="1" /> + <xs:element type="filterType" name="filter" minOccurs="0" maxOccurs="1"/> </xs:choice> - <xs:attribute type="dataType" name="dataType" use="optional" /> + <xs:attribute type="dataType" name="dataType"/> </xs:extension> </xs:complexContent> </xs:complexType> @@ -190,20 +190,20 @@ </xs:documentation> </xs:annotation> <xs:attribute name="fieldset" type="nameType" use="required"/> - <xs:attribute name="from" type="fromType" use="optional"/> - <xs:attribute name="to" type="toType" use="optional"/> + <xs:attribute name="from" type="fromType"/> + <xs:attribute name="to" type="toType"/> </xs:complexType> <xs:simpleType name="dataType"> <xs:restriction base="xs:string"> - <xs:enumeration value="int" /> - <xs:enumeration value="float" /> - <xs:enumeration value="varchar" /> - <xs:enumeration value="text" /> - <xs:enumeration value="mediumtext" /> - <xs:enumeration value="timestamp" /> - <xs:enumeration value="datetime" /> - <xs:enumeration value="date" /> + <xs:enumeration value="int"/> + <xs:enumeration value="float"/> + <xs:enumeration value="varchar"/> + <xs:enumeration value="text"/> + <xs:enumeration value="mediumtext"/> + <xs:enumeration value="timestamp"/> + <xs:enumeration value="datetime"/> + <xs:enumeration value="date"/> </xs:restriction> </xs:simpleType> @@ -226,8 +226,8 @@ Indexer Id must be unique. </xs:documentation> </xs:annotation> - <xs:selector xpath="indexer" /> - <xs:field xpath="@id" /> + <xs:selector xpath="indexer"/> + <xs:field xpath="@id"/> </xs:unique> <xs:unique name="uniqueViewId"> <xs:annotation> @@ -235,8 +235,8 @@ Indexer Id must be unique. </xs:documentation> </xs:annotation> - <xs:selector xpath="indexer" /> - <xs:field xpath="@view_id" /> + <xs:selector xpath="indexer"/> + <xs:field xpath="@view_id"/> </xs:unique> </xs:element> <xs:complexType name="configType"> @@ -266,8 +266,8 @@ Related indexer id must be unique. </xs:documentation> </xs:annotation> - <xs:selector xpath="indexer" /> - <xs:field xpath="@id" /> + <xs:selector xpath="indexer"/> + <xs:field xpath="@id"/> </xs:unique> </xs:element> </xs:choice> @@ -287,7 +287,7 @@ Indexer dependency that represents another indexer. </xs:documentation> </xs:annotation> - <xs:attribute name="id" type="xs:string" use="required" /> + <xs:attribute name="id" type="xs:string" use="required"/> </xs:complexType> </xs:element> </xs:sequence> From 1cf65ed2d45481e07651687899f58d20f44a1cc4 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Mon, 18 Nov 2019 11:26:48 -0600 Subject: [PATCH 2139/2437] MC-22931: VAT calculation issue with shipping rate selection - static fixing --- app/code/Magento/Tax/Model/Config.php | 86 +++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Tax/Model/Config.php b/app/code/Magento/Tax/Model/Config.php index 09212ce90bf58..201158eae25dd 100644 --- a/app/code/Magento/Tax/Model/Config.php +++ b/app/code/Magento/Tax/Model/Config.php @@ -14,11 +14,15 @@ use Magento\Store\Model\Store; /** + * Class to set flags for tax display setting + * * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ class Config { - // tax notifications + /** + * Tax notifications + */ const XML_PATH_TAX_NOTIFICATION_IGNORE_DISCOUNT = 'tax/notification/ignore_discount'; const XML_PATH_TAX_NOTIFICATION_IGNORE_PRICE_DISPLAY = 'tax/notification/ignore_price_display'; @@ -70,7 +74,11 @@ class Config const XML_PATH_DISPLAY_CART_SHIPPING = 'tax/cart_display/shipping'; - /** @deprecated */ + /** + * Tax cart display discount + * + * @deprecated + */ const XML_PATH_DISPLAY_CART_DISCOUNT = 'tax/cart_display/discount'; const XML_PATH_DISPLAY_CART_GRANDTOTAL = 'tax/cart_display/grandtotal'; @@ -88,7 +96,11 @@ class Config const XML_PATH_DISPLAY_SALES_SHIPPING = 'tax/sales_display/shipping'; - /** @deprecated */ + /** + * Tax sales display discount + * + * @deprecated + */ const XML_PATH_DISPLAY_SALES_DISCOUNT = 'tax/sales_display/discount'; const XML_PATH_DISPLAY_SALES_GRANDTOTAL = 'tax/sales_display/grandtotal'; @@ -231,6 +243,7 @@ public function discountTax($store = null) /** * Get taxes/discounts calculation sequence. + * * This sequence depends on "Apply Customer Tax" and "Apply Discount On Prices" configuration options. * * @param null|int|string|Store $store @@ -353,6 +366,8 @@ public function setShippingPriceIncludeTax($flag) } /** + * Return the flag for display sales for cart prices including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -366,6 +381,8 @@ public function displayCartPricesInclTax($store = null) } /** + * Return the flag for display sales for cart prices excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -379,6 +396,8 @@ public function displayCartPricesExclTax($store = null) } /** + * Return the flag for display sales for cart prices both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -392,6 +411,8 @@ public function displayCartPricesBoth($store = null) } /** + * Return the flag for display sales for cart subtotal including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -405,6 +426,8 @@ public function displayCartSubtotalInclTax($store = null) } /** + * Return the flag for display sales for cart subtotal excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -418,6 +441,8 @@ public function displayCartSubtotalExclTax($store = null) } /** + * Return the flag for display sales for cart subtotal both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -431,6 +456,8 @@ public function displayCartSubtotalBoth($store = null) } /** + * Return the flag for display sales for cart shipping including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -444,6 +471,8 @@ public function displayCartShippingInclTax($store = null) } /** + * Return the flag for display sales for cart shipping excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -457,6 +486,8 @@ public function displayCartShippingExclTax($store = null) } /** + * Return the flag for display sales for shipping both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -470,6 +501,8 @@ public function displayCartShippingBoth($store = null) } /** + * Return the flag for display cart discount for including tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -484,6 +517,8 @@ public function displayCartDiscountInclTax($store = null) } /** + * Return the flag for display cart discount for excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -498,6 +533,8 @@ public function displayCartDiscountExclTax($store = null) } /** + * Return the flag for display cart discount for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -512,6 +549,8 @@ public function displayCartDiscountBoth($store = null) } /** + * Return the flag for display cart tax with grand total for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -525,6 +564,8 @@ public function displayCartTaxWithGrandTotal($store = null) } /** + * Return the flag for display cart full summary + * * @param null|string|bool|int|Store $store * @return bool */ @@ -538,6 +579,8 @@ public function displayCartFullSummary($store = null) } /** + * Return the flag for display cart zero tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -551,6 +594,8 @@ public function displayCartZeroTax($store = null) } /** + * Return the flag for display sales prices for including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -564,6 +609,8 @@ public function displaySalesPricesInclTax($store = null) } /** + * Return the flag for display sales prices for excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -577,6 +624,8 @@ public function displaySalesPricesExclTax($store = null) } /** + * Return the flag for display sales prices for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -590,6 +639,8 @@ public function displaySalesPricesBoth($store = null) } /** + * Return the flag for display sales subtotal for including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -603,6 +654,8 @@ public function displaySalesSubtotalInclTax($store = null) } /** + * Return the flag for display sales subtotal for excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -616,6 +669,8 @@ public function displaySalesSubtotalExclTax($store = null) } /** + * Return the flag for display sales subtotal for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -629,6 +684,8 @@ public function displaySalesSubtotalBoth($store = null) } /** + * Return the flag for display sales for shipping including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -642,6 +699,8 @@ public function displaySalesShippingInclTax($store = null) } /** + * Return the flag for display sales for shipping excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -655,6 +714,8 @@ public function displaySalesShippingExclTax($store = null) } /** + * Return the flag for display sales for shipping both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -668,6 +729,8 @@ public function displaySalesShippingBoth($store = null) } /** + * Return the flag for display sales discount for including tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -682,6 +745,8 @@ public function displaySalesDiscountInclTax($store = null) } /** + * Return the flag for display sales discount for excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -696,6 +761,8 @@ public function displaySalesDiscountExclTax($store = null) } /** + * Return the flag for display sales discount for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -710,6 +777,8 @@ public function displaySalesDiscountBoth($store = null) } /** + * Return the flag for display sales tax with grand total + * * @param null|string|bool|int|Store $store * @return bool */ @@ -723,6 +792,8 @@ public function displaySalesTaxWithGrandTotal($store = null) } /** + * Return the flag for display sales full summary + * * @param null|string|bool|int|Store $store * @return bool */ @@ -736,6 +807,8 @@ public function displaySalesFullSummary($store = null) } /** + * Return the flag for display sales zero tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -829,15 +902,16 @@ public function getInfoUrl($store = null) /** * Check if necessary do product price conversion + * * If it necessary will be returned conversion type (minus or plus) * * @param null|int|string|Store $store - * @return bool|int + * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function needPriceConversion($store = null) { - $res = 0; + $res = false; $priceIncludesTax = $this->priceIncludesTax($store) || $this->getNeedUseShippingExcludeTax(); if ($priceIncludesTax) { switch ($this->getPriceDisplayType($store)) { @@ -845,7 +919,7 @@ public function needPriceConversion($store = null) case self::DISPLAY_TYPE_BOTH: return self::PRICE_CONVERSION_MINUS; case self::DISPLAY_TYPE_INCLUDING_TAX: - $res = false; + $res = $this->displayCartPricesInclTax($store); break; default: break; From 242aae9f6250bdb3704d8e4e369510e090628405 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 20 Nov 2019 12:49:20 -0600 Subject: [PATCH 2140/2437] MAGETWO-55858: Output escaping methods shouldn't be part of AbstractBlock --- lib/internal/Magento/Framework/View/Element/AbstractBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 628e595229137..6a098ab5a155d 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -877,7 +877,7 @@ public static function extractModuleName($className) $namespace = substr( $className, 0, - strpos($className, '\\' . 'Block' . '\\') + (int)strpos($className, '\\' . 'Block' . '\\') ); return str_replace('\\', '_', $namespace); } From 65272509dcb4d948f72a6299b9ca52cc28f21841 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <engcom-vendorworker-foxtrot@adobe.com> Date: Wed, 20 Nov 2019 16:43:20 +0200 Subject: [PATCH 2141/2437] magento/magento2#25658: MFTF coverage added. --- .../Test/Mftf/Section/AdminCartPriceRulesFormSection.xml | 1 + .../Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml | 3 +++ app/code/Magento/SalesRule/i18n/en_US.csv | 1 + 3 files changed, 5 insertions(+) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 3849d153be465..39399152f5fab 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -98,5 +98,6 @@ <element name="couponQty" type="input" selector="#coupons_qty"/> <element name="generateCouponsButton" type="button" selector="#coupons_generate_button" timeout="30"/> <element name="generatedCouponByIndex" type="text" selector="#couponCodesGrid_table > tbody > tr:nth-child({{var}}) > td.col-code" parameterized="true"/> + <element name="couponGridUsedHeader" type="text" selector="#couponCodesGrid thead th[data-sort='used']"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 9d807de409a0c..d719bb90efd59 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -62,6 +62,9 @@ <waitForPageLoad stepKey="waitFormToReload1"/> <click selector="{{AdminCartPriceRulesFormSection.manageCouponCodesHeader}}" stepKey="expandCouponSection2"/> + <!-- Assert coupon codes grid header is correct --> + <see selector="{{AdminCartPriceRulesFormSection.couponGridUsedHeader}}" userInput="Used" stepKey="seeCorrectUsedHeader"/> + <!-- Grab a coupon code and hold on to it for later --> <grabTextFrom selector="{{AdminCartPriceRulesFormSection.generatedCouponByIndex('1')}}" stepKey="grabCouponCode"/> diff --git a/app/code/Magento/SalesRule/i18n/en_US.csv b/app/code/Magento/SalesRule/i18n/en_US.csv index cd1e471516ac9..83a5aa76ba0c8 100644 --- a/app/code/Magento/SalesRule/i18n/en_US.csv +++ b/app/code/Magento/SalesRule/i18n/en_US.csv @@ -23,6 +23,7 @@ Generate,Generate "Coupon Code","Coupon Code" Created,Created Used,Used +Uses,Uses No,No Yes,Yes "Times Used","Times Used" From 1c17498d35e066ad509d92394e84224f1afa26ea Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Wed, 20 Nov 2019 15:37:35 -0600 Subject: [PATCH 2142/2437] MC-23192: 'Preview template' functionality places the whole email template into GET parameter - fixed func test --- .../Newsletter/Controller/Adminhtml/Template/Preview.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php index 784a62f86b145..c5ed6fb55c48b 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php @@ -7,11 +7,13 @@ namespace Magento\Newsletter\Controller\Adminhtml\Template; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Newsletter\Controller\Adminhtml\Template; /** * View a rendered template. */ -class Preview extends \Magento\Newsletter\Controller\Adminhtml\Template implements HttpPostActionInterface +class Preview extends Template implements HttpPostActionInterface, HttpGetActionInterface { /** * Preview Newsletter template From 59ff70d0473f55fbb6169f6a8c4d9300135def2d Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Wed, 20 Nov 2019 15:42:48 -0600 Subject: [PATCH 2143/2437] MAGETWO-55858: Output escaping methods shouldn't be part of AbstractBlock --- lib/internal/Magento/Framework/View/Element/AbstractBlock.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 6a098ab5a155d..802574110d080 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -822,7 +822,7 @@ public function formatDate( $showTime = false, $timezone = null ) { - $date = $date instanceof \DateTimeInterface ? $date : new \DateTime($date); + $date = $date instanceof \DateTimeInterface ? $date : new \DateTime($date ?? 'now'); return $this->_localeDate->formatDateTime( $date, $format, @@ -845,7 +845,7 @@ public function formatTime( $format = \IntlDateFormatter::SHORT, $showDate = false ) { - $time = $time instanceof \DateTimeInterface ? $time : new \DateTime($time); + $time = $time instanceof \DateTimeInterface ? $time : new \DateTime($time ?? 'now'); return $this->_localeDate->formatDateTime( $time, $showDate ? $format : \IntlDateFormatter::NONE, From d56adbb6e91bb014feafa6febfde65a53b7d3cf3 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Thu, 21 Nov 2019 09:23:14 +0200 Subject: [PATCH 2144/2437] Revert event add checks that method not fires two times at the time --- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 6cc53e3d40d92..94e931f3349d7 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -293,6 +293,7 @@ define([ }; varienGlobalEvents.fireEvent('open_browser_callback', payload); + this.eventBus.fireEvent('open_browser_callback', payload); }.bind(this); } @@ -372,7 +373,7 @@ define([ /** * @param {Object} o */ - openFileBrowser: function (o) { + openFileBrowser: _.debounce(function (o) { var typeTitle = this.translate('Select Images'), storeId = this.config['store_id'] ? this.config['store_id'] : 0, frameDialog = jQuery('div.mce-container[role="dialog"]'), @@ -405,7 +406,7 @@ define([ } ); }); - }, + }, 250), /** * @param {String} string From 644ffcd1c5e836c3131de288175880449f63f964 Mon Sep 17 00:00:00 2001 From: Viktor Sevch <svitja@ukr.net> Date: Thu, 21 Nov 2019 10:08:14 +0200 Subject: [PATCH 2145/2437] MC-18057: Product doesn'y match by "Date" attribute condition --- app/code/Magento/Rule/Model/Condition/AbstractCondition.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index f60817cce608b..67fc3590ac501 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -885,10 +885,8 @@ public function validateAttribute($validatedValue) */ protected function _compareValues($validatedValue, $value, $strict = true) { - if (null === $value || null === $validatedValue) { - return $value == $validatedValue; - } - if ($strict && is_numeric($validatedValue) && is_numeric($value)) { + if (null === $value || null === $validatedValue || + $strict && is_numeric($validatedValue) && is_numeric($value)) { return $validatedValue == $value; } From 748de99356d5fcec2839f860484311255aac7d32 Mon Sep 17 00:00:00 2001 From: Tristan Hofman <tristan@baldwin.be> Date: Thu, 21 Nov 2019 10:56:02 +0100 Subject: [PATCH 2146/2437] ISSUE 25680 Removed the theme.active.editor block from the layout for swagger index page --- .../Magento/Swagger/view/frontend/layout/swagger_index_index.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml index 5a592b9b7c987..059b9ad445806 100644 --- a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml +++ b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml @@ -30,6 +30,7 @@ <!--Remove Magento page content--> <referenceContainer name="page.wrapper" remove="true"/> <referenceBlock name="translate" remove="true"/> + <referenceBlock name="theme.active.editor" remove="true" /> <referenceBlock name="requirejs-config" remove="true"/> <referenceContainer name="root"> <block name="swaggerUiContent" class="Magento\Swagger\Block\Index" template="Magento_Swagger::swagger-ui/index.phtml" /> From 3872f5f63fcb26e13565694392b9feb88c4df9c9 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 21 Nov 2019 13:14:39 +0200 Subject: [PATCH 2147/2437] MC-22972: Credit memo without refunded shipment produces the negative Grand Total --- .../Model/Order/Creditmemo/Total/TaxTest.php | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php index 565d51ff515a2..7d1096b4c9ea1 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php @@ -610,6 +610,143 @@ public function collectDataProvider() ], ]; + // scenario 6: 2 items, 2 invoiced, price includes tax, full discount, free shipping + // partial credit memo, make sure that discount tax compensation (with 100 % discount) is calculated correctly + $result['collect_with_full_discount_product_price'] = [ + 'order_data' => [ + 'data_fields' => [ + 'discount_amount' => -200.00, + 'discount_invoiced' => -200.00, + 'subtotal' => 181.82, + 'subtotal_incl_tax' => 200, + 'base_subtotal' => 181.82, + 'base_subtotal_incl_tax' => 200, + 'subtotal_invoiced' => 181.82, + 'discount_tax_compensation_amount' => 18.18, + 'discount_tax_compensation_invoiced' => 18.18, + 'base_discount_tax_compensation_amount' => 18.18, + 'base_discount_tax_compensation_invoiced' => 18.18, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'tax_invoiced' => 0, + 'base_tax_invoiced' => 0, + 'tax_refunded' => 0, + 'base_tax_refunded' => 0, + 'base_shipping_amount' => 0, + ], + ], + 'creditmemo_data' => [ + 'items' => [ + 'item_1' => [ + 'order_item' => [ + 'qty_invoiced' => 1, + 'tax_amount' => 0, + 'tax_invoiced' => 0, + 'tax_refunded' => null, + 'base_tax_amount' => 0, + 'base_tax_invoiced' => 0, + 'base_tax_refunded' => 0, + 'tax_percent' => 10, + 'qty_refunded' => 0, + 'discount_percent' => 100, + 'discount_amount' => 100, + 'base_discount_amount' => 100, + 'discount_invoiced' => 100, + 'base_discount_invoiced' => 100, + 'row_total' => 90.91, + 'base_row_total' => 90.91, + 'row_invoiced' => 90.91, + 'base_row_invoiced' => 90.91, + 'price_incl_tax' => 100, + 'base_price_incl_tax' => 100, + 'row_total_incl_tax' => 100, + 'base_row_total_incl_tax' => 100, + 'discount_tax_compensation_amount' => 9.09, + 'base_discount_tax_compensation_amount' => 9.09, + 'discount_tax_compensation_invoiced' => 9.09, + 'base_discount_tax_compensation_invoiced' => 9.09, + ], + 'is_last' => true, + 'qty' => 1, + ], + 'item_2' => [ + 'order_item' => [ + 'qty_invoiced' => 1, + 'tax_amount' => 0, + 'tax_invoiced' => 0, + 'tax_refunded' => null, + 'base_tax_amount' => 0, + 'base_tax_invoiced' => 0, + 'base_tax_refunded' => null, + 'tax_percent' => 10, + 'qty_refunded' => 0, + 'discount_percent' => 100, + 'discount_amount' => 100, + 'base_discount_amount' => 100, + 'discount_invoiced' => 100, + 'base_discount_invoiced' => 100, + 'row_total' => 90.91, + 'base_row_total' => 90.91, + 'row_invoiced' => 90.91, + 'base_row_invoiced' => 90.91, + 'price_incl_tax' => 100, + 'base_price_incl_tax' => 100, + 'row_total_incl_tax' => 100, + 'base_row_total_incl_tax' => 100, + 'discount_tax_compensation_amount' => 9.09, + 'base_discount_tax_compensation_amount' => 9.09, + 'discount_tax_compensation_invoiced' => 9.09, + 'base_discount_tax_compensation_invoiced' => 9.09, + ], + 'is_last' => false, + 'qty' => 0, + ], + ], + 'is_last' => false, + 'data_fields' => [ + 'grand_total' => -9.09, + 'base_grand_total' => -9.09, + 'base_shipping_amount' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'invoice' => new MagentoObject( + [ + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + ] + ), + ], + ], + 'expected_results' => [ + 'creditmemo_items' => [ + 'item_1' => [ + 'tax_amount' => 0, + 'base_tax_amount' => 0, + ], + 'item_2' => [ + 'tax_amount' => 0, + 'base_tax_amount' => 0, + ], + ], + 'creditmemo_data' => [ + 'grand_total' => 0, + 'base_grand_total' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + ], + ], + ]; + return $result; } From 277bde305a1637f488fc867a47efe8bb247b0962 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 21 Nov 2019 15:35:42 +0200 Subject: [PATCH 2148/2437] MC-22972: Credit memo without refunded shipment produces the negative Grand Total --- .../Model/Order/Creditmemo/Total/TaxTest.php | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php index 7d1096b4c9ea1..f32ce7aa4715b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php @@ -100,16 +100,18 @@ public function testCollect($orderData, $creditmemoData, $expectedResults) } $this->creditmemo->expects($this->any()) ->method('roundPrice') - ->will($this->returnCallback( - function ($price, $type) use (&$roundingDelta) { - if (!isset($roundingDelta[$type])) { - $roundingDelta[$type] = 0; + ->will( + $this->returnCallback( + function ($price, $type) use (&$roundingDelta) { + if (!isset($roundingDelta[$type])) { + $roundingDelta[$type] = 0; + } + $roundedPrice = round($price + $roundingDelta[$type], 2); + $roundingDelta[$type] = $price - $roundedPrice; + return $roundedPrice; } - $roundedPrice = round($price + $roundingDelta[$type], 2); - $roundingDelta[$type] = $price - $roundedPrice; - return $roundedPrice; - } - )); + ) + ); $this->model->collect($this->creditmemo); From 129771e606bcadca1b7cc7e5e698b8de9e37e35e Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Thu, 21 Nov 2019 21:30:34 +0700 Subject: [PATCH 2149/2437] Unit Test to cover class \Magento\Captcha\CustomerData\Captcha --- .../Test/Unit/CustomerData/CaptchaTest.php | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php diff --git a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php new file mode 100644 index 0000000000000..0c2b1975e6cf4 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\CustomerData; + +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Captcha\CustomerData\Captcha; +use Magento\Captcha\Model\DefaultModel; +use Magento\Customer\Api\Data\CustomerInterface as CustomerData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Test class to cover \Magento\Captcha\CustomerData\Captcha + * + * Class \Magento\Captcha\Test\Unit\CustomerData\CaptchaTest + */ +class CaptchaTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CaptchaHelper | \PHPUnit_Framework_MockObject_MockObject + */ + private $helper; + + /** + * @var CustomerSession | \PHPUnit_Framework_MockObject_MockObject + */ + private $customerSession; + + /** + * @var CustomerData | \PHPUnit_Framework_MockObject_MockObject + */ + private $customerData; + + /** + * @var Captcha + */ + private $model; + + /** + * @var array + */ + private $formIds; + + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * Create mocks and model + */ + protected function setUp() + { + $this->helper = $this->createMock(CaptchaHelper::class); + $this->customerSession = $this->createMock(CustomerSession::class); + $this->formIds = [ + 'user_login' + ]; + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + Captcha::class, + [ + 'helper' => $this->helper, + 'formIds' => $this->formIds, + 'customerSession' => $this->customerSession + ] + ); + } + + /** + * Test getSectionData() when user is login and require captcha + */ + public function testGetSectionData() + { + $emailLogin = 'test@localhost.com'; + + $userLoginModel = $this->createMock(DefaultModel::class); + $userLoginModel->expects($this->any())->method('isRequired')->with($emailLogin) + ->willReturn(true); + $this->helper->expects($this->any())->method('getCaptcha')->with('user_login')->willReturn($userLoginModel); + + $this->customerSession->expects($this->any())->method('isLoggedIn') + ->willReturn(true); + + $this->customerData = $this->createMock(CustomerData::class); + $this->customerData->expects($this->any())->method('getEmail')->willReturn($emailLogin); + $this->customerSession->expects($this->any())->method('getCustomerData') + ->willReturn($this->customerData); + + /* Assert to test */ + $this->assertEquals( + [ + "user_login" => [ + "isRequired" => true, + "timestamp" => time() + ] + ], + $this->model->getSectionData() + ); + } +} From 8d44a36e0bfcb39cd5c9ff79b0a2e03aedd31fd9 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Thu, 21 Nov 2019 22:10:38 +0700 Subject: [PATCH 2150/2437] [Downloadable Product] Cover Sample DataProvider by Unit Test --- .../Form/Modifier/Data/SamplesTest.php | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php diff --git a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php new file mode 100644 index 0000000000000..2f5b47a1d86b5 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php @@ -0,0 +1,158 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Ui\DataProvider\Product\Form\Modifier\Data; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Data\Samples; +use \Magento\Framework\Escaper; +use Magento\Downloadable\Model\Product\Type; +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Downloadable\Helper\File as DownloadableFile; +use Magento\Framework\UrlInterface; +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Test class to cover Sample Modifier + * + * Class \Magento\Downloadable\Test\Unit\Ui\DataProvider\Product\Form\Modifier\Data\SampleTest + */ +class SamplesTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var LocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $locatorMock; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + + /** + * @var DownloadableFile|\PHPUnit_Framework_MockObject_MockObject + */ + private $downloadableFileMock; + + /** + * @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + + /** + * @var ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productMock; + + /** + * @var Samples + */ + private $samples; + + /** + * @return void + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->setMethods(['getSamplesTitle', 'getId', 'getTypeId']) + ->getMockForAbstractClass(); + $this->locatorMock = $this->createMock(LocatorInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->escaperMock = $this->createMock(Escaper::class); + $this->downloadableFileMock = $this->createMock(DownloadableFile::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->samples = $this->objectManagerHelper->getObject( + Samples::class, + [ + 'escaper' => $this->escaperMock, + 'locator' => $this->locatorMock, + 'scopeConfig' => $this->scopeConfigMock, + 'downloadableFile' => $this->downloadableFileMock, + 'urlBuilder' => $this->urlBuilderMock + ] + ); + } + + /** + * Test getSamplesTitle() + * + * @param int|null $id + * @param string $typeId + * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectedGetTitle + * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectedGetValue + * @return void + * @dataProvider getSamplesTitleDataProvider + */ + public function testGetSamplesTitle($id, $typeId, $expectedGetTitle, $expectedGetValue) + { + $title = 'My Title'; + $this->locatorMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getId') + ->willReturn($id); + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn($typeId); + $this->productMock->expects($expectedGetTitle) + ->method('getSamplesTitle') + ->willReturn($title); + $this->scopeConfigMock->expects($expectedGetValue) + ->method('getValue') + ->willReturn($title); + + /* Assert Result */ + $this->assertEquals($title, $this->samples->getSamplesTitle()); + } + + /** + * @return array + */ + public function getSamplesTitleDataProvider() + { + return [ + [ + 'id' => 1, + 'typeId' => Type::TYPE_DOWNLOADABLE, + 'expectedGetTitle' => $this->once(), + 'expectedGetValue' => $this->never(), + ], + [ + 'id' => null, + 'typeId' => Type::TYPE_DOWNLOADABLE, + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + [ + 'id' => 1, + 'typeId' => 'someType', + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + [ + 'id' => null, + 'typeId' => 'someType', + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + ]; + } +} From 414dad5e8f0135974d89c1f9a7d6381197f1ac0b Mon Sep 17 00:00:00 2001 From: Dan Mooney <dmooney@adobe.com> Date: Thu, 21 Nov 2019 09:53:56 -0600 Subject: [PATCH 2151/2437] MC-22813: Changing Quantity and Adding to cart throws exception - Fix static failure --- .../Model/Rule/Condition/ProductTest.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php index 1ac70f31bde87..bebbb04405c4b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php @@ -176,14 +176,16 @@ private function createProductMock(): \PHPUnit_Framework_MockObject_MockObject { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() - ->setMethods([ - 'getAttribute', - 'getId', - 'setQuoteItemQty', - 'setQuoteItemPrice', - 'getTypeId', - 'hasData', - ]) + ->setMethods( + [ + 'getAttribute', + 'getId', + 'setQuoteItemQty', + 'setQuoteItemPrice', + 'getTypeId', + 'hasData', + ] + ) ->getMock(); $productMock ->expects($this->any()) From cef605f171d616d33976ebfcc53874aaf40a2339 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 21 Nov 2019 10:13:52 -0600 Subject: [PATCH 2152/2437] MAGETWO-55858: Output escaping methods shouldn't be part of AbstractBlock --- lib/internal/Magento/Framework/View/Element/AbstractBlock.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 802574110d080..a70b87b8099f6 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -874,6 +874,10 @@ public function getModuleName() */ public static function extractModuleName($className) { + if (!$className) { + return ''; + } + $namespace = substr( $className, 0, From cbca71ed3aa091c7144b1c4633ac7155b6becb09 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 11:38:53 -0600 Subject: [PATCH 2153/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Resolver/Layer/DataProvider/Filters.php | 21 ++++++++++++++-- .../Model/Resolver/LayerFilters.php | 25 ++++++++++++++++++- .../Plugin/Filters/DataProviderPlugin.php | 11 +++++--- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index 786d4f1ab867c..9ce7af8b9012d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -34,14 +34,16 @@ public function __construct( * Get layered navigation filters data * * @param string $layerType + * @param array|null $attributesToFilter * @return array + * @throws \Magento\Framework\Exception\LocalizedException */ - public function getData(string $layerType) : array + public function getData(string $layerType, array $attributesToFilter = null) : array { $filtersData = []; /** @var AbstractFilter $filter */ foreach ($this->filtersProvider->getFilters($layerType) as $filter) { - if ($filter->getItemsCount()) { + if ($this->isNeedToAddFilter($filter, $attributesToFilter)) { $filterGroup = [ 'name' => (string)$filter->getName(), 'filter_items_count' => $filter->getItemsCount(), @@ -60,4 +62,19 @@ public function getData(string $layerType) : array } return $filtersData; } + + private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFilter): bool + { + if ($attributesToFilter === null) { + $result = (bool)$filter->getItemsCount(); + } else { + try { + $filterAttribute = $filter->getAttributeModel(); + $result = in_array($filterAttribute->getAttributeCode(), $attributesToFilter); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $result = false; + } + } + return $result; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php index 0ec7e12e42d55..78ac45a1ad001 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php @@ -44,6 +44,29 @@ public function resolve( return null; } - return $this->filtersDataProvider->getData($value['layer_type']); + $attributes = $this->prepareAttributesResults($value); + return $this->filtersDataProvider->getData($value['layer_type'], $attributes); + } + + /** + * Get attributes available to filtering from the search result + * + * @param array $value + * @return array|null + */ + private function prepareAttributesResults(array $value): ?array + { + $attributes = []; + if (!empty($value['search_result'])) { + $buckets = $value['search_result']->getSearchAggregation()->getBuckets(); + foreach ($buckets as $bucket) { + if (!empty($bucket->getValues())) { + $attributes[] = str_replace('_bucket', '', $bucket->getName()); + } + } + } else { + $attributes = null; + } + return $attributes; } } diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index 14c4ba62c8a47..f40d893cd30b0 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -53,12 +53,17 @@ public function __construct( * @param Filters $subject * @param \Closure $proceed * @param string $layerType + * @param array $attributesToFilter * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function aroundGetData(Filters $subject, \Closure $proceed, string $layerType) : array - { + public function aroundGetData( + Filters $subject, + \Closure $proceed, + string $layerType, + array $attributesToFilter = null + ) : array { $swatchFilters = []; /** @var AbstractFilter $filter */ foreach ($this->filtersProvider->getFilters($layerType) as $filter) { @@ -69,7 +74,7 @@ public function aroundGetData(Filters $subject, \Closure $proceed, string $layer } } - $filtersData = $proceed($layerType); + $filtersData = $proceed($layerType, $attributesToFilter); foreach ($filtersData as $groupKey => $filterGroup) { /** @var AbstractFilter $swatchFilter */ From 33b33e97c55bc099b99740fe180603360d04bc79 Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Thu, 21 Nov 2019 18:40:30 +0100 Subject: [PATCH 2154/2437] Add Tests for operation statuses endpoint --- .../Api/BulkStatusInterfaceTest.php | 39 +++++++++++++++++++ .../Api/OperationRepositoryInterfaceTest.php | 4 +- .../_files/operation_searchable.php | 17 +++++++- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php new file mode 100644 index 0000000000000..771e0d3079b7f --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\AsynchronousOperations\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Framework\Bulk\OperationInterface; + +class BulkStatusInterfaceTest extends WebapiAbstract +{ + const RESOURCE_PATH = '/V1/bulk/'; + const SERVICE_NAME = 'asynchronousOperationsBulkStatusV1'; + const TEST_UUID = "bulk-uuid-searchable-6"; + + /** + * @magentoApiDataFixture Magento/AsynchronousOperations/_files/operation_searchable.php + */ + public function testGetListByBulkStartTime() + { + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . self::TEST_UUID . "/operation-status/" . OperationInterface::STATUS_TYPE_OPEN, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $qty = $this->_webApiCall($serviceInfo); + $this->assertEquals(2, $qty); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php index 8eab6c9fd8676..81ed561a9803e 100644 --- a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php @@ -57,8 +57,8 @@ public function testGetListByBulkStartTime() $this->assertArrayHasKey('items', $response); $this->assertEquals($searchCriteria['searchCriteria'], $response['search_criteria']); - $this->assertEquals(3, $response['total_count']); - $this->assertEquals(3, count($response['items'])); + $this->assertEquals(5, $response['total_count']); + $this->assertEquals(5, count($response['items'])); foreach ($response['items'] as $item) { $this->assertEquals('bulk-uuid-searchable-6', $item['bulk_uuid']); diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php index e74f995f8b57b..7e0d7594c3510 100644 --- a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php @@ -52,7 +52,22 @@ 'error_code' => 2222, 'result_message' => 'Entity with ID=4 does not exist', ], - + [ + 'bulk_uuid' => 'bulk-uuid-searchable-6', + 'topic_name' => 'topic-5', + 'serialized_data' => json_encode(['entity_id' => 5]), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + 'error_code' => null, + 'result_message' => '', + ], + [ + 'bulk_uuid' => 'bulk-uuid-searchable-6', + 'topic_name' => 'topic-5', + 'serialized_data' => json_encode(['entity_id' => 5]), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + 'error_code' => null, + 'result_message' => '', + ] ]; $bulkQuery = "INSERT INTO {$bulkTable} (`uuid`, `user_id`, `description`, `operation_count`, `start_time`)" From 871321f7e8eea79982c8d0f7de06d6ad67df1f67 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 12:47:18 -0600 Subject: [PATCH 2155/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Model/Resolver/Layer/DataProvider/Filters.php | 14 +++++++++++--- .../Plugin/Filters/DataProviderPlugin.php | 5 ++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index 9ce7af8b9012d..fa1807b5709a8 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Layer\Filter\AbstractFilter; use Magento\CatalogGraphQl\Model\Resolver\Layer\FiltersProvider; +use Magento\Catalog\Model\Layer\Filter\Item; /** * Layered navigation filters data provider. @@ -49,7 +50,7 @@ public function getData(string $layerType, array $attributesToFilter = null) : a 'filter_items_count' => $filter->getItemsCount(), 'request_var' => $filter->getRequestVar(), ]; - /** @var \Magento\Catalog\Model\Layer\Filter\Item $filterItem */ + /** @var Item $filterItem */ foreach ($filter->getItems() as $filterItem) { $filterGroup['filter_items'][] = [ 'label' => (string)$filterItem->getLabel(), @@ -63,15 +64,22 @@ public function getData(string $layerType, array $attributesToFilter = null) : a return $filtersData; } + /** + * Check for adding filter to the list + * + * @param AbstractFilter $filter + * @param array $attributesToFilter + * @return bool + */ private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFilter): bool { if ($attributesToFilter === null) { $result = (bool)$filter->getItemsCount(); } else { - try { + if ($filter->hasAttributeModel()) { $filterAttribute = $filter->getAttributeModel(); $result = in_array($filterAttribute->getAttributeCode(), $attributesToFilter); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } else { $result = false; } } diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index f40d893cd30b0..947018ce1bdf4 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -53,10 +53,12 @@ public function __construct( * @param Filters $subject * @param \Closure $proceed * @param string $layerType - * @param array $attributesToFilter + * @param array|null $attributesToFilter * @return array + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * phpcs:disable Generic.Metrics.NestingLevel */ public function aroundGetData( Filters $subject, @@ -97,4 +99,5 @@ public function aroundGetData( return $filtersData; } + //phpcs:enable } From 669e717c9b6a6860aba67db7650031e0e80af3c7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 13:37:59 -0600 Subject: [PATCH 2156/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Model/Resolver/Layer/DataProvider/Filters.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index fa1807b5709a8..c16a174f7787c 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -21,6 +21,11 @@ class Filters */ private $filtersProvider; + /** + * @var array + */ + private $mappings; + /** * Filters constructor. * @param FiltersProvider $filtersProvider @@ -29,6 +34,9 @@ public function __construct( FiltersProvider $filtersProvider ) { $this->filtersProvider = $filtersProvider; + $this->mappings = [ + 'Category' => 'category' + ]; } /** @@ -70,6 +78,7 @@ public function getData(string $layerType, array $attributesToFilter = null) : a * @param AbstractFilter $filter * @param array $attributesToFilter * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFilter): bool { @@ -79,6 +88,8 @@ private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFi if ($filter->hasAttributeModel()) { $filterAttribute = $filter->getAttributeModel(); $result = in_array($filterAttribute->getAttributeCode(), $attributesToFilter); + } elseif (!empty($this->mappings[$filter->getName()])) { + $result = in_array($this->mappings[$filter->getName()], $attributesToFilter); } else { $result = false; } From 4f7497c84b4aafff5777a5d21f8e0a7ed93303df Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 14:28:24 -0600 Subject: [PATCH 2157/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index 947018ce1bdf4..22178b65203af 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -61,10 +61,10 @@ public function __construct( * phpcs:disable Generic.Metrics.NestingLevel */ public function aroundGetData( - Filters $subject, + Flters $subject, \Closure $proceed, string $layerType, - array $attributesToFilter = null + $attributesToFilter = null ) : array { $swatchFilters = []; /** @var AbstractFilter $filter */ From fb108a7efec2916c50c123db50e72a48ea883e21 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Thu, 21 Nov 2019 14:38:58 -0600 Subject: [PATCH 2158/2437] MAGETWO-55858: Output escaping methods shouldn't be part of AbstractBlock --- .../Test/Unit/Block/Widget/DobTest.php | 10 ++- .../Encryption/Test/Unit/EncryptorTest.php | 17 ++-- .../Locale/Test/Unit/TranslatedListsTest.php | 88 +++++++++++++++---- 3 files changed, 91 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index b1d7c455324b3..1fd7fc340e542 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -354,7 +354,15 @@ public function testGetDateFormat(string $locale, string $expectedFormat) public function getDateFormatDataProvider(): array { return [ - ['ar_SA', 'd/M/y'], + [ + 'ar_SA', + preg_replace( + '/[^MmDdYy\/\.\-]/', + '', + (new \IntlDateFormatter('ar_SA', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE)) + ->getPattern() + ) + ], [Resolver::DEFAULT_LOCALE, self::DATE_FORMAT], ]; } diff --git a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php index 6478b74ac05cd..c87982ff5e3e6 100644 --- a/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php +++ b/lib/internal/Magento/Framework/Encryption/Test/Unit/EncryptorTest.php @@ -135,8 +135,11 @@ public function testGetHashRandomSaltSpecifiedLength(): void * * @dataProvider validateHashDataProvider */ - public function testValidateHash($password, $hash, $expected): void + public function testValidateHash($password, $hash, $expected, int $requiresVersion): void { + if ($requiresVersion > $this->encryptor->getLatestHashVersion()) { + $this->markTestSkipped('On current installation encryptor does not support algo #' .$requiresVersion); + } $actual = $this->encryptor->validateHash($password, $hash); $this->assertEquals($expected, $actual); } @@ -149,10 +152,14 @@ public function testValidateHash($password, $hash, $expected): void public function validateHashDataProvider(): array { return [ - ['password', 'hash:salt:1', false], - ['password', '67a1e09bb1f83f5007dc119c14d663aa:salt:0', true], - ['password', '13601bda4ea78e55a07b98866d2be6be0744e3866f13c00c811cab608a28f322:salt:1', true], - ['password', 'c6aad9e058f6c4b06187c06d2b69bf506a786af030f81fb6d83778422a68205e:salt:1:2', true], + ['password', 'hash:salt:1', false, 1], + ['password', '67a1e09bb1f83f5007dc119c14d663aa:salt:0', true, 0], + ['password', '13601bda4ea78e55a07b98866d2be6be0744e3866f13c00c811cab608a28f322:salt:1', true, 1], + //Hashes after customer:hash:upgrade command issued + //Upgraded from version #1 to #2 + ['password', 'c6aad9e058f6c4b06187c06d2b69bf506a786af030f81fb6d83778422a68205e:salt:1:2', true, 2], + //From #0 to #1 + ['password', '3b68ca4706cbae291455e4340478076c1e1618e742b6144cfcc3e50f648903e4:salt:0:1', true, 1] ]; } diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/TranslatedListsTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/TranslatedListsTest.php index 0d51d6fbda305..1d15d2de9f161 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/TranslatedListsTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/TranslatedListsTest.php @@ -44,24 +44,36 @@ class TranslatedListsTest extends TestCase * @var array */ private $expectedLocales = [ - 'en_US' => 'English (United States)', - 'en_GB' => 'English (United Kingdom)', - 'uk_UA' => 'Ukrainian (Ukraine)', - 'de_DE' => 'German (Germany)', - 'sr_Cyrl_RS' => 'Serbian (Cyrillic, Serbia)', - 'sr_Latn_RS' => 'Serbian (Latin, Serbia)' + 'en_US', + 'en_GB', + 'uk_UA', + 'de_DE', + 'sr_Cyrl_RS', + 'sr_Latn_RS' ]; /** - * @var array + * @var string[] + */ + private $languages = [ + 'en_US' => 'English', + 'en_GB' => 'English', + 'uk_UA' => 'Ukrainian', + 'de_DE' => 'German', + 'sr_Cyrl_RS' => 'Serbian', + 'sr_Latn_RS' => 'Serbian' + ]; + + /** + * @var string[] */ - private $expectedTranslatedLocales = [ - 'en_US' => 'English (United States) / English (United States)', - 'en_GB' => 'English (United Kingdom) / English (United Kingdom)', - 'uk_UA' => 'українська (Україна) / Ukrainian (Ukraine)', - 'de_DE' => 'Deutsch (Deutschland) / German (Germany)', - 'sr_Cyrl_RS' => 'српски (ћирилица, Србија) / Serbian (Cyrillic, Serbia)', - 'sr_Latn_RS' => 'Srpski (latinica, Srbija) / Serbian (Latin, Serbia)' + private $countries = [ + 'en_US' => 'United States', + 'en_GB' => 'United Kingdom', + 'uk_UA' => 'Ukraine', + 'de_DE' => 'Germany', + 'sr_Cyrl_RS' => 'Serbia', + 'sr_Latn_RS' => 'Serbia' ]; protected function setUp() @@ -168,20 +180,22 @@ public function testGetOptionTimezones() public function testGetOptionLocales() { + $expected = $this->getExpectedLocales(); $locales = array_intersect( - $this->expectedLocales, + $expected, $this->convertOptionLocales($this->listsModel->getOptionLocales()) ); - $this->assertEquals($this->expectedLocales, $locales); + $this->assertEquals($expected, $locales); } public function testGetTranslatedOptionLocales() { + $expected = $this->getExpectedTranslatedLocales(); $locales = array_intersect( - $this->expectedTranslatedLocales, + $expected, $this->convertOptionLocales($this->listsModel->getTranslatedOptionLocales()) ); - $this->assertEquals($this->expectedTranslatedLocales, $locales); + $this->assertEquals($expected, $locales); } /** @@ -198,4 +212,42 @@ private function convertOptionLocales($optionLocales): array return $result; } + + /** + * Expected translated locales list. + * + * @return string[] + */ + private function getExpectedTranslatedLocales(): array + { + $expected = []; + foreach ($this->expectedLocales as $locale) { + $script = \Locale::getDisplayScript($locale); + $scriptTranslated = $script ? \Locale::getDisplayScript($locale, $locale) .', ' : ''; + $expected[$locale] = ucwords(\Locale::getDisplayLanguage($locale, $locale)) + . ' (' . $scriptTranslated + . \Locale::getDisplayRegion($locale, $locale) . ') / ' + . $this->languages[$locale] + . ' (' . ($script ? $script .', ' : '') . $this->countries[$locale] . ')'; + } + + return $expected; + } + + /** + * Expected locales list. + * + * @return string[] + */ + private function getExpectedLocales(): array + { + $expected = []; + foreach ($this->expectedLocales as $locale) { + $script = \Locale::getScript($locale); + $scriptDisplayed = $script ? \Locale::getDisplayScript($locale) . ', ' : ''; + $expected[$locale] = $this->languages[$locale] .' (' .$scriptDisplayed .$this->countries[$locale] .')'; + } + + return $expected; + } } From 76b782005c1913981244f1547d74af5ef80c7433 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 21 Nov 2019 15:08:52 -0600 Subject: [PATCH 2159/2437] MC-17545: Remove deprecation annotations from the PatchVersionInterface --- .../Magento/Framework/Setup/Patch/PatchVersionInterface.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php index fb2b0e2c379f4..1638d67041a11 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php @@ -6,9 +6,8 @@ namespace Magento\Framework\Setup\Patch; /** - * For backward compatibility with versioned style module installation. Deprecated since creation. + * For backward compatibility with versioned style module installation. * - * @deprecated */ interface PatchVersionInterface { @@ -19,7 +18,6 @@ interface PatchVersionInterface * by old mechanism of UpgradeData.php script * * @return string - * @deprecated since appearance, required for backward compatibility */ public static function getVersion(); } From bb2d0f8cf6c3f701a086a58caf425943b97a15b6 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 15:16:12 -0600 Subject: [PATCH 2160/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Model/Resolver/Layer/DataProvider/Filters.php | 9 ++++++--- .../Plugin/Filters/DataProviderPlugin.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index c16a174f7787c..75186ac58384f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -88,10 +88,13 @@ private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFi if ($filter->hasAttributeModel()) { $filterAttribute = $filter->getAttributeModel(); $result = in_array($filterAttribute->getAttributeCode(), $attributesToFilter); - } elseif (!empty($this->mappings[$filter->getName()])) { - $result = in_array($this->mappings[$filter->getName()], $attributesToFilter); } else { - $result = false; + $name = (string)$filter->getName(); + if (array_key_exists($name, $this->mappings)) { + $result = in_array($this->mappings[$name], $attributesToFilter); + } else { + $result = false; + } } } return $result; diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index 22178b65203af..9b3c96cabbab9 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -61,7 +61,7 @@ public function __construct( * phpcs:disable Generic.Metrics.NestingLevel */ public function aroundGetData( - Flters $subject, + Filters $subject, \Closure $proceed, string $layerType, $attributesToFilter = null From 37106029ec063d5e9710ee70ee4a0ed9239536f0 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 15:47:38 -0600 Subject: [PATCH 2161/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index 9b3c96cabbab9..c14ec68b9ab38 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -36,6 +36,7 @@ class DataProviderPlugin * * @param FiltersProvider $filtersProvider * @param \Magento\Swatches\Helper\Data $swatchHelper + * @param \Magento\Swatches\Block\LayeredNavigation\RenderLayered $renderLayered */ public function __construct( FiltersProvider $filtersProvider, From be319e9114ecbeee75e1c65473f80f68dfb3d1af Mon Sep 17 00:00:00 2001 From: Alex Ghiban <drew7721@gmail.com> Date: Thu, 21 Nov 2019 16:58:56 -0500 Subject: [PATCH 2162/2437] Remove UserLockedException catch. `UserLockedException` extends `AuthenticationException` and provides the same message. These lines were useless. --- app/code/Magento/Customer/Controller/Account/LoginPost.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 4091a068e3094..011d8f5c944d6 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -204,11 +204,6 @@ public function execute() 'This account is not confirmed. <a href="%1">Click here</a> to resend confirmation email.', $value ); - } catch (UserLockedException $e) { - $message = __( - 'The account sign-in was incorrect or your account is disabled temporarily. ' - . 'Please wait and try again later.' - ); } catch (AuthenticationException $e) { $message = __( 'The account sign-in was incorrect or your account is disabled temporarily. ' From 1030ac0bfa0941126d389a6a10eafa253b6dec8a Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 16:54:23 -0600 Subject: [PATCH 2163/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Model/Resolver/Layer/DataProvider/Filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index 75186ac58384f..f6d8edf1fe9b5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -93,7 +93,7 @@ private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFi if (array_key_exists($name, $this->mappings)) { $result = in_array($this->mappings[$name], $attributesToFilter); } else { - $result = false; + $result = true; } } } From ddb9acb6aab086844306e637c38e01b1433de583 Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Fri, 22 Nov 2019 00:01:10 +0100 Subject: [PATCH 2164/2437] Fix document elements check to prevent WYSIWYG error on IE When creating a document using DomParser's parseFromString method from an empty string, IE 11 returns a document which both head and body elements are null, which caused the errors. Fixes https://github.com/magento/magento2/issues/13209 --- .../tiny_mce/plugins/magentovariable/editor_plugin.js | 6 ++---- .../wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js index 92c13fca63920..e54aaa1006d4b 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js @@ -196,10 +196,8 @@ define([ } }); - returnval += doc.head.innerHTML ? - doc.head.innerHTML.replace(/&quot;/g, '"') : ''; - returnval += doc.body.innerHTML ? - doc.body.innerHTML.replace(/&quot;/g, '"') : ''; + returnval += doc.head ? doc.head.innerHTML.replace(/&quot;/g, '"') : ''; + returnval += doc.body ? doc.body.innerHTML.replace(/&quot;/g, '"') : ''; return returnval ? returnval : content; }, diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js index e6669d77a3889..f163206a13656 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js @@ -212,10 +212,8 @@ define([ widgetEl.parentNode.removeChild(widgetEl); }); - returnval += doc.head.innerHTML ? - doc.head.innerHTML.replace(/&quot;/g, '"') : ''; - returnval += doc.body.innerHTML ? - doc.body.innerHTML.replace(/&quot;/g, '"') : ''; + returnval += doc.head ? doc.head.innerHTML.replace(/&quot;/g, '"') : ''; + returnval += doc.body ? doc.body.innerHTML.replace(/&quot;/g, '"') : ''; return returnval ? returnval : content; }, From 982d64a1aaf43843b07c3b05b2e13d70a06ce080 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 21 Nov 2019 17:25:25 -0600 Subject: [PATCH 2165/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../GraphQl/Catalog/ProductSearchTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index e1615eb9a667e..6e732d68fe7d3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -31,6 +31,37 @@ */ class ProductSearchTest extends GraphQlAbstract { + /** + * Verify that filters for non-existing category are empty + * + * @throws \Exception + */ + public function testFilterForNonExistingCategory() + { + $query = <<<QUERY +{ + products(filter: {category_id: {eq: "99999999"}}) { + filters { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey( + 'filters', + $response['products'], + 'Filters are missing in product query result.' + ); + + $this->assertEmpty( + $response['products']['filters'], + 'Returned filters data set does not empty' + ); + } + /** * Verify that layered navigation filters and aggregations are correct for product query * From 7534f33b2bbed6d1bb86e6b3fb22218d45c97158 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 22 Nov 2019 13:01:32 +0700 Subject: [PATCH 2166/2437] Refactor to pass review --- .../Captcha/Test/Unit/CustomerData/CaptchaTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php index 0c2b1975e6cf4..b34de7778e176 100644 --- a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php +++ b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php @@ -23,17 +23,17 @@ class CaptchaTest extends \PHPUnit\Framework\TestCase { /** - * @var CaptchaHelper | \PHPUnit_Framework_MockObject_MockObject + * @var CaptchaHelper|\PHPUnit_Framework_MockObject_MockObject */ private $helper; /** - * @var CustomerSession | \PHPUnit_Framework_MockObject_MockObject + * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject */ private $customerSession; /** - * @var CustomerData | \PHPUnit_Framework_MockObject_MockObject + * @var CustomerData|\PHPUnit_Framework_MockObject_MockObject */ private $customerData; @@ -76,7 +76,7 @@ protected function setUp() /** * Test getSectionData() when user is login and require captcha */ - public function testGetSectionData() + public function testGetSectionDataWhenLoginAndRequireCaptcha() { $emailLogin = 'test@localhost.com'; From 822364927cfda079506ab0400734f7acbc372418 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Fri, 22 Nov 2019 11:10:42 +0200 Subject: [PATCH 2167/2437] Adding dependency for New Relic system configs --- .../etc/adminhtml/system.xml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml index 98f9c55adbdf0..60c52164021d9 100644 --- a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml +++ b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml @@ -19,37 +19,61 @@ </field> <field id="api_url" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Relic API URL</label> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="insights_api_url" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Insights API URL</label> <comment>Use %s to replace the account ID in the URL</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="account_id" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Account ID</label> <comment><![CDATA["Need a New Relic account? <a href="http://www.newrelic.com/magento" target="_blank">Click here to get one]]></comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="app_id" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Application ID</label> <comment>This can commonly be found at the end of the URL when viewing the APM after "/applications/"</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="api" translate="label comment" type="obscure" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic API Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>This is located by navigating to Events -> Deployments from the New Relic APM website</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="insights_insert_key" translate="label comment" type="obscure" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Insights API Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>Generated under Insights in Manage data -> API Keys -> Insert Keys</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="app_name" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Application Name</label> <comment>This is located by navigating to Settings from the New Relic APM website</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="separate_apps" translate="label comment" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Adminhtml and Frontend as Separate Apps</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set.</comment> + <depends> + <field id="enable">1</field> + </depends> </field> </group> <group id="cron" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> From 9ce49c47861dd1ca058d516f02b2da7441ce203b Mon Sep 17 00:00:00 2001 From: Lyzun Oleksandr <alex.nuzil@gmail.com> Date: Fri, 22 Nov 2019 11:01:54 +0100 Subject: [PATCH 2168/2437] Fix tests --- .../Api/BulkStatusInterfaceTest.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php index 771e0d3079b7f..de4ec207653a3 100644 --- a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php @@ -15,6 +15,7 @@ class BulkStatusInterfaceTest extends WebapiAbstract { const RESOURCE_PATH = '/V1/bulk/'; const SERVICE_NAME = 'asynchronousOperationsBulkStatusV1'; + const GET_COUNT_OPERATION_NAME = "GetOperationsCountByBulkIdAndStatus"; const TEST_UUID = "bulk-uuid-searchable-6"; /** @@ -22,18 +23,22 @@ class BulkStatusInterfaceTest extends WebapiAbstract */ public function testGetListByBulkStartTime() { - + $resourcePath = self::RESOURCE_PATH . self::TEST_UUID . "/operation-status/" . OperationInterface::STATUS_TYPE_OPEN; $serviceInfo = [ 'rest' => [ - 'resourcePath' => self::RESOURCE_PATH . self::TEST_UUID . "/operation-status/" . OperationInterface::STATUS_TYPE_OPEN, + 'resourcePath' => $resourcePath, 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET ], 'soap' => [ 'service' => self::SERVICE_NAME, - 'operation' => self::SERVICE_NAME . 'Get', + 'serviceVersion' => 'V1', + 'operation' => self::SERVICE_NAME . self::GET_COUNT_OPERATION_NAME ], ]; - $qty = $this->_webApiCall($serviceInfo); + $qty = $this->_webApiCall( + $serviceInfo, + ['bulkUuid' => self::TEST_UUID, 'status' => OperationInterface::STATUS_TYPE_OPEN] + ); $this->assertEquals(2, $qty); } } From a880a833582e76446ca12e23850805da0357299c Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 22 Nov 2019 14:38:47 +0200 Subject: [PATCH 2169/2437] remove duplicated events on wisiwyg initialization --- lib/internal/Magento/Framework/Data/Form/Element/Editor.php | 4 ---- lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index b5f2017501c01..92dc2e6c97ecd 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -533,10 +533,6 @@ protected function getInlineJs($jsSetupObject, $forceLoad) $jsSetupObject . ')); varienGlobalEvents.attachEventHandler("formSubmit", editorFormValidationHandler); - varienGlobalEvents.clearEventHandlers("open_browser_callback"); - varienGlobalEvents.attachEventHandler("open_browser_callback", ' . - $jsSetupObject . - '.openFileBrowser); //]]> }); </script>'; diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index 94e931f3349d7..df691601eccb9 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -373,7 +373,7 @@ define([ /** * @param {Object} o */ - openFileBrowser: _.debounce(function (o) { + openFileBrowser: function (o) { var typeTitle = this.translate('Select Images'), storeId = this.config['store_id'] ? this.config['store_id'] : 0, frameDialog = jQuery('div.mce-container[role="dialog"]'), @@ -406,7 +406,7 @@ define([ } ); }); - }, 250), + }, /** * @param {String} string From 07fc246f5db339b9a79cc9ebe841df28eb1a239e Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 22 Nov 2019 15:03:06 +0200 Subject: [PATCH 2170/2437] Static test fix --- lib/internal/Magento/Framework/Data/Form/Element/Editor.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index 92dc2e6c97ecd..0c9a3ee5b9495 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -309,9 +309,11 @@ protected function _getPluginButtonsHtml($visible = true) $configStyle = ''; if (isset($buttonOptions['style'])) { $configStyle = $buttonOptions['style']; - } + } + // phpcs:disable Magento2.Performance.ForeachArrayMerge $buttonOptions = array_merge($buttonOptions, ['style' => 'display:none;' . $configStyle]); - } + // phpcs:enable + } $buttonsHtml .= $this->_getButtonHtml($buttonOptions); } } From ae2416b3b537ce27b7696673505dad5c7e6e78c9 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 22 Nov 2019 15:34:51 +0200 Subject: [PATCH 2171/2437] MC-23203: Admin: Simple product with all custom attributes --- .../Attribute/Save/AbstractAttributeTest.php | 183 ++++++++++++++++++ .../Attribute/Save/AttributeDateTest.php | 79 ++++++++ .../Attribute/Save/AttributeDropdownTest.php | 70 +++++++ .../Save/AttributeMultiSelectTest.php | 79 ++++++++ .../Attribute/Save/AttributePriceTest.php | 93 +++++++++ .../Attribute/Save/AttributeTextAreaTest.php | 70 +++++++ .../Save/AttributeTextSwatchTest.php | 70 +++++++ .../Attribute/Save/AttributeTextTest.php | 70 +++++++ .../Save/AttributeVisualSwatchTest.php | 70 +++++++ .../Attribute/Save/AttributeYesNoTest.php | 70 +++++++ .../Product/Gallery/CreateHandlerTest.php | 26 +++ .../Save/AttributeFixedProductTaxTest.php | 134 +++++++++++++ 12 files changed, 1014 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php new file mode 100644 index 0000000000000..88ca9e589ebfb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Model\Entity\Attribute\Exception; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Base class for text product attributes + */ +abstract class AbstractAttributeTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var AttributeRepositoryInterface */ + protected $attributeRepository; + + /** @var ProductRepositoryInterface */ + protected $productRepository; + + /** @var Attribute */ + protected $attribute; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->attributeRepository = $this->objectManager->create(AttributeRepositoryInterface::class); + $this->attribute = $this->attributeRepository->get( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $this->getAttributeCode() + ); + } + + /** + * @dataProvider productProvider + * @param $productSku + * @return void + */ + public function testSaveText(string $productSku): void + { + $product = $this->setAttributeValueAndValidate($productSku, $this->getDefaultAttributeValue()); + $product = $this->productRepository->save($product); + $this->assertEquals($this->getDefaultAttributeValue(), $product->getData($this->getAttributeCode())); + } + + /** + * @dataProvider productProvider + * @param string $productSku + * @return void + */ + public function testRequiredAttribute(string $productSku): void + { + $this->expectException(Exception::class); + $messageFormat = 'The "%s" attribute value is empty. Set the attribute and try again.'; + $this->expectExceptionMessage((string)__(sprintf($messageFormat, $this->attribute->getDefaultFrontendLabel()))); + $this->prepareAttribute(['is_required' => true]); + $this->unsetAttributeValueAndValidate($productSku); + } + + /** + * @dataProvider productProvider + * @param string $productSku + * @return void + */ + public function testDefaultValue(string $productSku): void + { + $this->prepareAttribute(['default_value' => $this->getDefaultAttributeValue()]); + $product = $this->unsetAttributeValueAndValidate($productSku); + $product = $this->productRepository->save($product); + $this->assertEquals($this->getDefaultAttributeValue(), $product->getData($this->getAttributeCode())); + } + + /** + * @dataProvider uniqueTestProvider + * @param string $firstSku + * @param string $secondSku + * @return void + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + $this->expectException(Exception::class); + $messageFormat = 'The value of the "%s" attribute isn\'t unique. Set a unique value and try again.'; + $this->expectExceptionMessage((string)__(sprintf($messageFormat, $this->attribute->getDefaultFrontendLabel()))); + $this->prepareAttribute(['is_unique' => 1]); + $product = $this->setAttributeValueAndValidate($firstSku, $this->getDefaultAttributeValue()); + $this->productRepository->save($product); + $this->setAttributeValueAndValidate($secondSku, $this->getDefaultAttributeValue()); + } + + /** + * Set attribute value to product and validate the product + * + * @param string $attributeValue + * @param string $productSku + * @return ProductInterface + */ + protected function setAttributeValueAndValidate(string $productSku, string $attributeValue): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->addData([$this->getAttributeCode() => $attributeValue]); + $product->validate(); + + return $product; + } + + /** + * Unset attribute value of the product and validate the product + * + * @param string $productSku + * @return ProductInterface + */ + private function unsetAttributeValueAndValidate(string $productSku): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->unsetData($this->getAttributeCode()); + $product->validate(); + + return $product; + } + + /** + * Prepare attribute to test + * + * @param array $data + * @return void + */ + private function prepareAttribute(array $data): void + { + $attribute = $this->attributeRepository->get( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $this->getAttributeCode() + ); + $attribute->addData($data); + $this->attributeRepository->save($attribute); + } + + /** + * Returns attribute code for current test + * + * @return string + */ + abstract protected function getAttributeCode(): string; + + /** + * Get default value for current attribute + * + * @return string + */ + abstract protected function getDefaultAttributeValue(): string; + + /** + * Products provider for tests + * + * @return array + */ + abstract public function productProvider(): array; + + /** + * Provider for unique attribute tests + * + * @return array + */ + abstract public function uniqueTestProvider(): array; +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php new file mode 100644 index 0000000000000..625418592d4e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeDateTest extends AbstractAttributeTest +{ + /** + * @dataProvider productProvider + * @param string $productSku + */ + public function testDefaultValue(string $productSku): void + { + $this->markTestSkipped('Test is blocked by issue MC-28950'); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'date_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->attribute->getBackend()->formatDate('11/20/19'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php new file mode 100644 index 0000000000000..b724279a55f42 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeDropdownTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'dropdown_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->attribute->getSource()->getOptionId('Option 1'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php new file mode 100644 index 0000000000000..50280060daad1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeMultiSelectTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'multiselect_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->attribute->getSource()->getOptionId('Option 1'); + } + + /** + * @inheritdoc + * @dataProvider productProvider + */ + public function testDefaultValue(string $productSku): void + { + $this->markTestSkipped('Test is blocked by issue MC-29019'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php new file mode 100644 index 0000000000000..a56058ae5007a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +use Magento\Eav\Model\Entity\Attribute\Exception; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributePriceTest extends AbstractAttributeTest +{ + /** + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + $this->markTestSkipped('Test is blocked by issue MC-29019'); + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testNegativeValue(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage((string)__('Please enter a number 0 or greater in this field.')); + $this->setAttributeValueAndValidate('simple2', '-1'); + } + + /** + * @dataProvider productProvider + * @param string $productSku + */ + public function testDefaultValue(string $productSku): void + { + // product price attribute does not support default value + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'decimal_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return '100'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php new file mode 100644 index 0000000000000..6f9b26e6680b1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeTextAreaTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'text_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return 'default text area value'; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ] + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php new file mode 100644 index 0000000000000..0650155ba3e47 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeTextSwatchTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'text_swatch_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->attribute->getSource()->getOptionId('Option 2'); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php new file mode 100644 index 0000000000000..e1fa9f6229f0d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeTextTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'varchar_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return 'default text value'; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php new file mode 100644 index 0000000000000..5666d4223505d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeVisualSwatchTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'visual_swatch_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->attribute->getSource()->getOptionId('option 2'); + } + + /** + * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php new file mode 100644 index 0000000000000..71d2855fdb3bf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Save; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeYesNoTest extends AbstractAttributeTest +{ + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'boolean_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return '1'; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueTestProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueTestProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php index 03455bb341cae..2277470e33b12 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php @@ -251,6 +251,32 @@ public function additionalGalleryFieldsProvider(): array ]; } + /** + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @return void + */ + public function testExecuteWithCustomMediaAttribute(): void + { + $data = [ + 'media_gallery' => ['images' => ['image' => ['file' => $this->fileName, 'label' => '']]], + 'image' => 'no_selection', + 'small_image' => 'no_selection', + 'swatch_image' => 'no_selection', + 'thumbnail' => 'no_selection', + 'image_attribute' => $this->fileName + ]; + $product = $this->initProduct($data); + $this->createHandler->execute($product); + $mediaAttributeValue = $this->productResource->getAttributeRawValue( + $product->getId(), + ['image_attribute'], + $product->getStoreId() + ); + $this->assertEquals($this->fileName, $mediaAttributeValue); + } + /** * Returns product for testing. * diff --git a/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php b/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php new file mode 100644 index 0000000000000..af474d8f8954f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Model\Product\Attribute\Save; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Eav\Model\Entity\Attribute\Exception; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class AttributeFixedProductTaxTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var ProductRepositoryInterface */ + protected $productRepository; + + /** @var string */ + private $attributeCode; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->attributeCode = 'fixed_product_attribute'; + } + + /** + * @dataProvider FPTProvider + * @param array $data + * @param array $expectedData + * @return void + */ + public function testSaveProductWithFPTAttribute(array $data, array $expectedData): void + { + $product = $this->productRepository->get('simple2'); + $product->addData([$this->attributeCode => $data]); + $product = $this->productRepository->save($product); + $this->assertEquals($expectedData, $product->getData($this->attributeCode)); + } + + /** + * @return array + */ + public function FPTProvider(): array + { + return [ + [ + 'data' => [ + [ + 'region_id' => '0', + 'country' => 'GB', + 'val' => '', + 'value' => '15', + 'website_id' => '0', + 'state' => '', + ], + [ + 'region_id' => '1', + 'country' => 'US', + 'val' => '', + 'value' => '35', + 'website_id' => '0', + 'state' => '', + ], + ], + 'expected_data' => [ + [ + 'website_id' => '0', + 'country' => 'GB', + 'state' => '0', + 'value' => '15.000', + 'website_value' => 15.0, + ], + [ + 'website_id' => '0', + 'country' => 'US', + 'state' => '0', + 'value' => '35.000', + 'website_value' => 35.0 + ], + ], + ], + ]; + } + + /** + * @return void + */ + public function testSaveProductWithFPTAttributeWithDuplicates(): void + { + $attributeValues = [ + [ + 'region_id' => '0', + 'country' => 'GB', + 'val' => '', + 'value' => '15', + 'website_id' => '0', + 'state' => '', + ], + [ + 'region_id' => '0', + 'country' => 'GB', + 'val' => '', + 'value' => '15', + 'website_id' => '0', + 'state' => '', + ], + ]; + $this->expectException(Exception::class); + $message = 'Set unique country-state combinations within the same fixed product tax. ' + . 'Verify the combinations and try again.'; + $this->expectExceptionMessage((string)__($message)); + $product = $this->productRepository->get('simple2'); + $product->addData([$this->attributeCode => $attributeValues]); + $this->productRepository->save($product); + } +} From a6ba5850045eb2276fb91ce6b12bf0e12b90b383 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 22 Nov 2019 15:24:42 +0200 Subject: [PATCH 2172/2437] Remove duplicated event --- .../Magento/Framework/Data/Form/Element/Editor.php | 8 ++++---- .../mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index 0c9a3ee5b9495..5059fb38f29ca 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -309,11 +309,11 @@ protected function _getPluginButtonsHtml($visible = true) $configStyle = ''; if (isset($buttonOptions['style'])) { $configStyle = $buttonOptions['style']; - } + } // phpcs:disable Magento2.Performance.ForeachArrayMerge $buttonOptions = array_merge($buttonOptions, ['style' => 'display:none;' . $configStyle]); - // phpcs:enable - } + // phpcs:enable + } $buttonsHtml .= $this->_getButtonHtml($buttonOptions); } } @@ -411,7 +411,7 @@ protected function _getButtonHtml($data) protected function _wrapIntoContainer($html) { if (!$this->getConfig('use_container')) { - return '<div class="admin__control-wysiwig">' .$html . '</div>'; + return '<div class="admin__control-wysiwig">' . $html . '</div>'; } $html = '<div id="editor' . $this->getHtmlId() . '"' diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js index df691601eccb9..aea636066be61 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter.js @@ -118,6 +118,7 @@ define([ tinyMCE4.init(settings); this.getPluginButtons().hide(); varienGlobalEvents.clearEventHandlers('open_browser_callback'); + this.eventBus.clearEventHandlers('open_browser_callback'); this.eventBus.attachEventHandler('open_browser_callback', tinyMceEditors.get(self.id).openFileBrowser); }.bind(this)); }, From 871bef6380cfc16daffe973b3e69a618c20ebc4e Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com> Date: Fri, 22 Nov 2019 16:33:04 +0200 Subject: [PATCH 2173/2437] magento/magento2#25569: Static tests fix. --- .../AsynchronousOperations/Api/BulkStatusInterfaceTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php index de4ec207653a3..135af2b556734 100644 --- a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php @@ -23,7 +23,10 @@ class BulkStatusInterfaceTest extends WebapiAbstract */ public function testGetListByBulkStartTime() { - $resourcePath = self::RESOURCE_PATH . self::TEST_UUID . "/operation-status/" . OperationInterface::STATUS_TYPE_OPEN; + $resourcePath = self::RESOURCE_PATH + . self::TEST_UUID + . "/operation-status/" + . OperationInterface::STATUS_TYPE_OPEN; $serviceInfo = [ 'rest' => [ 'resourcePath' => $resourcePath, From b423aaa869641e51b439b882b214f895b9996c9a Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 22 Nov 2019 21:48:49 +0700 Subject: [PATCH 2174/2437] [ImportExport] Cover Helper Data by Unit Test --- .../Test/Unit/Helper/DataTest.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php b/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..85630d2106b45 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Helper; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\File\Size as FileSize; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\ImportExport\Helper\Data as HelperData; +use PHPUnit\Framework\TestCase; + +/** + * Test class to cover Data Helper + * + * Class \Magento\ImportExport\Test\Unit\Helper\DataTest + */ +class DataTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var FileSize|PHPUnit_Framework_MockObject_MockObject + */ + private $fileSizeMock; + + /** + * @var Context|PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var ScopeConfigInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var HelperData|PHPUnit_Framework_MockObject_MockObject + */ + private $helperData; + + /** + * Set up environment + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->fileSizeMock = $this->createMock(FileSize::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->contextMock->expects($this->any())->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->helperData = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock, + 'fileSize' => $this->fileSizeMock + ] + ); + } + + /** + * Test getMaxUploadSizeMessage() with data provider below + * + * @param float $maxImageSize + * @param string $expected + * @return void + * @dataProvider getMaxUploadSizeMessageDataProvider + */ + public function testGetMaxUploadSizeMessage($maxImageSize, $expected) + { + $this->fileSizeMock->expects($this->any())->method('getMaxFileSizeInMb')->willReturn($maxImageSize); + $this->assertEquals($expected, $this->helperData->getMaxUploadSizeMessage()); + } + + /** + * DataProvider for testGetMaxUploadSizeMessage() function + * + * @return array + */ + public function getMaxUploadSizeMessageDataProvider() + { + return [ + 'Test with max image size = 10Mb' => [ + 'maxImageSize' => 10, + 'expected' => 'Make sure your file isn\'t more than 10M.', + ], + 'Test with max image size = 0' => [ + 'maxImageSize' => 0, + 'expected' => 'We can\'t provide the upload settings right now.', + ] + ]; + } + + /** + * Test getLocalValidPaths() + * + * @return void + */ + public function testGetLocalValidPaths() + { + $paths = [ + 'available' => [ + 'export_xml' => 'var/export/*/*.xml', + 'export_csv' => 'var/export/*/*.csv', + 'import_xml' => 'var/import/*/*.xml', + 'import_csv' => 'var/import/*/*.csv', + ] + ]; + $this->scopeConfigMock->expects($this->any())->method('getValue') + ->with(HelperData::XML_PATH_EXPORT_LOCAL_VALID_PATH) + ->willReturn($paths); + + $this->assertEquals($paths, $this->helperData->getLocalValidPaths()); + } + + /** + * Test getBunchSize() + * + * @return void + */ + public function testGetBunchSize() + { + $bunchSize = '100'; + + $this->scopeConfigMock->expects($this->any())->method('getValue') + ->with(HelperData::XML_PATH_BUNCH_SIZE) + ->willReturn($bunchSize); + + $this->assertEquals(100, $this->helperData->getBunchSize()); + } +} From 6d19852ac3af89bd36e0a3527d010c629ffa3a97 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com> Date: Fri, 22 Nov 2019 16:59:41 +0200 Subject: [PATCH 2175/2437] magento/magento2#25569: Static tests fix. --- .../AsynchronousOperations/Api/BulkStatusInterfaceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php index 135af2b556734..46ffb39878c0d 100644 --- a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php @@ -23,7 +23,7 @@ class BulkStatusInterfaceTest extends WebapiAbstract */ public function testGetListByBulkStartTime() { - $resourcePath = self::RESOURCE_PATH + $resourcePath = self::RESOURCE_PATH . self::TEST_UUID . "/operation-status/" . OperationInterface::STATUS_TYPE_OPEN; From 7ac931df6589b9c8069c4f2d97062d38510c7e03 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 22 Nov 2019 09:06:04 -0600 Subject: [PATCH 2176/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Magento/GraphQl/Catalog/ProductSearchTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 6e732d68fe7d3..73baa59b92bf9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -123,10 +123,15 @@ public function testFilterLn() usort($expectedFilters, [$this, 'compareFilterNames']); usort($actualFilters, [$this, 'compareFilterNames']); + $errMsg = print_r($expectedFilters, true); + $errMsg .= '---'; + $errMsg .= print_r($actualFilters, true); + $this->assertFilters( ['products' => ['filters' => $actualFilters]], $expectedFilters, - 'Returned filters data set does not match the expected value' + //'Returned filters data set does not match the expected value' + $errMsg ); } From 1be4e03189b9461ebff420596f0bc6a44a60a9ec Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 22 Nov 2019 17:07:53 +0200 Subject: [PATCH 2177/2437] Fix for tinymce3 editor --- app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js index bb3300baf988a..86602b2017c15 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js +++ b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js @@ -78,6 +78,8 @@ define([ } tinyMCE3.init(this.getSettings(mode)); + varienGlobalEvents.clearEventHandlers("open_browser_callback"); + varienGlobalEvents.attachEventHandler("open_browser_callback", tinyMceEditors.get(this.id).openFileBrowser); }, /** From 806576a72355803e7bb768ccbeda35b82bf29faf Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 22 Nov 2019 17:26:59 +0200 Subject: [PATCH 2178/2437] MC-23203: Admin: Simple product with all custom attributes --- .../Model/Product/Attribute/Save/AbstractAttributeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php index 88ca9e589ebfb..7a49475fd5107 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php @@ -55,7 +55,7 @@ protected function setUp() * @param $productSku * @return void */ - public function testSaveText(string $productSku): void + public function testSaveAttribute(string $productSku): void { $product = $this->setAttributeValueAndValidate($productSku, $this->getDefaultAttributeValue()); $product = $this->productRepository->save($product); From 6976aabdfdab91a9d06e412c2ed619538ed034b6 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Thu, 21 Nov 2019 16:05:04 -0600 Subject: [PATCH 2179/2437] MC-21868: Magento 2.3.3 notifications converted to attachments in MS Exchange - Disposition Header --- lib/internal/Magento/Framework/Mail/Message.php | 1 - lib/internal/Magento/Framework/Mail/MimePart.php | 5 ++++- .../Magento/Framework/Mail/Test/Unit/MessageTest.php | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 1f423e8010870..0e4d79aac9331 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -171,7 +171,6 @@ private function createMimeFromString($body, $messageType) $part = new Part($body); $part->setCharset($this->zendMessage->getEncoding()); $part->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE); - $part->setDisposition(Mime::DISPOSITION_INLINE); $part->setType($messageType); $mimeMessage = new \Zend\Mime\Message(); $mimeMessage->addPart($part); diff --git a/lib/internal/Magento/Framework/Mail/MimePart.php b/lib/internal/Magento/Framework/Mail/MimePart.php index 9b9bb6eadbec3..a43ed4b36e072 100644 --- a/lib/internal/Magento/Framework/Mail/MimePart.php +++ b/lib/internal/Magento/Framework/Mail/MimePart.php @@ -15,6 +15,9 @@ */ class MimePart implements MimePartInterface { + /** + * UTF-8 charset + */ public const CHARSET_UTF8 = 'utf-8'; /** @@ -47,7 +50,7 @@ public function __construct( $content, ?string $type = MimeInterface::TYPE_HTML, ?string $fileName = null, - ?string $disposition = MimeInterface::DISPOSITION_INLINE, + ?string $disposition = null, ?string $encoding = MimeInterface::ENCODING_QUOTED_PRINTABLE, ?string $description = null, ?array $filters = [], diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php index c29ca4d261cc4..6e5763714b88a 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/MessageTest.php @@ -29,7 +29,6 @@ public function testSetBodyHtml() $this->assertEquals('quoted-printable', $part->getEncoding()); $this->assertEquals('utf-8', $part->getCharset()); $this->assertEquals('body', $part->getContent()); - $this->assertEquals('inline', $part->getDisposition()); } public function testSetBodyText() @@ -41,6 +40,5 @@ public function testSetBodyText() $this->assertEquals('quoted-printable', $part->getEncoding()); $this->assertEquals('utf-8', $part->getCharset()); $this->assertEquals('body', $part->getContent()); - $this->assertEquals('inline', $part->getDisposition()); } } From 29335aa1fb7f3418125e7140aede0430de336bf6 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Fri, 22 Nov 2019 12:33:04 -0600 Subject: [PATCH 2180/2437] ENGCOM-6317: Bump coding standard version --- .../Magento/BundleGraphQl/etc/schema.graphqls | 1 + composer.json | 2 +- composer.lock | 36 ++++++++++++------- .../static/framework/Magento/ruleset.xml | 21 ----------- 4 files changed, 25 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index 0eff0e086180e..4544c07d59997 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -3,6 +3,7 @@ type Mutation { addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") + testStatic(invalid_argument): invalid_output } input AddBundleProductsToCartInput { diff --git a/composer.json b/composer.json index 1a73e551168b0..e95bce2314e7f 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", - "magento/magento-coding-standard": "~4.0.0", + "magento/magento-coding-standard": "*", "magento/magento2-functional-testing-framework": "2.5.3", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", diff --git a/composer.lock b/composer.lock index 65e5fd8e5a42c..d6cd5368fd72a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e75fa994f056960e832018efd6af5a40", + "content-hash": "0e6d1fc607befd753c33181c6bcfd1b4", "packages": [ { "name": "braintree/braintree_php", @@ -530,6 +530,7 @@ ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", "homepage": "https://github.com/container-interop/container-interop", + "abandoned": "psr/container", "time": "2017-02-14T19:40:03+00:00" }, { @@ -7178,33 +7179,42 @@ }, { "name": "magento/magento-coding-standard", - "version": "4", + "version": "5", "source": { "type": "git", "url": "https://github.com/magento/magento-coding-standard.git", - "reference": "d24e0230a532e19941ed264f57db38fad5b1008a" + "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/d24e0230a532e19941ed264f57db38fad5b1008a", - "reference": "d24e0230a532e19941ed264f57db38fad5b1008a", + "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/da46c5d57a43c950dfa364edc7f1f0436d5353a5", + "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5", "shasum": "" }, "require": { "php": ">=5.6.0", - "squizlabs/php_codesniffer": "^3.4" + "squizlabs/php_codesniffer": "^3.4", + "webonyx/graphql-php": ">=0.12.6 <1.0" }, "require-dev": { "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "type": "phpcodesniffer-standard", + "autoload": { + "classmap": [ + "PHP_CodeSniffer/Tokenizers/" + ], + "psr-4": { + "Magento2\\": "Magento2/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "OSL-3.0", "AFL-3.0" ], "description": "A set of Magento specific PHP CodeSniffer rules.", - "time": "2019-08-06T15:58:37+00:00" + "time": "2019-11-04T22:08:27+00:00" }, { "name": "magento/magento2-functional-testing-framework", @@ -7914,20 +7924,20 @@ "authors": [ { "name": "Manuel Pichler", + "role": "Project Founder", "email": "github@manuel-pichler.de", - "homepage": "https://github.com/manuelpichler", - "role": "Project Founder" + "homepage": "https://github.com/manuelpichler" }, { "name": "Marc Würth", + "role": "Project Maintainer", "email": "ravage@bluewin.ch", - "homepage": "https://github.com/ravage84", - "role": "Project Maintainer" + "homepage": "https://github.com/ravage84" }, { "name": "Other contributors", - "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", - "role": "Contributors" + "role": "Contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors" } ], "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 89020150035bb..70d1810d1eb2f 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -15,25 +15,4 @@ <exclude-pattern>*/Test/*</exclude-pattern> <exclude-pattern>*Test.php</exclude-pattern> </rule> - <rule ref="Magento2.Files.LineLength.MaxExceeded"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="Squiz.Operators.IncrementDecrementUsage"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="PEAR.ControlStructures.ControlSignature"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="PEAR.Functions.FunctionCallSignature"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> - <rule ref="Magento2.Security.LanguageConstruct.DirectOutput"> - <exclude-pattern>*.phtml</exclude-pattern> - </rule> </ruleset> From 7150c78d37af31583f3e2467aab2272497f0964d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 22 Nov 2019 13:24:23 -0600 Subject: [PATCH 2181/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../Magento/GraphQl/Catalog/ProductSearchTest.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 73baa59b92bf9..6e732d68fe7d3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -123,15 +123,10 @@ public function testFilterLn() usort($expectedFilters, [$this, 'compareFilterNames']); usort($actualFilters, [$this, 'compareFilterNames']); - $errMsg = print_r($expectedFilters, true); - $errMsg .= '---'; - $errMsg .= print_r($actualFilters, true); - $this->assertFilters( ['products' => ['filters' => $actualFilters]], $expectedFilters, - //'Returned filters data set does not match the expected value' - $errMsg + 'Returned filters data set does not match the expected value' ); } From 4f6042f63074e53082e50ca675021506122bedac Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Fri, 22 Nov 2019 13:38:43 -0600 Subject: [PATCH 2182/2437] ENGCOM-6317: Bump coding standard version --- dev/tests/static/phpunit.xml.dist | 3 ++ .../Magento/Test/GraphQl/LiveCodeTest.php | 51 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index d6db407fb5264..f82086a2c8bb2 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -24,6 +24,9 @@ <testsuite name="PHP Coding Standard Verification"> <file>testsuite/Magento/Test/Php/LiveCodeTest.php</file> </testsuite> + <testsuite name="GraphQL Static Code Analysis"> + <file>testsuite/Magento/Test/GraphQl/LiveCodeTest.php</file> + </testsuite> <testsuite name="Code Integrity Tests"> <directory>testsuite/Magento/Test/Integrity</directory> </testsuite> diff --git a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php new file mode 100644 index 0000000000000..32bd666b3d7cd --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Test\GraphQl; + +use Magento\TestFramework\CodingStandard\Tool\CodeSniffer; +use Magento\TestFramework\CodingStandard\Tool\CodeSniffer\Wrapper; +use Magento\Test\Php\LiveCodeTest as PHPCodeTest; +use PHPUnit\Framework\TestCase; + +/** + * Set of tests for static code style + */ +class LiveCodeTest extends TestCase +{ + /** + * @var string + */ + private static $reportDir = ''; + + /** + * Setup basics for all tests + */ + public static function setUpBeforeClass(): void + { + self::$reportDir = BP . '/dev/tests/static/report'; + if (!is_dir(self::$reportDir)) { + mkdir(self::$reportDir, 0770); + } + } + + /** + * Test GraphQL schema files code style using phpcs + */ + public function testCodeStyle(): void + { + $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; + $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); + $result = $codeSniffer->run(PHPCodeTest::getWhitelist(['graphqls'])); + $report = file_exists($reportFile) ? file_get_contents($reportFile) : ''; + $this->assertEquals( + 0, + $result, + "PHP Code Sniffer detected {$result} violation(s): " . PHP_EOL . $report + ); + } +} From 004d61a7055ef5a4abd1ce8a8ac0c557f496a7f9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Fri, 22 Nov 2019 13:41:46 -0600 Subject: [PATCH 2183/2437] MC-29026: GraphQL returns filters with some data if you making request to not existing or empty category --- .../testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 6e732d68fe7d3..9ee3b3baa5fc2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -72,6 +72,7 @@ public function testFilterForNonExistingCategory() */ public function testFilterLn() { + $this->reIndexAndCleanCache(); $query = <<<QUERY { products ( From 491b373ea13c8ba66f9207bc671fab0e613dda82 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Fri, 22 Nov 2019 14:10:27 -0600 Subject: [PATCH 2184/2437] ENGCOM-6317: Bump coding standard version --- .../static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php index 32bd666b3d7cd..2805d7d897097 100644 --- a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php @@ -38,7 +38,7 @@ public static function setUpBeforeClass(): void */ public function testCodeStyle(): void { - $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; + $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); $result = $codeSniffer->run(PHPCodeTest::getWhitelist(['graphqls'])); $report = file_exists($reportFile) ? file_get_contents($reportFile) : ''; From 9b55f0ee31b3b1d68ce7112251d6c12b0f73119f Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Fri, 22 Nov 2019 19:03:34 -0300 Subject: [PATCH 2185/2437] 25703 change margin bottom value to fix issue --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 460a961830b43..7d3e9f5dbf01b 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -113,7 +113,7 @@ .page-main & { .block { - margin-bottom: 0; + margin-bottom: 20px; } } From f61f1d9878a3bf4becb19e90b2e1332cdccec163 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 11:37:49 +0700 Subject: [PATCH 2186/2437] [Tax] Cover Tax CustomerData by Unit Test --- ...CheckoutTotalsJsLayoutDataProviderTest.php | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php diff --git a/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php b/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php new file mode 100644 index 0000000000000..d624a42c1e134 --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Tax\Test\Unit\CustomerData; + +use PHPUnit\Framework\TestCase; +use Magento\Tax\CustomerData\CheckoutTotalsJsLayoutDataProvider; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Tax\Model\Config as TaxConfig; + +/** + * Test class to cover CheckoutTotalsJsLayoutDataProvider + * + * Class \Magento\Tax\Test\Unit\CustomerData\CheckoutTotalsJsLayoutDataProviderTest + */ +class CheckoutTotalsJsLayoutDataProviderTest extends TestCase +{ + /** + * @var CheckoutTotalsJsLayoutDataProvider + */ + private $dataProvider; + + /** + * @var TaxConfig|PHPUnit_Framework_MockObject_MockObject + */ + private $taxConfigMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->taxConfigMock = $this->createMock(TaxConfig::class); + $objectManager = new ObjectManagerHelper($this); + + $this->dataProvider = $objectManager->getObject( + CheckoutTotalsJsLayoutDataProvider::class, + [ + 'taxConfig' => $this->taxConfigMock + ] + ); + } + + /** + * Test getData() with dataset getDataDataProvider + * + * @param int $displayCartSubtotalInclTax + * @param int $displayCartSubtotalExclTax + * @param array $expected + * @return void + * @dataProvider getDataDataProvider + */ + public function testGetData($displayCartSubtotalInclTax, $displayCartSubtotalExclTax, $expected) + { + $this->taxConfigMock->expects($this->any())->method('displayCartSubtotalInclTax') + ->willReturn($displayCartSubtotalInclTax); + $this->taxConfigMock->expects($this->any())->method('displayCartSubtotalExclTax') + ->willReturn($displayCartSubtotalExclTax); + + $this->assertEquals($expected, $this->dataProvider->getData()); + } + + /** + * Dataset for test getData() + * + * @return array + */ + public function getDataDataProvider() + { + return [ + 'Test with settings display cart incl and excl is Yes' => [ + '1' , + '1', + [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 1, + 'display_cart_subtotal_excl_tax' => 1 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ] + ], + 'Test with settings display cart incl and excl is No' => [ + '0' , + '0', + [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 0, + 'display_cart_subtotal_excl_tax' => 0 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ] + ] + ]; + } +} From 02c01c0ff028e2dc79e59829e4aeeefd0b4e64a1 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 21:46:44 +0700 Subject: [PATCH 2187/2437] [Shipping] Cover Helper Data by Unit Test --- .../Shipping/Test/Unit/Helper/DataTest.php | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php b/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..b82e3537d26e2 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Test\Unit\Helper; + +use PHPUnit\Framework\TestCase; +use Magento\Shipping\Helper\Data as HelperData; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Data helper test + * + * Class \Magento\Shipping\Test\Unit\Helper\DataTest + */ +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlDecoderMock; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->urlDecoderMock = $this->createMock(DecoderInterface::class); + $this->contextMock->expects($this->any())->method('getUrlDecoder') + ->willReturn($this->urlDecoderMock); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->helper = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock + ] + ); + } + + /** + * test decodeTrackingHash() with data provider below + * + * @param string $hash + * @param string $urlDecodeResult + * @param array $expected + * @dataProvider decodeTrackingHashDataProvider + */ + public function testDecodeTrackingHash($hash, $urlDecodeResult, $expected) + { + $this->urlDecoderMock->expects($this->any())->method('decode') + ->with($hash) + ->willReturn($urlDecodeResult); + $this->assertEquals($expected, $this->helper->decodeTrackingHash($hash)); + } + + /** + * Dataset to test getData() + * + * @return array + */ + public function decodeTrackingHashDataProvider() + { + return [ + 'Test with hash key is allowed' => [ + strtr(base64_encode('order_id:1:protected_code'), '+/=', '-_,'), + 'order_id:1:protected_code', + [ + 'key' => 'order_id', + 'id' => 1, + 'hash' => 'protected_code' + ] + ], + 'Test with hash key is not allowed' => [ + strtr(base64_encode('invoice_id:1:protected_code'), '+/=', '-_,'), + 'invoice_id:1:protected_code', + [] + ] + ]; + } +} From 9992c0e30b4e368ca978be334c541996c66207cc Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 22:41:29 +0700 Subject: [PATCH 2188/2437] refactor code to pass review --- .../Test/Unit/CustomerData/CaptchaTest.php | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php index b34de7778e176..a791039fe27f9 100644 --- a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php +++ b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php @@ -14,28 +14,19 @@ use Magento\Captcha\Model\DefaultModel; use Magento\Customer\Api\Data\CustomerInterface as CustomerData; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; -/** - * Test class to cover \Magento\Captcha\CustomerData\Captcha - * - * Class \Magento\Captcha\Test\Unit\CustomerData\CaptchaTest - */ -class CaptchaTest extends \PHPUnit\Framework\TestCase +class CaptchaTest extends TestCase { /** * @var CaptchaHelper|\PHPUnit_Framework_MockObject_MockObject */ - private $helper; + private $helperMock; /** * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject */ - private $customerSession; - - /** - * @var CustomerData|\PHPUnit_Framework_MockObject_MockObject - */ - private $customerData; + private $customerSessionMock; /** * @var Captcha @@ -57,8 +48,8 @@ class CaptchaTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->helper = $this->createMock(CaptchaHelper::class); - $this->customerSession = $this->createMock(CustomerSession::class); + $this->helperMock = $this->createMock(CaptchaHelper::class); + $this->customerSessionMock = $this->createMock(CustomerSession::class); $this->formIds = [ 'user_login' ]; @@ -66,9 +57,9 @@ protected function setUp() $this->model = $this->objectManagerHelper->getObject( Captcha::class, [ - 'helper' => $this->helper, + 'helper' => $this->helperMock, 'formIds' => $this->formIds, - 'customerSession' => $this->customerSession + 'customerSession' => $this->customerSessionMock ] ); } @@ -83,15 +74,15 @@ public function testGetSectionDataWhenLoginAndRequireCaptcha() $userLoginModel = $this->createMock(DefaultModel::class); $userLoginModel->expects($this->any())->method('isRequired')->with($emailLogin) ->willReturn(true); - $this->helper->expects($this->any())->method('getCaptcha')->with('user_login')->willReturn($userLoginModel); + $this->helperMock->expects($this->any())->method('getCaptcha')->with('user_login')->willReturn($userLoginModel); - $this->customerSession->expects($this->any())->method('isLoggedIn') + $this->customerSessionMock->expects($this->any())->method('isLoggedIn') ->willReturn(true); - $this->customerData = $this->createMock(CustomerData::class); - $this->customerData->expects($this->any())->method('getEmail')->willReturn($emailLogin); - $this->customerSession->expects($this->any())->method('getCustomerData') - ->willReturn($this->customerData); + $customerDataMock = $this->createMock(CustomerData::class); + $customerDataMock->expects($this->any())->method('getEmail')->willReturn($emailLogin); + $this->customerSessionMock->expects($this->any())->method('getCustomerData') + ->willReturn($customerDataMock); /* Assert to test */ $this->assertEquals( From 34998b6ee11084d047aeca7c137d4fd65818e821 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 23:10:37 +0700 Subject: [PATCH 2189/2437] Resolve Wrong Css at "Minimum Qty Allowed in Shopping Cart" on Google Chrome issue25647 --- .../Block/Adminhtml/Form/Field/Minsaleqty.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php index 6c4f6a0f46a59..ffcb758dcbd66 100644 --- a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php +++ b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php @@ -37,7 +37,7 @@ protected function _getGroupRenderer() '', ['data' => ['is_render_to_js_template' => true]] ); - $this->_groupRenderer->setClass('customer_group_select'); + $this->_groupRenderer->setClass('customer_group_select admin__control-select'); } return $this->_groupRenderer; } @@ -57,7 +57,7 @@ protected function _prepareToRender() 'min_sale_qty', [ 'label' => __('Minimum Qty'), - 'class' => 'required-entry validate-number validate-greater-than-zero' + 'class' => 'required-entry validate-number validate-greater-than-zero admin__control-text' ] ); $this->_addAfter = false; From 20fd591f056285e1d906fd64fe18919f83e5963e Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 23:12:03 +0700 Subject: [PATCH 2190/2437] Resolve Wrong Css at "Minimum Qty Allowed in Shopping Cart" on Google Chrome issue25647 --- .../Block/Adminhtml/Form/Field/CustomergroupTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php index 527828a92dea8..f552f10d50304 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php @@ -22,11 +22,11 @@ protected function setUp() public function testToHtml() { - $this->_block->setClass('customer_group_select'); + $this->_block->setClass('customer_group_select admin__control-select'); $this->_block->setId('123'); $this->_block->setTitle('Customer Group'); $this->_block->setInputName('groups[item_options]'); - $expectedResult = '<select name="groups[item_options]" id="123" class="customer_group_select" ' + $expectedResult = '<select name="groups[item_options]" id="123" class="customer_group_select admin__control-select" ' . 'title="Customer Group" ><option value="32000" >ALL GROUPS</option><option value="0" >NOT LOGGED IN' . '</option><option value="1" >General</option><option value="2" >Wholesale</option><option value="3" >' . 'Retailer</option></select>'; From 0a4393ccce5760c8551a315ac774b160c0b6e19a Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 23 Nov 2019 23:51:12 +0700 Subject: [PATCH 2191/2437] Fix static test --- .../Block/Adminhtml/Form/Field/CustomergroupTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php index f552f10d50304..b7be72e9ff827 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php @@ -26,7 +26,8 @@ public function testToHtml() $this->_block->setId('123'); $this->_block->setTitle('Customer Group'); $this->_block->setInputName('groups[item_options]'); - $expectedResult = '<select name="groups[item_options]" id="123" class="customer_group_select admin__control-select" ' + $expectedResult = '<select name="groups[item_options]" id="123" ' + . 'class="customer_group_select admin__control-select" ' . 'title="Customer Group" ><option value="32000" >ALL GROUPS</option><option value="0" >NOT LOGGED IN' . '</option><option value="1" >General</option><option value="2" >Wholesale</option><option value="3" >' . 'Retailer</option></select>'; From 7b16d76207684bc5422058d323d5e35d36ac58bc Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sun, 24 Nov 2019 01:13:15 +0200 Subject: [PATCH 2192/2437] Cover with js unit test, static test fix --- .../Tinymce3/view/base/web/tinymce3Adapter.js | 4 +- .../tests/lib/mage/tinymce4Adapter.test.js | 41 +++++++++++++++++++ .../Framework/Data/Form/Element/Editor.php | 4 +- 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 dev/tests/js/jasmine/tests/lib/mage/tinymce4Adapter.test.js diff --git a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js index 86602b2017c15..15bc5465e5d04 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js +++ b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js @@ -78,8 +78,8 @@ define([ } tinyMCE3.init(this.getSettings(mode)); - varienGlobalEvents.clearEventHandlers("open_browser_callback"); - varienGlobalEvents.attachEventHandler("open_browser_callback", tinyMceEditors.get(this.id).openFileBrowser); + varienGlobalEvents.clearEventHandlers('open_browser_callback'); + varienGlobalEvents.attachEventHandler('open_browser_callback', tinyMceEditors.get(this.id).openFileBrowser); }, /** diff --git a/dev/tests/js/jasmine/tests/lib/mage/tinymce4Adapter.test.js b/dev/tests/js/jasmine/tests/lib/mage/tinymce4Adapter.test.js new file mode 100644 index 0000000000000..7af1a19e4b4c1 --- /dev/null +++ b/dev/tests/js/jasmine/tests/lib/mage/tinymce4Adapter.test.js @@ -0,0 +1,41 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'wysiwygAdapter', + 'underscore' +], function (wysiwygAdapter, _) { + 'use strict'; + + var obj; + + beforeEach(function () { + + /** + * Dummy constructor to use for instantiation + * @constructor + */ + var Constr = function () {}; + + Constr.prototype = wysiwygAdapter; + + obj = new Constr(); + obj.eventBus = new window.varienEvents(); + obj.initialize(1, { + 'store_id': 0, + 'tinymce4': { + 'content_css': '' + }, + 'files_browser_window_url': 'url' + }); + obj.setup(); + }); + + describe('"openFileBrowser" method', function () { + it('Opens file browser to given instance', function () { + expect(_.size(obj.eventBus.arrEvents['open_browser_callback'])).toBe(1); + }); + }); +}); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php index 5059fb38f29ca..08a5edd09bf35 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Editor.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Editor.php @@ -310,9 +310,7 @@ protected function _getPluginButtonsHtml($visible = true) if (isset($buttonOptions['style'])) { $configStyle = $buttonOptions['style']; } - // phpcs:disable Magento2.Performance.ForeachArrayMerge - $buttonOptions = array_merge($buttonOptions, ['style' => 'display:none;' . $configStyle]); - // phpcs:enable + $buttonOptions['style'] = 'display:none; ' . $configStyle; } $buttonsHtml .= $this->_getButtonHtml($buttonOptions); } From ad17bd912b73c3afc6ad4192bc6cfdd1ad594a6b Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <rosenberger@e-conomix.at> Date: Sun, 24 Nov 2019 11:40:27 +0100 Subject: [PATCH 2193/2437] add the id of the category to the category tree names --- app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index 9a4a9fa768006..929c181bf820c 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -407,6 +407,7 @@ protected function _getNodeJson($node, $level = 0) public function buildNodeName($node) { $result = $this->escapeHtml($node->getName()); + $result .= ' (ID: ' . $node->getId() . ')'; if ($this->_withProductCount) { $result .= ' (' . $node->getProductCount() . ')'; } From f307e2edce1d04e6e4cff145505d64dcc98047be Mon Sep 17 00:00:00 2001 From: Sergiy Zhovnir <s.zhovnir@atwix.com> Date: Sun, 24 Nov 2019 14:18:45 +0200 Subject: [PATCH 2194/2437] #issue-723 Fixed the issue Image preview should be closed when the page is changed --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 4f632df025ac8..bc21e9e21ff3b 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -30,7 +30,8 @@ define([ }, listens: { '${ $.provider }:params.filters': 'hide', - '${ $.provider }:params.search': 'hide' + '${ $.provider }:params.search': 'hide', + '${ $.provider }:params.paging': 'hide' }, exports: { height: '${ $.parentName }.thumbnail_url:previewHeight' From 353eaa884b9cb14fbd74978666a5a29c2881c3bd Mon Sep 17 00:00:00 2001 From: Benjamin Rosenberger <rosenberger@e-conomix.at> Date: Sun, 24 Nov 2019 13:25:43 +0100 Subject: [PATCH 2195/2437] add correct test category output to wait for --- .../AdminFilteringCategoryProductsUsingScopeSelectorTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index 5c434ecabf80d..41b446b474078 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -131,7 +131,7 @@ userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName5"/> - <waitForText userInput="$$createCategory.name$$ (2)" stepKey="seeCorrectProductCount"/> + <waitForText userInput="$$createCategory.name$$ (ID: 6) (2)" stepKey="seeCorrectProductCount"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" @@ -151,7 +151,7 @@ userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName7"/> - <waitForText userInput="$$createCategory.name$$ (2)" stepKey="seeCorrectProductCount2"/> + <waitForText userInput="$$createCategory.name$$ (ID: 6) (2)" stepKey="seeCorrectProductCount2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" From 539e4c620a75aa294050025d02d48f313e6dd2c7 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 24 Nov 2019 23:09:54 +0700 Subject: [PATCH 2196/2437] [Customer] Cover Customer Navigation Block by Unit Test --- .../Unit/Block/Account/NavigationTest.php | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php new file mode 100644 index 0000000000000..ac439d1c7973f --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\Block\Account; + +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\Block\Account\Navigation; +use Magento\Framework\View\Element\Template\Context; +use Magento\Framework\View\LayoutInterface; +use Magento\Wishlist\Block\Link as WishListLink; +use Magento\Customer\Block\Account\Link as CustomerAccountLink; + +class NavigationTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Navigation + */ + private $navigation; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->layoutMock = $this->createMock(LayoutInterface::class); + $this->contextMock->expects($this->any()) + ->method('getLayout') + ->willReturn($this->layoutMock); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->navigation = $this->objectManagerHelper->getObject( + Navigation::class, + [ + 'context' => $this->contextMock + ] + ); + } + + /** + * Test get links with block customer account link and wish list link + * + * @return void + */ + public function testGetLinksWithCustomerAndWishList() + { + $wishListLink = $this->getMockBuilder(WishListLink::class) + ->disableOriginalConstructor() + ->setMethods(['getSortOrder']) + ->getMock(); + + $customerAccountLink = $this->getMockBuilder(CustomerAccountLink::class) + ->disableOriginalConstructor() + ->setMethods(['getSortOrder']) + ->getMock(); + + $wishListLink->expects($this->any()) + ->method('getSortOrder') + ->willReturn(100); + + $customerAccountLink->expects($this->any()) + ->method('getSortOrder') + ->willReturn(20); + + $nameInLayout = 'top.links'; + + $blockChildren = [ + 'wishListLink' => $wishListLink, + 'customerAccountLink' => $customerAccountLink + ]; + + $this->navigation->setNameInLayout($nameInLayout); + $this->layoutMock->expects($this->any()) + ->method('getChildBlocks') + ->with($nameInLayout) + ->willReturn($blockChildren); + + /* Assertion */ + $this->assertEquals( + [ + 0 => $wishListLink, + 1 => $customerAccountLink + ], + $this->navigation->getLinks() + ); + } +} From 49b0c16a24a0bc8b77f2066f4f2bd8a1856f21bc Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 24 Nov 2019 23:10:51 +0700 Subject: [PATCH 2197/2437] [Customer] Cover Customer Navigation Block by Unit Test --- .../Test/Unit/Block/Account/NavigationTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php index ac439d1c7973f..e8c7bd886ab01 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php @@ -63,29 +63,29 @@ protected function setUp() */ public function testGetLinksWithCustomerAndWishList() { - $wishListLink = $this->getMockBuilder(WishListLink::class) + $wishListLinkMock = $this->getMockBuilder(WishListLink::class) ->disableOriginalConstructor() ->setMethods(['getSortOrder']) ->getMock(); - $customerAccountLink = $this->getMockBuilder(CustomerAccountLink::class) + $customerAccountLinkMock = $this->getMockBuilder(CustomerAccountLink::class) ->disableOriginalConstructor() ->setMethods(['getSortOrder']) ->getMock(); - $wishListLink->expects($this->any()) + $wishListLinkMock->expects($this->any()) ->method('getSortOrder') ->willReturn(100); - $customerAccountLink->expects($this->any()) + $customerAccountLinkMock->expects($this->any()) ->method('getSortOrder') ->willReturn(20); $nameInLayout = 'top.links'; $blockChildren = [ - 'wishListLink' => $wishListLink, - 'customerAccountLink' => $customerAccountLink + 'wishListLink' => $wishListLinkMock, + 'customerAccountLink' => $customerAccountLinkMock ]; $this->navigation->setNameInLayout($nameInLayout); @@ -97,8 +97,8 @@ public function testGetLinksWithCustomerAndWishList() /* Assertion */ $this->assertEquals( [ - 0 => $wishListLink, - 1 => $customerAccountLink + 0 => $wishListLinkMock, + 1 => $customerAccountLinkMock ], $this->navigation->getLinks() ); From e5d143d8d968bf5a71429677015be4ae36196a74 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sun, 24 Nov 2019 18:59:53 +0200 Subject: [PATCH 2198/2437] Cover changes with jasmnine test --- .../js/grid/columns/image-preview.test.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js new file mode 100644 index 0000000000000..b5c6e75248bfa --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js @@ -0,0 +1,50 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/* eslint-disable max-nested-callbacks, no-undef */ + +define([ + 'Magento_Ui/js/grid/columns/image-preview', + 'ko', + 'jquery' +], function (Preview, ko, $) { + 'use strict'; + + describe('Ui/js/grid/columns/image-preview', function () { + var record = { + _rowIndex: 1, + rowNumber: 1 + }, + imagePreview; + + beforeEach(function () { + imagePreview = new Preview(); + + /** + * @return {Object} + */ + function getThumbnail() { + return { + previewRowId: ko.observable() + }; + } + + imagePreview.thumbnailComponent = getThumbnail; + + imagePreview.visibleRecord = ko.observable(1); + }); + + describe('show method', function () { + it('show image', function () { + var mockImg = document.createElement('img'), + hide = spyOn(imagePreview, 'hide'); + + spyOn($.fn, 'get').and.returnValue(mockImg); + imagePreview.show(record); + expect(hide).toHaveBeenCalledTimes(1); + }); + + }); + }); +}); From 9348fb818ae587d87e71d485a95592d105117e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com> Date: Sun, 24 Nov 2019 22:22:17 +0100 Subject: [PATCH 2199/2437] Fix #24713 - Symbol of the Belarusian currency BYR is outdated --- .../Dhl/Test/Unit/Model/_files/countries.xml | 2 +- app/code/Magento/Dhl/etc/countries.xml | 2 +- app/code/Magento/Directory/etc/config.xml | 2 +- app/code/Magento/GraphQl/etc/schema.graphqls | 2 +- .../Magento/Framework/Locale/Config.php | 2 +- .../Framework/Locale/Test/Unit/ConfigTest.php | 35 +++++++++++-------- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml index 792465ce45942..d71bb69c99c9b 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml @@ -216,7 +216,7 @@ <name>Botswana</name> </BW> <BY> - <currency>BYR</currency> + <currency>BYN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> <region>AP</region> diff --git a/app/code/Magento/Dhl/etc/countries.xml b/app/code/Magento/Dhl/etc/countries.xml index 792465ce45942..d71bb69c99c9b 100644 --- a/app/code/Magento/Dhl/etc/countries.xml +++ b/app/code/Magento/Dhl/etc/countries.xml @@ -216,7 +216,7 @@ <name>Botswana</name> </BW> <BY> - <currency>BYR</currency> + <currency>BYN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> <region>AP</region> diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index 2ff0b484fe979..32099ff9d8af5 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -9,7 +9,7 @@ <default> <system> <currency> - <installed>AZN,AZM,AFN,ALL,DZD,AOA,ARS,AMD,AWG,AUD,BSD,BHD,BDT,BBD,BYR,BZD,BMD,BTN,BOB,BAM,BWP,BRL,GBP,BND,BGN,BUK,BIF,KHR,CAD,CVE,CZK,KYD,CLP,CNY,COP,KMF,CDF,CRC,HRK,CUP,DKK,DJF,DOP,XCD,EGP,SVC,GQE,ERN,EEK,ETB,EUR,FKP,FJD,GMD,GEK,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,KGS,LAK,LVL,LBP,LSL,LRD,LYD,LTL,MOP,MKD,MGA,MWK,MYR,MVR,LSM,MRO,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,ANG,TRL,TRY,NZD,NIC,NGN,KPW,NOK,OMR,PKR,PAB,PGK,PYG,PEN,PHP,PLN,QAR,RHD,RON,ROL,RUB,RWF,SHP,STD,SAR,RSD,SCR,SLL,SGD,SKK,SBD,SOS,ZAR,KRW,LKR,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TMM,USD,UGX,UAH,AED,UYU,UZS,VUV,VEB,VEF,VND,CHE,CHW,XOF,XPF,WST,YER,ZMK,ZWD</installed> + <installed>AZN,AZM,AFN,ALL,DZD,AOA,ARS,AMD,AWG,AUD,BSD,BHD,BDT,BBD,BYN,BZD,BMD,BTN,BOB,BAM,BWP,BRL,GBP,BND,BGN,BUK,BIF,KHR,CAD,CVE,CZK,KYD,CLP,CNY,COP,KMF,CDF,CRC,HRK,CUP,DKK,DJF,DOP,XCD,EGP,SVC,GQE,ERN,EEK,ETB,EUR,FKP,FJD,GMD,GEK,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,KGS,LAK,LVL,LBP,LSL,LRD,LYD,LTL,MOP,MKD,MGA,MWK,MYR,MVR,LSM,MRO,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,ANG,TRL,TRY,NZD,NIC,NGN,KPW,NOK,OMR,PKR,PAB,PGK,PYG,PEN,PHP,PLN,QAR,RHD,RON,ROL,RUB,RWF,SHP,STD,SAR,RSD,SCR,SLL,SGD,SKK,SBD,SOS,ZAR,KRW,LKR,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TMM,USD,UGX,UAH,AED,UYU,UZS,VUV,VEB,VEF,VND,CHE,CHW,XOF,XPF,WST,YER,ZMK,ZWD</installed> </currency> </system> <currency> diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 559ccf9428929..fccde015c3388 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -113,7 +113,7 @@ enum CurrencyEnum @doc(description: "The list of available currency codes") { BHD BDT BBD - BYR + BYN BZD BMD BTN diff --git a/lib/internal/Magento/Framework/Locale/Config.php b/lib/internal/Magento/Framework/Locale/Config.php index f02ba78ccc3e0..c2d0147c7fdc8 100644 --- a/lib/internal/Magento/Framework/Locale/Config.php +++ b/lib/internal/Magento/Framework/Locale/Config.php @@ -129,7 +129,7 @@ class Config implements \Magento\Framework\Locale\ConfigInterface 'BHD', /*Bahraini Dinar*/ 'BDT', /*Bangladesh Taka*/ 'BBD', /*Barbados Dollar*/ - 'BYR', /*Belarussian Ruble*/ + 'BYN', /*Belarussian Ruble*/ 'BZD', /*Belize Dollar*/ 'BMD', /*Bermudan Dollar*/ 'BTN', /*Bhutan Ngultrum*/ diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/ConfigTest.php index 149f6b5e33b6e..20731ee34558e 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/ConfigTest.php @@ -6,7 +6,11 @@ namespace Magento\Framework\Locale\Test\Unit; -class ConfigTest extends \PHPUnit\Framework\TestCase +use Magento\Framework\Locale\Config; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +class ConfigTest extends TestCase { private static $allAllowedLocales = [ 'af_ZA', 'ar_DZ', 'ar_EG', 'ar_KW', 'ar_MA', 'ar_SA', 'az_Latn_AZ', 'be_BY', 'bg_BG', 'bn_BD', @@ -22,7 +26,7 @@ class ConfigTest extends \PHPUnit\Framework\TestCase private static $allAllowedCurrencies = [ 'AFN', 'ALL', 'AZN', 'DZD', 'AOA', 'ARS', 'AMD', 'AWG', 'AUD', 'BSD', - 'BHD', 'BDT', 'BBD', 'BYR', 'BZD', 'BMD', 'BTN', 'BOB', 'BAM', 'BWP', + 'BHD', 'BDT', 'BBD', 'BYN', 'BZD', 'BMD', 'BTN', 'BOB', 'BAM', 'BWP', 'BRL', 'GBP', 'BND', 'BGN', 'BUK', 'BIF', 'KHR', 'CAD', 'CVE', 'CZK', 'KYD', 'GQE', 'CLP', 'CNY', 'COP', 'KMF', 'CDF', 'CRC', 'HRK', 'CUP', 'DKK', 'DJF', 'DOP', 'XCD', 'EGP', 'SVC', 'ERN', 'EEK', 'ETB', 'EUR', @@ -67,13 +71,14 @@ class ConfigTest extends \PHPUnit\Framework\TestCase 'QED', 'PNP', 'EJN', 'MTO', 'EBY', ]; - /** @var \Magento\Framework\Locale\Config */ + /** + * @var Config + */ private $configObject; public function testGetAllowedLocalesNoDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) - ->getObject(\Magento\Framework\Locale\Config::class); + $this->configObject = (new ObjectManager($this))->getObject(Config::class); $retrievedLocales = $this->configObject->getAllowedLocales(); @@ -88,9 +93,9 @@ public function testGetAllowedLocalesNoDataArray() public function testGetAllowedLocalesGivenDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) + $this->configObject = (new ObjectManager($this)) ->getObject( - \Magento\Framework\Locale\Config::class, + Config::class, [ 'data' => [ 'allowedLocales' => $this::$sampleAdditionalLocales, @@ -114,9 +119,9 @@ public function testGetAllowedLocalesGivenDataArray() public function testGetAllowedLocalesGivenRedundantDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) + $this->configObject = (new ObjectManager($this)) ->getObject( - \Magento\Framework\Locale\Config::class, + Config::class, [ 'data' => [ 'allowedLocales' => $this::$samplePresentLocales, @@ -140,8 +145,8 @@ public function testGetAllowedLocalesGivenRedundantDataArray() public function testGetAllowedCurrenciesNoDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) - ->getObject(\Magento\Framework\Locale\Config::class); + $this->configObject = (new ObjectManager($this)) + ->getObject(Config::class); $retrievedCurrencies = $this->configObject->getAllowedCurrencies(); @@ -156,9 +161,9 @@ public function testGetAllowedCurrenciesNoDataArray() public function testGetAllowedCurrenciesGivenDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) + $this->configObject = (new ObjectManager($this)) ->getObject( - \Magento\Framework\Locale\Config::class, + Config::class, [ 'data' => [ 'allowedCurrencies' => $this::$sampleAdditionalCurrencies, @@ -182,9 +187,9 @@ public function testGetAllowedCurrenciesGivenDataArray() public function testGetAllowedCurrenciesGivenRedundantDataArray() { - $this->configObject = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this)) + $this->configObject = (new ObjectManager($this)) ->getObject( - \Magento\Framework\Locale\Config::class, + Config::class, [ 'data' => [ 'allowedCurrencies' => $this::$samplePresentCurrencies, From 95eea5245b97a8c6f62d435dbfe32c2f3ddcb4c8 Mon Sep 17 00:00:00 2001 From: Alexey Rakitin <alexey.rakitin@babenkocommerce.com> Date: Fri, 22 Nov 2019 13:00:25 +0200 Subject: [PATCH 2200/2437] magento/magento2#23481: Billing/Shipping Address edit form design update from order backend - Messages container added to correctly apply margin between message and form - Form wrapper html tag changed from 'fieldset' to 'div' - Added 'admin__fieldset' class to form for standard admin styles to work properly --- .../Sales/Block/Adminhtml/Order/Address/Form.php | 1 + .../adminhtml/templates/order/address/form.phtml | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php index 1efa149b390ef..06f2edba878ae 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php @@ -108,6 +108,7 @@ protected function _prepareForm() { parent::_prepareForm(); $this->_form->setId('edit_form'); + $this->_form->setClass('admin__fieldset'); $this->_form->setMethod('post'); $this->_form->setAction( $this->getUrl('sales/*/addressSave', ['address_id' => $this->_getAddress()->getId()]) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml index a7f3b3c1cc8f5..b794c418de8d9 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml @@ -4,13 +4,15 @@ * See COPYING.txt for license details. */ ?> -<div class="message message-notice"> - <div class="message-inner"> - <div class="message-content"><?= $block->escapeHtml(__('Changing address information will not recalculate shipping, tax or other order amount.')) ?></div> +<div class="messages"> + <div class="message message-notice"> + <div class="message-inner"> + <div class="message-content"><?= $block->escapeHtml(__('Changing address information will not recalculate shipping, tax or other order amount.')) ?></div> + </div> </div> </div> -<fieldset class="fieldset admin__fieldset-wrapper"> +<div class="fieldset admin__fieldset-wrapper"> <legend class="legend admin__legend"> <span><?= $block->escapeHtml($block->getHeaderText()) ?></span> </legend> @@ -18,4 +20,4 @@ <div class="form-inline" data-mage-init='{"Magento_Sales/order/edit/address/form":{}}'> <?= $block->getForm()->toHtml() ?> </div> -</fieldset> +</div> From feac134662923a95ff8633a3f99a6adf0bf7d6d2 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Mon, 25 Nov 2019 10:47:16 +0200 Subject: [PATCH 2201/2437] MC-23203: Admin: Simple product with all custom attributes --- .../Attribute/Save/AbstractAttributeTest.php | 41 ++++++++++++------- .../Attribute/Save/AttributeDateTest.php | 6 +-- .../Attribute/Save/AttributeDropdownTest.php | 6 +-- .../Save/AttributeMultiSelectTest.php | 6 +-- .../Attribute/Save/AttributePriceTest.php | 6 +-- .../Attribute/Save/AttributeTextAreaTest.php | 4 +- .../Attribute/Save/AttributeTextTest.php | 4 +- .../Attribute/Save/AttributeYesNoTest.php | 4 +- .../Model}/AttributeTextSwatchTest.php | 10 +++-- .../Model}/AttributeVisualSwatchTest.php | 10 +++-- .../Save/AttributeFixedProductTaxTest.php | 8 ++-- 11 files changed, 61 insertions(+), 44 deletions(-) rename dev/tests/integration/testsuite/Magento/{Catalog/Model/Product/Attribute/Save => Swatches/Model}/AttributeTextSwatchTest.php (84%) rename dev/tests/integration/testsuite/Magento/{Catalog/Model/Product/Attribute/Save => Swatches/Model}/AttributeVisualSwatchTest.php (84%) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php index 7a49475fd5107..d2ab4d69dc45c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php @@ -10,7 +10,6 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Eav\Model\Entity\Attribute\Exception; use Magento\Framework\ObjectManagerInterface; @@ -31,7 +30,7 @@ abstract class AbstractAttributeTest extends TestCase /** @var ProductRepositoryInterface */ protected $productRepository; - /** @var Attribute */ + /** @var ProductAttributeInterface */ protected $attribute; /** @@ -44,10 +43,6 @@ protected function setUp() $this->objectManager = Bootstrap::getObjectManager(); $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $this->attributeRepository = $this->objectManager->create(AttributeRepositoryInterface::class); - $this->attribute = $this->attributeRepository->get( - ProductAttributeInterface::ENTITY_TYPE_CODE, - $this->getAttributeCode() - ); } /** @@ -71,7 +66,9 @@ public function testRequiredAttribute(string $productSku): void { $this->expectException(Exception::class); $messageFormat = 'The "%s" attribute value is empty. Set the attribute and try again.'; - $this->expectExceptionMessage((string)__(sprintf($messageFormat, $this->attribute->getDefaultFrontendLabel()))); + $this->expectExceptionMessage( + (string)__(sprintf($messageFormat, $this->getAttribute()->getDefaultFrontendLabel())) + ); $this->prepareAttribute(['is_required' => true]); $this->unsetAttributeValueAndValidate($productSku); } @@ -90,7 +87,7 @@ public function testDefaultValue(string $productSku): void } /** - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * @param string $firstSku * @param string $secondSku * @return void @@ -99,13 +96,32 @@ public function testUniqueAttribute(string $firstSku, string $secondSku): void { $this->expectException(Exception::class); $messageFormat = 'The value of the "%s" attribute isn\'t unique. Set a unique value and try again.'; - $this->expectExceptionMessage((string)__(sprintf($messageFormat, $this->attribute->getDefaultFrontendLabel()))); + $this->expectExceptionMessage( + (string)__(sprintf($messageFormat, $this->getAttribute()->getDefaultFrontendLabel())) + ); $this->prepareAttribute(['is_unique' => 1]); $product = $this->setAttributeValueAndValidate($firstSku, $this->getDefaultAttributeValue()); $this->productRepository->save($product); $this->setAttributeValueAndValidate($secondSku, $this->getDefaultAttributeValue()); } + /** + * Get attribute + * + * @return ProductAttributeInterface + */ + protected function getAttribute(): ProductAttributeInterface + { + if ($this->attribute === null) { + $this->attribute = $this->attributeRepository->get( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $this->getAttributeCode() + ); + } + + return $this->attribute; + } + /** * Set attribute value to product and validate the product * @@ -145,10 +161,7 @@ private function unsetAttributeValueAndValidate(string $productSku): ProductInte */ private function prepareAttribute(array $data): void { - $attribute = $this->attributeRepository->get( - ProductAttributeInterface::ENTITY_TYPE_CODE, - $this->getAttributeCode() - ); + $attribute = $this->getAttribute(); $attribute->addData($data); $this->attributeRepository->save($attribute); } @@ -179,5 +192,5 @@ abstract public function productProvider(): array; * * @return array */ - abstract public function uniqueTestProvider(): array; + abstract public function uniqueAttributeValueProvider(): array; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php index 625418592d4e2..d30f32087c815 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php @@ -36,14 +36,14 @@ protected function getAttributeCode(): string */ protected function getDefaultAttributeValue(): string { - return $this->attribute->getBackend()->formatDate('11/20/19'); + return $this->getAttribute()->getBackend()->formatDate('11/20/19'); } /** * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -67,7 +67,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php index b724279a55f42..c1cdd0bab28aa 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php @@ -27,14 +27,14 @@ protected function getAttributeCode(): string */ protected function getDefaultAttributeValue(): string { - return $this->attribute->getSource()->getOptionId('Option 1'); + return $this->getAttribute()->getSource()->getOptionId('Option 1'); } /** * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +58,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php index 50280060daad1..4ee2b83010bdc 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php @@ -27,7 +27,7 @@ protected function getAttributeCode(): string */ protected function getDefaultAttributeValue(): string { - return $this->attribute->getSource()->getOptionId('Option 1'); + return $this->getAttribute()->getSource()->getOptionId('Option 1'); } /** @@ -43,7 +43,7 @@ public function testDefaultValue(string $productSku): void * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -67,7 +67,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php index a56058ae5007a..5de9d30f71638 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php @@ -20,12 +20,12 @@ class AttributePriceTest extends AbstractAttributeTest * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * @inheritdoc */ public function testUniqueAttribute(string $firstSku, string $secondSku): void { - $this->markTestSkipped('Test is blocked by issue MC-29019'); + $this->markTestSkipped('Test is blocked by issue MC-29018'); parent::testUniqueAttribute($firstSku, $secondSku); } @@ -65,7 +65,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php index 6f9b26e6680b1..61dbf78962c9e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php @@ -34,7 +34,7 @@ protected function getDefaultAttributeValue(): string * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +58,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php index e1fa9f6229f0d..4da09bc1eca5a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php @@ -34,7 +34,7 @@ protected function getDefaultAttributeValue(): string * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +58,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php index 71d2855fdb3bf..7b966791c7b6e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php @@ -34,7 +34,7 @@ protected function getDefaultAttributeValue(): string * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +58,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeTextSwatchTest.php similarity index 84% rename from dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php rename to dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeTextSwatchTest.php index 0650155ba3e47..067ca94e02371 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextSwatchTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeTextSwatchTest.php @@ -5,7 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Catalog\Model\Product\Attribute\Save; +namespace Magento\Swatches\Model; + +use Magento\Catalog\Model\Product\Attribute\Save\AbstractAttributeTest; /** * @magentoDbIsolation enabled @@ -27,14 +29,14 @@ protected function getAttributeCode(): string */ protected function getDefaultAttributeValue(): string { - return $this->attribute->getSource()->getOptionId('Option 2'); + return $this->getAttribute()->getSource()->getOptionId('Option 2'); } /** * @magentoDataFixture Magento/Swatches/_files/product_text_swatch_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +60,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeVisualSwatchTest.php similarity index 84% rename from dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php rename to dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeVisualSwatchTest.php index 5666d4223505d..ab9030f5ed8d9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeVisualSwatchTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Model/AttributeVisualSwatchTest.php @@ -5,7 +5,9 @@ */ declare(strict_types=1); -namespace Magento\Catalog\Model\Product\Attribute\Save; +namespace Magento\Swatches\Model; + +use Magento\Catalog\Model\Product\Attribute\Save\AbstractAttributeTest; /** * @magentoDbIsolation enabled @@ -27,14 +29,14 @@ protected function getAttributeCode(): string */ protected function getDefaultAttributeValue(): string { - return $this->attribute->getSource()->getOptionId('option 2'); + return $this->getAttribute()->getSource()->getOptionId('option 2'); } /** * @magentoDataFixture Magento/Swatches/_files/product_visual_swatch_attribute.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php - * @dataProvider uniqueTestProvider + * @dataProvider uniqueAttributeValueProvider * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod * @inheritdoc */ @@ -58,7 +60,7 @@ public function productProvider(): array /** * @inheritdoc */ - public function uniqueTestProvider(): array + public function uniqueAttributeValueProvider(): array { return [ [ diff --git a/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php b/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php index af474d8f8954f..c83a694b4fc1d 100644 --- a/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Weee/Model/Product/Attribute/Save/AttributeFixedProductTaxTest.php @@ -21,10 +21,10 @@ class AttributeFixedProductTaxTest extends TestCase { /** @var ObjectManagerInterface */ - protected $objectManager; + private $objectManager; /** @var ProductRepositoryInterface */ - protected $productRepository; + private $productRepository; /** @var string */ private $attributeCode; @@ -42,7 +42,7 @@ protected function setUp() } /** - * @dataProvider FPTProvider + * @dataProvider fPTProvider * @param array $data * @param array $expectedData * @return void @@ -58,7 +58,7 @@ public function testSaveProductWithFPTAttribute(array $data, array $expectedData /** * @return array */ - public function FPTProvider(): array + public function fPTProvider(): array { return [ [ From 3f80642207a39a012cd1267586af752868a8f607 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Fri, 22 Nov 2019 17:51:32 +0200 Subject: [PATCH 2202/2437] Tests for: magento/magento2#25600, magento/magento2#25528, magento/magento2#25613, magento/magento2#25606. --- .../Test/StorefrontForthLevelCategoryTest.xml | 52 +++++++ .../Model/Product/Image/ParamsBuilderTest.php | 134 ++++++++++++++++++ .../Section/StorefrontMiniCartSection.xml | 1 + .../Test/NoErrorForMiniCartItemEditTest.xml | 69 +++++++++ .../App/Test/Unit/FrontControllerTest.php | 85 ++++++++++- 5 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml new file mode 100644 index 0000000000000..bb46f8010eaa8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontForthLevelCategoryTest"> + <annotations> + <features value="Catalog"/> + <stories value="Category"/> + <title value="Storefront forth level category test"/> + <description value="When the submenu was created in the third stage follow, the submenu works"/> + <severity value="MAJOR"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="category1"/> + <createData entity="SubCategoryWithParent" stepKey="category2"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="category3"> + <requiredEntity createDataKey="category2"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="category4"> + <requiredEntity createDataKey="category3"/> + </createData> + </before> + <after> + <deleteData createDataKey="category4" stepKey="deleteCategory4"/> + <deleteData createDataKey="category3" stepKey="deleteCategory3"/> + <deleteData createDataKey="category2" stepKey="deleteCategory2"/> + <deleteData createDataKey="category1" stepKey="deleteCategory1"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category1.name$$)}}" + stepKey="hoverCategoryLevelOne"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category2.name$$)}}" + stepKey="hoverCategoryLevelTwo"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category3.name$$)}}" + stepKey="hoverCategoryLevelThree"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category4.name$$)}}" + stepKey="hoverCategoryLevelFour"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php new file mode 100644 index 0000000000000..22e3a88574e03 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Image; + +use Magento\Catalog\Model\Product\Image; +use Magento\Catalog\Model\Product\Image\ParamsBuilder; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Config\View; +use Magento\Framework\View\ConfigInterface; +use Magento\Store\Model\ScopeInterface; + +class ParamsBuilderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var ConfigInterface + */ + private $viewConfig; + + /** + * @var ParamsBuilder + */ + private $model; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->viewConfig = $this->createMock(ConfigInterface::class); + $this->model = $objectManager->getObject( + ParamsBuilder::class, + [ + 'scopeConfig' => $this->scopeConfig, + 'viewConfig' => $this->viewConfig, + ] + ); + } + + /** + * Test watermark location. + */ + public function testWatermarkLocation() + { + $imageArguments = [ + 'type' => 'type', + 'height' => 'image_height', + 'width' => 'image_width', + 'angle' => 'angle', + 'background' => [1, 2, 3] + ]; + $scopeId = 1; + $quality = 100; + $file = 'file'; + $width = 'width'; + $height = 'height'; + $size = "{$width}x{$height}"; + $opacity = 'opacity'; + $position = 'position'; + + $viewMock = $this->createMock(View::class); + $viewMock->expects($this->once()) + ->method('getVarValue') + ->with('Magento_Catalog', 'product_image_white_borders') + ->willReturn(true); + + $this->viewConfig->expects($this->once()) + ->method('getViewConfig') + ->with(['area' => Area::AREA_FRONTEND]) + ->willReturn($viewMock); + + $this->scopeConfig->expects($this->exactly(5))->method('getValue')->withConsecutive( + [ + Image::XML_PATH_JPEG_QUALITY + ], + [ + "design/watermark/{$imageArguments['type']}_image", + ScopeInterface::SCOPE_STORE, + $scopeId, + ], + [ + "design/watermark/{$imageArguments['type']}_size", + ScopeInterface::SCOPE_STORE], + [ + "design/watermark/{$imageArguments['type']}_imageOpacity", + ScopeInterface::SCOPE_STORE, + $scopeId + ], + [ + "design/watermark/{$imageArguments['type']}_position", + ScopeInterface::SCOPE_STORE, + $scopeId + ] + )->willReturnOnConsecutiveCalls( + $quality, + $file, + $size, + $opacity, + $position + ); + + $actual = $this->model->build($imageArguments, $scopeId); + $expected = [ + 'image_type' => $imageArguments['type'], + 'background' => $imageArguments['background'], + 'angle' => $imageArguments['angle'], + 'quality' => $quality, + 'keep_aspect_ratio' => true, + 'keep_frame' => true, + 'keep_transparency' => true, + 'constrain_only' => true, + 'watermark_file' => $file, + 'watermark_image_opacity' => $opacity, + 'watermark_position' => $position, + 'watermark_width' => $width, + 'watermark_height' => $height, + 'image_height' => $imageArguments['height'], + 'image_width' => $imageArguments['width'], + ]; + + $this->assertEquals( + $expected, + $actual + ); + } +} diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index e00906386e46b..80ed4f90c2cd0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -25,6 +25,7 @@ <element name="goToCheckout" type="button" selector="#top-cart-btn-checkout" timeout="30"/> <element name="viewAndEditCart" type="button" selector=".action.viewcart" timeout="30"/> <element name="miniCartItemsText" type="text" selector=".minicart-items"/> + <element name="editMiniCartItem" type="button" selector=".action.edit" timeout="30"/> <element name="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> <element name="deleteMiniCartItemByName" type="button" selector="//ol[@id='mini-cart']//div[contains(., '{{var}}')]//a[contains(@class, 'delete')]" parameterized="true"/> <element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml new file mode 100644 index 0000000000000..2d0c4a05c1dec --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NoErrorForMiniCartItemEditTest"> + <annotations> + <features value="ConfigurableProduct"/> + <title value="No error for minicart item edit test"/> + <description value="Already selected configurable option should be selected when configurable product is edited from minicart"/> + <severity value="MAJOR"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Create Configurable product --> + <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" + dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" + stepKey="clickClearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go To Created Product Page --> + <amOnPage stepKey="goToCreatedProductPage" url="{{_defaultProduct.urlKey}}.html"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Add Product to Cart --> + <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" + stepKey="checkDropDownProductOption"/> + <selectOption userInput="{{colorProductAttribute1.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOption1"/> + <selectOption userInput="{{colorProductAttribute2.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOption2"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" + stepKey="clickDropDownProductOption"/> + <selectOption userInput="{{colorProductAttribute1.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOptionForAddingToCart"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> + <waitForPageLoad stepKey="waitForMiniCart"/> + + <!-- Edit Item in Cart --> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/> + + <!-- Check if Product Configuration is still selected --> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + userInput="{{colorProductAttribute1.name}}" stepKey="seeConfigurationSelected"/> + </test> +</tests> diff --git a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php index e7500e78f7b97..e088bb92c8782 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php @@ -3,10 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\App\Test\Unit; +use Magento\Framework\App\Request\InvalidRequestException; +use Magento\Framework\App\Request\ValidatorInterface; use Magento\Framework\Exception\NotFoundException; +use Magento\Framework\Message\ManagerInterface as MessageManager; +use Psr\Log\LoggerInterface; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class FrontControllerTest extends \PHPUnit\Framework\TestCase { /** @@ -34,6 +42,21 @@ class FrontControllerTest extends \PHPUnit\Framework\TestCase */ protected $response; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|ValidatorInterface + */ + private $requestValidator; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\MessageManager + */ + private $messages; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\LoggerInterface + */ + private $logger; + protected function setUp() { $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -44,7 +67,16 @@ protected function setUp() $this->router = $this->createMock(\Magento\Framework\App\RouterInterface::class); $this->routerList = $this->createMock(\Magento\Framework\App\RouterList::class); $this->response = $this->createMock(\Magento\Framework\App\Response\Http::class); - $this->model = new \Magento\Framework\App\FrontController($this->routerList, $this->response); + $this->requestValidator = $this->createMock(ValidatorInterface::class); + $this->messages = $this->createMock(MessageManager::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->model = new \Magento\Framework\App\FrontController( + $this->routerList, + $this->response, + $this->requestValidator, + $this->messages, + $this->logger + ); } /** @@ -55,7 +87,8 @@ public function testDispatchThrowException() { $validCounter = 0; $callbackValid = function () use (&$validCounter) { - return $validCounter++%10 ? false : true; + $validCounter++; + return $validCounter % 10 ? false : true; }; $this->routerList->expects($this->any())->method('valid')->will($this->returnCallback($callbackValid)); @@ -73,6 +106,54 @@ public function testDispatchThrowException() $this->model->dispatch($this->request); } + /** + * Check adding validation failure message to debug log. + */ + public function testAddingValidationFailureMessageToDebugLog() + { + $exceptionMessage = 'exception_message'; + $exception = new InvalidRequestException($exceptionMessage); + + $this->routerList->expects($this->any()) + ->method('valid') + ->will($this->returnValue(true)); + + $response = $this->createMock(\Magento\Framework\App\Response\Http::class); + $controllerInstance = $this->getMockBuilder(\Magento\Framework\App\Action\Action::class) + ->disableOriginalConstructor() + ->getMock(); + $controllerInstance->expects($this->any()) + ->method('dispatch') + ->with($this->request) + ->will($this->returnValue($response)); + $this->router->expects($this->at(0)) + ->method('match') + ->with($this->request) + ->will($this->returnValue(false)); + $this->router->expects($this->at(1)) + ->method('match') + ->with($this->request) + ->will($this->returnValue($controllerInstance)); + + $this->routerList->expects($this->any()) + ->method('current') + ->will($this->returnValue($this->router)); + + $this->request->expects($this->at(0))->method('isDispatched')->will($this->returnValue(false)); + $this->request->expects($this->at(1))->method('setDispatched')->with(true); + $this->request->expects($this->at(2))->method('isDispatched')->will($this->returnValue(true)); + + $this->requestValidator->expects($this->once()) + ->method('validate')->with($this->request, $controllerInstance)->willThrowException($exception); + $this->logger->expects($this->once())->method('debug')->with( + 'Request validation failed for action "' + . get_class($controllerInstance) . '"', + ["exception" => $exception] + ); + + $this->assertEquals($exceptionMessage, $this->model->dispatch($this->request)); + } + public function testDispatched() { $this->routerList->expects($this->any()) From 4e3e5bd5d387cdb4ba6358c7d9e3a49a2cd8a3d8 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 25 Nov 2019 12:52:23 +0200 Subject: [PATCH 2203/2437] MC-21860: Partial sitemaps have wrong urls in sitemap index --- app/code/Magento/Sitemap/Model/Sitemap.php | 20 ++++-- .../Magento/Sitemap/Model/SitemapTest.php | 69 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index 0d69634ccfa5e..2baa6ff2c71a7 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; use Magento\Framework\UrlInterface; use Magento\Robots\Model\Config\Value; use Magento\Sitemap\Model\ItemProvider\ItemProviderInterface; @@ -191,6 +192,16 @@ class Sitemap extends \Magento\Framework\Model\AbstractModel implements \Magento */ private $lastModMinTsVal; + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var DocumentRoot + */ + private $documentRoot; + /** * Initialize dependencies. * @@ -238,8 +249,9 @@ public function __construct( ) { $this->_escaper = $escaper; $this->_sitemapData = $sitemapData; - $documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); - $this->_directory = $filesystem->getDirectoryWrite($documentRoot->getPath()); + $this->documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); + $this->filesystem = $filesystem; + $this->_directory = $filesystem->getDirectoryWrite($this->documentRoot->getPath()); $this->_categoryFactory = $categoryFactory; $this->_productFactory = $productFactory; $this->_cmsFactory = $cmsFactory; @@ -727,8 +739,8 @@ protected function _getFormattedLastmodDate($date) */ protected function _getDocumentRoot() { - // phpcs:ignore Magento2.Functions.DiscouragedFunction - return realpath($this->_request->getServer('DOCUMENT_ROOT')); + return $this->filesystem->getDirectoryRead($this->documentRoot->getPath()) + ->getAbsolutePath(); } /** diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php new file mode 100644 index 0000000000000..8b9c4229d2b28 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Sitemap\Model; + +use Magento\Framework\App\Config\Value; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\ObjectManagerInterface; +use Magento\Sitemap\Model\Sitemap; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Request; +use Zend\Stdlib\Parameters; +use PHPUnit\Framework\TestCase; + +class SitemapTest extends TestCase +{ + /** + * @var Sitemap + */ + private $model; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->get(Sitemap::class); + } + + /** + * Test get sitemap URL from parent root directory path + * + * @return void + */ + public function testGetSitemapUrlFromParentRootDirectoryPath(): void + { + /** @var Value $configValue */ + $configValue = $this->objectManager->get(Value::class); + $configValue->load('web/unsecure/base_url', 'path'); + $baseUrl = $configValue->getValue() ?: 'http://localhost/'; + + /** @var Filesystem $filesystem */ + $filesystem = $this->objectManager->create(Filesystem::class); + $rootDir = $filesystem->getDirectoryRead(DirectoryList::ROOT) + ->getAbsolutePath(); + $requestPath = dirname($rootDir); + + /** @var Request $request */ + $request = $this->objectManager->get(Request::class); + //imitation run script from parent root directory + $request->setServer(new Parameters(['DOCUMENT_ROOT' => $requestPath])); + + $sitemapUrl = $this->model->getSitemapUrl('/', 'sitemap.xml'); + + $this->assertEquals($baseUrl.'sitemap.xml', $sitemapUrl); + } +} From 071b47b38d08fe8c092625a19dca09bb6059e211 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Mon, 25 Nov 2019 15:33:23 +0200 Subject: [PATCH 2204/2437] MC-21860: Partial sitemaps have wrong urls in sitemap index --- .../Magento/Sitemap/Model/SitemapTest.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php index 8b9c4229d2b28..a051bfc4dad45 100644 --- a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php @@ -8,7 +8,6 @@ namespace Magento\Sitemap\Model; -use Magento\Framework\App\Config\Value; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\ObjectManagerInterface; @@ -18,6 +17,9 @@ use Zend\Stdlib\Parameters; use PHPUnit\Framework\TestCase; +/** + * Test for Sitemap + */ class SitemapTest extends TestCase { /** @@ -30,6 +32,11 @@ class SitemapTest extends TestCase */ private $objectManager; + /** + * @var Filesystem + */ + private $filesystem; + /** * @inheritdoc */ @@ -37,6 +44,7 @@ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->model = $this->objectManager->get(Sitemap::class); + $this->filesystem = $this->objectManager->get(Filesystem::class); } /** @@ -46,14 +54,9 @@ protected function setUp() */ public function testGetSitemapUrlFromParentRootDirectoryPath(): void { - /** @var Value $configValue */ - $configValue = $this->objectManager->get(Value::class); - $configValue->load('web/unsecure/base_url', 'path'); - $baseUrl = $configValue->getValue() ?: 'http://localhost/'; + $baseUrl = 'http://localhost/'; - /** @var Filesystem $filesystem */ - $filesystem = $this->objectManager->create(Filesystem::class); - $rootDir = $filesystem->getDirectoryRead(DirectoryList::ROOT) + $rootDir = $this->filesystem->getDirectoryRead(DirectoryList::ROOT) ->getAbsolutePath(); $requestPath = dirname($rootDir); From 8036f49046955af249dbae8037495a42c78c05a9 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 25 Nov 2019 15:43:33 +0200 Subject: [PATCH 2205/2437] MC-28956: Discounted shipping price is incorrectly displayed on multishipping review order page --- .../Magento/Multishipping/Model/Checkout/Type/Multishipping.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 4c5f666805570..7fa674505461e 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -625,6 +625,7 @@ public function setShippingMethods($methods) $addressId = $address->getId(); if (isset($methods[$addressId])) { $address->setShippingMethod($methods[$addressId]); + $address->setCollectShippingRates(true); } elseif (!$address->getShippingMethod()) { throw new \Magento\Framework\Exception\LocalizedException( __('Set shipping methods for all addresses. Verify the shipping methods and try again.') From 41565f0f7b702136668189eb5a2ecfa1d6b82d69 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Fri, 22 Nov 2019 08:53:45 -0600 Subject: [PATCH 2206/2437] MC-22856: Disable product issue with Cart Item bundle product - Fix issue with checkout button not showing if bundle product was disabled --- .../Model/ResourceModel/Quote/Item/Collection.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 79fd2b1495c49..443e4fda1bd8c 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Quote\Model\ResourceModel\Quote\Item as ResourceQuoteItem; @@ -256,8 +257,17 @@ protected function _assignProducts(): self foreach ($this as $item) { /** @var ProductInterface $product */ $product = $productCollection->getItemById($item->getProductId()); + try { + /** @var QuoteItem $item */ + $parentItem = $item->getParentItem(); + $parentProduct = $parentItem ? $parentItem->getProduct() : null; + } catch (NoSuchEntityException $exception) { + $parentItem = null; + $parentProduct = null; + $this->_logger->error($exception); + } $qtyOptions = []; - if ($product && $this->isValidProduct($product)) { + if ($this->isValidProduct($product) && (!$parentItem || $this->isValidProduct($parentProduct))) { $product->setCustomOptions([]); $optionProductIds = $this->getOptionProductIds($item, $product, $productCollection); foreach ($optionProductIds as $optionProductId) { @@ -327,7 +337,7 @@ private function getOptionProductIds( * @param ProductInterface $product * @return bool */ - private function isValidProduct(ProductInterface $product): bool + private function isValidProduct(?ProductInterface $product): bool { $result = ($product && (int)$product->getStatus() !== ProductStatus::STATUS_DISABLED); From 9fc40860a813211d1a86e70edd8b01c7dd1bfed5 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Fri, 22 Nov 2019 17:55:53 -0600 Subject: [PATCH 2207/2437] MC-29105: Disable product issue with Cart Item bundle product - Add MFTF test --- ...frontCheckoutDisabledBundleProductTest.xml | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml new file mode 100644 index 0000000000000..97eceae962bfb --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckoutDisabledBundleProductTest"> + <annotations> + <features value="Checkout"/> + <stories value="Disabled bundle product is preventing customer to checkout for the first attempt"/> + <title value="Customer should be able to checkout if there is at least one available product in the cart"/> + <description value="Customer should be able to checkout if there is at least one available product in the cart"/> + <severity value="MINOR"/> + <testCaseId value="MC-29105"/> + <group value="checkout"/> + </annotations> + + <before> + <!-- Create category and simple product --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create bundle product --> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleDynamicProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="createBundleDynamicProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createNewBundleLink"> + <requiredEntity createDataKey="createBundleDynamicProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="cacheFlush"/> + </before> + <after> + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete bundle product data --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createBundleDynamicProduct" stepKey="deleteBundleProduct"/> + </after> + <!-- Add simple product to the cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Go to bundle product page --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleDynamicProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!-- Add bundle product to the cart --> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <argument name="productName" value="$$createBundleDynamicProduct.name$$"/> + </actionGroup> + <!-- Login to admin panel --> + <openNewTab stepKey="openNewTab"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <!-- Disabled bundle product from grid --> + <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> + <argument name="product" value="$$createBundleDynamicProduct$$"/> + <argument name="status" value="Disable"/> + </actionGroup> + <closeTab stepKey="closeTab"/> + <!-- Go to cart page--> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="openCartPage"/> + <!-- Assert checkout button exists on the page--> + <seeElement selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="seeCheckoutButton"/> + <!-- Assert no error message is not shown on the page--> + <dontSee userInput="Some of the products are out of stock." stepKey="seeNoItemsInShoppingCart"/> + </test> +</tests> From 46b4e37bac99457c1cecd2c04fe740419a9042fe Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 25 Nov 2019 20:14:28 +0200 Subject: [PATCH 2208/2437] Covering the system dependency by MFTF --- .../AdminTogglesEnabledConfigActionGroup.xml | 17 +++++++++ ...AdminUnchecksUseSystemValueActionGroup.xml | 14 +++++++ ...rtAdminDoesntSeeConfigFieldActionGroup.xml | 17 +++++++++ .../AssertAdminSeesConfigFieldActionGroup.xml | 17 +++++++++ .../Mftf/Page/AdminNewRelicConfigPage.xml | 12 ++++++ .../AdminNewRelicConfigSystemSection.xml | 16 ++++++++ .../AdminChecksSystemConfigDependencyTest.xml | 38 +++++++++++++++++++ 7 files changed, 131 insertions(+) create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml new file mode 100644 index 0000000000000..f9632784f4a92 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminTogglesEnabledConfigActionGroup"> + <arguments> + <argument name="state" type="string"/> + </arguments> + <selectOption selector="{{AdminNewRelicConfigSystemSection.status}}" userInput="{{state}}" stepKey="switchActiveState"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..7d90c1d8dd478 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUnchecksUseSystemValueActionGroup"> + <uncheckOption selector="{{AdminNewRelicConfigSystemSection.useSystemValueForEnabled}}" stepKey="uncheckCheckbox"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml new file mode 100644 index 0000000000000..6e87dbbfbbf4b --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminDoesntSeeConfigFieldActionGroup"> + <arguments> + <argument name="config" type="string"/> + </arguments> + <dontSeeElement selector="{{config}}" stepKey="dontSeeConfigField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml new file mode 100644 index 0000000000000..4df7099e50b28 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminSeesConfigFieldActionGroup"> + <arguments> + <argument name="config" type="string"/> + </arguments> + <seeElement selector="{{config}}" stepKey="seeConfigField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml new file mode 100644 index 0000000000000..fda7d0ef336b1 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewRelicConfigPage" url="admin/system_config/edit/section/newrelicreporting/" area="admin" module="Magento_Config"> + <section name="AdminNewRelicConfigSystemSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml new file mode 100644 index 0000000000000..7c8baccca52fe --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewRelicConfigSystemSection"> + <element name="status" type="select" selector="#row_newrelicreporting_general_enable [data-ui-id='select-groups-general-fields-enable-value']"/> + <element name="useSystemValueForEnabled" type="checkbox" selector="#newrelicreporting_general_enable_inherit"/> + <element name="apiUrl" type="select" selector="input#newrelicreporting_general_api_url"/> + </section> +</sections> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml new file mode 100644 index 0000000000000..12aacbc964d2f --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChecksSystemConfigDependencyTest"> + <annotations> + <features value="NewRelicReporting"/> + <stories value="Admin is able to see the configuration fields only after enabling the feature"/> + <title value="Admin can see the configuration fields only after enabling the feature"/> + <description value="The system configs should be available only after enabling the New Relic feature."/> + <severity value="CRITICAL"/> + <group value="NewRelicReporting"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <amOnPage url="{{AdminNewRelicConfigPage.url}}" stepKey="navigateToNewRelicConfigurationPage"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <actionGroup ref="AssertAdminDoesntSeeConfigFieldActionGroup" stepKey="checkIfApiUrlIsNotVisible"> + <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> + </actionGroup> + <actionGroup ref="AdminUnchecksUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"/> + <actionGroup ref="AdminTogglesEnabledConfigActionGroup" stepKey="enableNewRelic"> + <argument name="state" value="Yes"/> + </actionGroup> + <actionGroup ref="AssertAdminSeesConfigFieldActionGroup" stepKey="checkIfApiUrlIsVisible"> + <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> + </actionGroup> + </test> +</tests> From 2c20f71758c8dcf11d85c81de935ef59c6237b01 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Mon, 25 Nov 2019 13:27:40 -0600 Subject: [PATCH 2209/2437] ENGCOM-6317: Bump coding standard version --- .../Magento/BundleGraphQl/etc/schema.graphqls | 3 +- .../Tool/CodeSniffer/GraphQlWrapper.php | 28 +++++++++++++++++++ dev/tests/static/phpunit.xml.dist | 6 ++-- .../Magento/Test/GraphQl/LiveCodeTest.php | 8 ++++-- 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/CodeSniffer/GraphQlWrapper.php diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index 4544c07d59997..46906e958bde5 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -3,7 +3,6 @@ type Mutation { addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") - testStatic(invalid_argument): invalid_output } input AddBundleProductsToCartInput { @@ -87,3 +86,5 @@ enum ShipBundleItemsEnum @doc(description: "This enumeration defines whether bun TOGETHER SEPARATELY } + +type invalidCamelCaseType {} diff --git a/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/CodeSniffer/GraphQlWrapper.php b/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/CodeSniffer/GraphQlWrapper.php new file mode 100644 index 0000000000000..311b2b8f72164 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/CodeSniffer/GraphQlWrapper.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\CodingStandard\Tool\CodeSniffer; + +/** + * Add GraphQl files extension to config. + */ +class GraphQlWrapper extends Wrapper +{ + const FILE_EXTENSION = 'graphqls'; + + private const TOKENIZER = 'GraphQL'; + + /** + * @inheritDoc + */ + public function init() + { + parent::init(); + + $this->config->extensions += [self::FILE_EXTENSION => self::TOKENIZER]; + } +} diff --git a/dev/tests/static/phpunit.xml.dist b/dev/tests/static/phpunit.xml.dist index f82086a2c8bb2..c85b2d2a0bdfb 100644 --- a/dev/tests/static/phpunit.xml.dist +++ b/dev/tests/static/phpunit.xml.dist @@ -21,12 +21,12 @@ <testsuite name="HTML Static Code Analysis"> <file>testsuite/Magento/Test/Html/LiveCodeTest.php</file> </testsuite> - <testsuite name="PHP Coding Standard Verification"> - <file>testsuite/Magento/Test/Php/LiveCodeTest.php</file> - </testsuite> <testsuite name="GraphQL Static Code Analysis"> <file>testsuite/Magento/Test/GraphQl/LiveCodeTest.php</file> </testsuite> + <testsuite name="PHP Coding Standard Verification"> + <file>testsuite/Magento/Test/Php/LiveCodeTest.php</file> + </testsuite> <testsuite name="Code Integrity Tests"> <directory>testsuite/Magento/Test/Integrity</directory> </testsuite> diff --git a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php index 2805d7d897097..db429dd6f3ceb 100644 --- a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php @@ -38,9 +38,11 @@ public static function setUpBeforeClass(): void */ public function testCodeStyle(): void { - $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; - $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); - $result = $codeSniffer->run(PHPCodeTest::getWhitelist(['graphqls'])); + $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; + $codeSniffer = new CodeSniffer('Magento', + $reportFile, new Wrapper()); + $codeSniffer->setExtensions([CodeSniffer\GraphQlWrapper::FILE_EXTENSION]); + $result = $codeSniffer->run(PHPCodeTest::getWhitelist([CodeSniffer\GraphQlWrapper::FILE_EXTENSION])); $report = file_exists($reportFile) ? file_get_contents($reportFile) : ''; $this->assertEquals( 0, From 91a59726aa966e99c5ec52b7cb0dc71ec3c83b32 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Mon, 25 Nov 2019 14:38:52 -0600 Subject: [PATCH 2210/2437] ENGCOM-6317: Bump coding standard version --- app/code/Magento/BundleGraphQl/etc/schema.graphqls | 2 +- .../static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index 46906e958bde5..914e405ba2695 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -87,4 +87,4 @@ enum ShipBundleItemsEnum @doc(description: "This enumeration defines whether bun SEPARATELY } -type invalidCamelCaseType {} +type invalidCamelCaseType {} #test static check diff --git a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php index db429dd6f3ceb..97d134517a5a6 100644 --- a/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/GraphQl/LiveCodeTest.php @@ -39,8 +39,7 @@ public static function setUpBeforeClass(): void public function testCodeStyle(): void { $reportFile = self::$reportDir . '/graphql_phpcs_report.txt'; - $codeSniffer = new CodeSniffer('Magento', - $reportFile, new Wrapper()); + $codeSniffer = new CodeSniffer('Magento', $reportFile, new Wrapper()); $codeSniffer->setExtensions([CodeSniffer\GraphQlWrapper::FILE_EXTENSION]); $result = $codeSniffer->run(PHPCodeTest::getWhitelist([CodeSniffer\GraphQlWrapper::FILE_EXTENSION])); $report = file_exists($reportFile) ? file_get_contents($reportFile) : ''; From 66fddf2e77580582c5c3f0ce957b73dfa3fd6f50 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 26 Nov 2019 07:02:39 +0200 Subject: [PATCH 2211/2437] Adding expanding config tab step --- .../AdminExpandNewRelicConfigTabActionGroup.xml | 17 +++++++++++++++++ .../AdminNewRelicConfigSystemSection.xml | 1 + .../AdminChecksSystemConfigDependencyTest.xml | 7 +++++-- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml new file mode 100644 index 0000000000000..4dc3d2ea8ea34 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandNewRelicConfigTabActionGroup"> + <arguments> + <argument name="tabName" type="string"/> + </arguments> + <conditionalClick selector="{{AdminNewRelicConfigSystemSection.tab(tabName)}}" dependentSelector="{{AdminNewRelicConfigSystemSection.status}}" visible="false" stepKey="expandTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml index 7c8baccca52fe..79625273b988e 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewRelicConfigSystemSection"> + <element name="tab" type="button" selector="#newrelicreporting_{{tab}}-head" parameterized="true"/> <element name="status" type="select" selector="#row_newrelicreporting_general_enable [data-ui-id='select-groups-general-fields-enable-value']"/> <element name="useSystemValueForEnabled" type="checkbox" selector="#newrelicreporting_general_enable_inherit"/> <element name="apiUrl" type="select" selector="input#newrelicreporting_general_api_url"/> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml index 12aacbc964d2f..eabacffe9b181 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml @@ -19,12 +19,15 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <amOnPage url="{{AdminNewRelicConfigPage.url}}" stepKey="navigateToNewRelicConfigurationPage"/> + <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> + <actionGroup ref="AdminExpandNewRelicConfigTabActionGroup" stepKey="expandGeneralTab"> + <argument name="tabName" value="general"/> + </actionGroup> </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> - <actionGroup ref="AssertAdminDoesntSeeConfigFieldActionGroup" stepKey="checkIfApiUrlIsNotVisible"> + <actionGroup ref="AssertAdminDontSeeConfigFieldActionGroup" stepKey="checkIfApiUrlIsNotVisible"> <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> </actionGroup> <actionGroup ref="AdminUnchecksUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"/> From 1298766deffe2c992b3f29aeb85454cc7bc6e44c Mon Sep 17 00:00:00 2001 From: Adarsh Manickam <amanickam@ztech.io> Date: Tue, 19 Nov 2019 20:01:53 +0530 Subject: [PATCH 2212/2437] Refactored action click --- .../base/web/js/grid/columns/image-preview.js | 51 +++++++++---------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 7cbbfdab28ba1..98bb5770dca5d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -40,7 +40,7 @@ define([ */ initialize: function () { this._super(); - this.setNavigationListener(); + $(document).on('keydown', this.handleKeyDown.bind(this)); return this; }, @@ -67,8 +67,13 @@ define([ * @param {Object} record */ next: function (record) { - var recordToShow = this.getRecord(record._rowIndex + 1); + var recordToShow; + if (record._rowIndex + 1 === this.masonry().rows().length) { + return; + } + + recordToShow = this.getRecord(record._rowIndex + 1); recordToShow.rowNumber = record.lastInRow ? record.rowNumber + 1 : record.rowNumber; this.show(recordToShow); }, @@ -79,6 +84,9 @@ define([ * @param {Object} record */ prev: function (record) { + if (record._rowIndex === 0) { + return; + } var recordToShow = this.getRecord(record._rowIndex - 1); recordToShow.rowNumber = record.firstInRow ? record.rowNumber - 1 : record.rowNumber; @@ -190,33 +198,20 @@ define([ }, /** - * Set image preview keyboard navigation listener + * Handle keyboard navigation for image preview + * + * @param {Object} e */ - setNavigationListener: function () { - var imageIndex, endIndex, key, - startIndex = 0, - imageColumnSelector = '.masonry-image-column', - adobeModalSelector = '.adobe-stock-modal', - imageGridSelector = '.masonry-image-grid'; - - $(document).on('keydown', function(e) { - key = keyCodes[e.keyCode]; - endIndex = $(imageGridSelector)[0].children.length - 1; - - if($(this.previewImageSelector).length > 0) { - imageIndex = $(this.previewImageSelector) - .parents(imageColumnSelector) - .data('repeatIndex'); + handleKeyDown: function (e) { + var key = keyCodes[e.keyCode]; + + if (this.visibleRecord() !== null) { + if (key === 'pageLeftKey') { + this.prev(this.displayedRecord()); + } else if (key === 'pageRightKey') { + this.next(this.displayedRecord()); } - - if($(adobeModalSelector).hasClass('_show')) { - if(key === 'pageLeftKey' && imageIndex !== startIndex) { - $(this.previewImageSelector + ' .action-previous').click(); - } else if (key === 'pageRightKey' && imageIndex !== endIndex) { - $(this.previewImageSelector + ' .action-next').click(); - } - } - }); - }, + } + } }); }); From a70f50cf096765815c766465aca000e08c102825 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Mon, 25 Nov 2019 22:28:12 -0800 Subject: [PATCH 2213/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Model/Indexer/IndexBuilder.php | 19 ++----------------- .../Model/Rule/ConfigurableProductHandler.php | 9 ++++++--- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index ae76a20cdf762..0ca9c544b74c9 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -292,23 +292,8 @@ protected function doReindexByIds($ids) /** @var Rule[] $activeRules */ $activeRules = $this->getActiveRules()->getItems(); - foreach ($activeRules as $activeRule) { - $rule = clone $activeRule; - $rule->setProductsFilter($ids); - $matchedProductIds = $rule->getMatchingProductIds(); - if (empty($matchedProductIds)) { - continue; - } - - $matchedProductIds = array_intersect_key($matchedProductIds, array_flip($ids)); - foreach ($matchedProductIds as $matchedProductId => $validationByWebsite) { - $websiteIds = array_keys(array_filter($validationByWebsite)); - if (empty($websiteIds)) { - continue; - } - - $this->assignProductToRule($rule, $matchedProductId, $websiteIds); - } + foreach ($activeRules as $rule) { + $this->reindexRuleProduct->execute($rule, $this->batchCount); } foreach ($ids as $productId) { diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php index e846c10bf49ef..d57ce702f70b8 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php +++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php @@ -1,16 +1,14 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\CatalogRuleConfigurable\Plugin\CatalogRule\Model\Rule; -use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\CatalogRuleConfigurable\Plugin\CatalogRule\Model\ConfigurableProductsProvider; /** - * Add configurable sub products to catalog rule indexer on full reindex + * Add configurable sub products to catalog rule indexer on reindex */ class ConfigurableProductHandler { @@ -42,9 +40,12 @@ public function __construct( } /** + * Add configurable products during setting product ids for filtering + * * @param \Magento\CatalogRule\Model\Rule $rule * @param int|array $productIds * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeSetProductsFilter(\Magento\CatalogRule\Model\Rule $rule, $productIds) { @@ -62,6 +63,8 @@ public function beforeSetProductsFilter(\Magento\CatalogRule\Model\Rule $rule, $ } /** + * Add configurable products for matched products + * * @param \Magento\CatalogRule\Model\Rule $rule * @param array $productIds * @return array From 61303b69a2ea96d20db1f5b728af7ca5e714510d Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 26 Nov 2019 10:56:53 +0200 Subject: [PATCH 2214/2437] MC-21860: Partial sitemaps have wrong urls in sitemap index --- .../testsuite/Magento/Sitemap/Model/SitemapTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php index a051bfc4dad45..73863e0915c66 100644 --- a/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php +++ b/dev/tests/integration/testsuite/Magento/Sitemap/Model/SitemapTest.php @@ -18,7 +18,7 @@ use PHPUnit\Framework\TestCase; /** - * Test for Sitemap + * Tests \Magento\Sitemap\Model\Sitemap functionality. */ class SitemapTest extends TestCase { @@ -54,8 +54,6 @@ protected function setUp() */ public function testGetSitemapUrlFromParentRootDirectoryPath(): void { - $baseUrl = 'http://localhost/'; - $rootDir = $this->filesystem->getDirectoryRead(DirectoryList::ROOT) ->getAbsolutePath(); $requestPath = dirname($rootDir); @@ -67,6 +65,6 @@ public function testGetSitemapUrlFromParentRootDirectoryPath(): void $sitemapUrl = $this->model->getSitemapUrl('/', 'sitemap.xml'); - $this->assertEquals($baseUrl.'sitemap.xml', $sitemapUrl); + $this->assertEquals('http://localhost/sitemap.xml', $sitemapUrl); } } From b7d1359371841ddf9fd8f2494567f56ccb5da027 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com> Date: Tue, 26 Nov 2019 11:30:29 +0200 Subject: [PATCH 2215/2437] magento/magento2#5328: Static test fix. --- .../Magento/Sales/Model/ResourceModel/Status/Collection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php index f9dc4a7d83ae2..83346d4528c22 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php @@ -6,8 +6,7 @@ namespace Magento\Sales\Model\ResourceModel\Status; /** - * Class Collection - * Oder statuses grid collection + * Order statuses grid collection. */ class Collection extends \Magento\Sales\Model\ResourceModel\Order\Status\Collection { From 41c95a21ff19503b3870748f0c5a8bbb5b6d3bdb Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <51681487+engcom-Foxtrot@users.noreply.github.com> Date: Tue, 26 Nov 2019 11:34:50 +0200 Subject: [PATCH 2216/2437] magento/magento2#25611: Static test fix. --- .../Magento/Ui/view/base/web/js/grid/columns/image-preview.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 98bb5770dca5d..fec9fd969d61e 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -84,10 +84,12 @@ define([ * @param {Object} record */ prev: function (record) { + var recordToShow; + if (record._rowIndex === 0) { return; } - var recordToShow = this.getRecord(record._rowIndex - 1); + recordToShow = this.getRecord(record._rowIndex - 1); recordToShow.rowNumber = record.firstInRow ? record.rowNumber - 1 : record.rowNumber; this.show(recordToShow); From e4b09b462d15786cfb69e530e54ddd1b7fc2ccde Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 26 Nov 2019 16:53:58 +0700 Subject: [PATCH 2217/2437] Resolve wrong Cursor icon when hover in "Images And Videos" thumbnail, small ... options --- .../backend/web/css/source/components/_media-gallery.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less index 9a5f35e4ede90..2490ac4cee625 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less @@ -284,6 +284,7 @@ line-height: 1; margin: 0 .4rem .4rem 0; padding: .6rem; + cursor: pointer; } } From a05fa051b3bf2c03ba29addc98d8079b7928e42c Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 26 Nov 2019 17:22:09 +0700 Subject: [PATCH 2218/2437] Fix static test --- .../backend/web/css/source/components/_media-gallery.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less index 2490ac4cee625..6de25c424656d 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less @@ -280,11 +280,11 @@ .item-role { background: @color-gray89; color: @color-brownie; + cursor: pointer; font-size: @font-size__s; line-height: 1; margin: 0 .4rem .4rem 0; padding: .6rem; - cursor: pointer; } } From 5a8e81589c82b478f95454151e35977a73b260fb Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Tue, 26 Nov 2019 07:52:27 -0300 Subject: [PATCH 2219/2437] change by request --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 7d3e9f5dbf01b..6c0c7a4fb66e1 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -113,7 +113,7 @@ .page-main & { .block { - margin-bottom: 20px; + margin-bottom: @indent__base; } } From 8bf48fb0cfa69ac1b377d90503bbb2b20e5247ee Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <p.bystritsky@yandex.ru> Date: Tue, 26 Nov 2019 14:10:30 +0200 Subject: [PATCH 2220/2437] Tests for: magento/magento2#25102, magento/magento2#25251, magento/magento2#25584. --- .../Test/Mftf/Page/AdminThreeDSecurePage.xml | 14 ++++++++ .../Section/AdminCardinalCommerceSection.xml | 16 +++++++++ ...dminCardinalCommerceSettingsHiddenTest.xml | 33 +++++++++++++++++++ .../Test/Mftf/Page/AdminCurrencySetupPage.xml | 14 ++++++++ .../AdminScheduledImportSettingsSection.xml | 16 +++++++++ ...AdminScheduledImportSettingsHiddenTest.xml | 33 +++++++++++++++++++ .../Mftf/Page/AdminFraudProtectionPage.xml | 14 ++++++++ .../AdminSignifydConfigurationSection.xml | 16 +++++++++ ...gnifydConfigDependentOnActiveFieldTest.xml | 33 +++++++++++++++++++ 9 files changed, 189 insertions(+) create mode 100644 app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml create mode 100644 app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml create mode 100644 app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml create mode 100644 app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml create mode 100644 app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml create mode 100644 app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml create mode 100644 app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml new file mode 100644 index 0000000000000..dae6869dbfe79 --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminThreeDSecurePage" url="admin/system_config/edit/section/three_d_secure/" area="admin" module="Magento_CardinalCommerce"> + <section name="AdminCardinalCommerceSection"/> + </page> +</pages> diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml new file mode 100644 index 0000000000000..1016fbaefb0ab --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCardinalCommerceSection"> + <element name="head" type="button" selector="#three_d_secure_cardinal_config-link"/> + <element name="enabled" type="input" selector="#three_d_secure_cardinal_config_enabled_authorize"/> + <element name="environment" type="input" selector="#three_d_secure_cardinal_config_environment"/> + </section> +</sections> diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml new file mode 100644 index 0000000000000..a41b96f0db6e4 --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCardinalCommerceSettingsHiddenTest"> + <annotations> + <features value="CardinalCommerce"/> + <title value="CardinalCommerce settings hidden" /> + <description value="CardinalCommerce config shouldn't be visible if the 3D secure is disabled for Authorize.Net."/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 1" stepKey="enableCardinalCommerce"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 0" stepKey="disableCardinalCommerce"/> + </after> + + <amOnPage url="{{AdminThreeDSecurePage.url}}" stepKey="openCurrencyOptionsPage" /> + <conditionalClick dependentSelector="{{AdminCardinalCommerceSection.enabled}}" visible="false" selector="{{AdminCardinalCommerceSection.head}}" stepKey="openCollapsibleBlock"/> + <see selector="{{AdminCardinalCommerceSection.environment}}" userInput="Production" stepKey="seeEnvironmentProduction"/> + <selectOption selector="{{AdminCardinalCommerceSection.enabled}}" userInput="0" stepKey="disableCardinalCommerceOption"/> + <dontSeeElement selector="{{AdminCardinalCommerceSection.environment}}" stepKey="dontSeeEnvironmentProduction"/> + </test> +</tests> diff --git a/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml b/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml new file mode 100644 index 0000000000000..03c2b0f856d19 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCurrencySetupPage" url="admin/system_config/edit/section/currency/" area="admin" module="Magento_Directory"> + <section name="AdminScheduledImportSettingsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml b/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml new file mode 100644 index 0000000000000..4c4b0d6c9541e --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminScheduledImportSettingsSection"> + <element name="head" type="button" selector="#currency_import-head"/> + <element name="enabled" type="input" selector="#currency_import_enabled"/> + <element name="service" type="input" selector="#currency_import_service"/> + </section> +</sections> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml new file mode 100644 index 0000000000000..0320b6f422cd6 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminScheduledImportSettingsHiddenTest"> + <annotations> + <features value="Directory"/> + <title value="Scheduled import settings hidden" /> + <description value="Scheduled Import Settings' should hide fields when 'Enabled' is 'No'"/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set currency/import/enabled 1" stepKey="enableCurrencyImport"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set currency/import/enabled 0" stepKey="disableCurrencyImport"/> + </after> + + <amOnPage url="{{AdminCurrencySetupPage.url}}" stepKey="openCurrencyOptionsPage" /> + <conditionalClick dependentSelector="{{AdminScheduledImportSettingsSection.enabled}}" visible="false" selector="{{AdminScheduledImportSettingsSection.head}}" stepKey="openCollapsibleBlock"/> + <see selector="{{AdminScheduledImportSettingsSection.service}}" userInput="Fixer.io" stepKey="seeServiceFixerIo"/> + <selectOption selector="{{AdminScheduledImportSettingsSection.enabled}}" userInput="0" stepKey="disableCurrencyImportOption"/> + <dontSeeElement selector="{{AdminScheduledImportSettingsSection.service}}" stepKey="dontSeeServiceFixerIo"/> + </test> +</tests> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml new file mode 100644 index 0000000000000..07b58b8594843 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminFraudProtectionPage" url="admin/system_config/edit/section/fraud_protection/" area="admin" module="Magento_Signifyd"> + <section name="AdminSignifydConfigurationSection"/> + </page> +</pages> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml new file mode 100644 index 0000000000000..618e9d520dd87 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminSignifydConfigurationSection"> + <element name="head" type="button" selector="#fraud_protection_signifyd_config-head"/> + <element name="enabled" type="input" selector="#fraud_protection_signifyd_config_active"/> + <element name="url" type="text" selector="#fraud_protection_signifyd_config_api_url"/> + </section> +</sections> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml new file mode 100644 index 0000000000000..dcae0c4091ba6 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSignifydConfigDependentOnActiveFieldTest"> + <annotations> + <features value="Signifyd"/> + <title value="Signifyd config dependent on active field" /> + <description value="Signifyd system configs dependent by Enable this Solution field."/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set fraud_protection/signifyd/active 1" stepKey="enableSignifyd"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set fraud_protection/signifyd/active 0" stepKey="disableSignifyd"/> + </after> + + <amOnPage url="{{AdminFraudProtectionPage.url}}" stepKey="openFraudProtectionPagePage" /> + <conditionalClick dependentSelector="{{AdminSignifydConfigurationSection.enabled}}" visible="false" selector="{{AdminSignifydConfigurationSection.head}}" stepKey="openCollapsibleBlock"/> + <seeInField selector="{{AdminSignifydConfigurationSection.url}}" userInput="https://api.signifyd.com/v2/" stepKey="seeApiUrlField"/> + <selectOption selector="{{AdminSignifydConfigurationSection.enabled}}" userInput="0" stepKey="disableSignifydOption"/> + <dontSeeElement selector="{{AdminSignifydConfigurationSection.url}}" stepKey="dontSeeApiUrlField"/> + </test> +</tests> From 1ab336b33d26713f660ae4aade5aae35332a13f5 Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Tue, 26 Nov 2019 10:52:50 -0300 Subject: [PATCH 2221/2437] change by request to improve --- .../luma/Magento_Checkout/web/css/source/module/_cart.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 6c0c7a4fb66e1..ac0ab760ead62 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -113,7 +113,7 @@ .page-main & { .block { - margin-bottom: @indent__base; + margin-bottom: 0; } } @@ -563,6 +563,9 @@ .widget { float: left; + &.block { + margin-bottom: @indent__base; + } } } From e95fee449a70264c36e0e482bd3f56c3bf7cafb4 Mon Sep 17 00:00:00 2001 From: Nagamaiah K <54108580+Nagamaiah007@users.noreply.github.com> Date: Tue, 26 Nov 2019 20:04:15 +0530 Subject: [PATCH 2222/2437] Update system.xml --- app/code/Magento/Analytics/etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index c7da840b7e665..ad542cd30758d 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -15,7 +15,7 @@ <label>Advanced Reporting</label> <comment><![CDATA[This service provides a dynamic suite of reports with rich insights about your business. Your reports can be accessed securely on a personalized dashboard outside of the admin panel by clicking on the - "Go to Advanced Reporting" link. </br> For more information, see our <a href="https://magento.com/legal/terms/cloud-terms"> + "Go to Advanced Reporting" link. </br> For more information, see our <a target="_blank" href="https://magento.com/legal/terms/cloud-terms"> terms and conditions</a>.]]></comment> <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Advanced Reporting Service</label> From c4a3e5158af088b9735ff8c2abdd881960b344ae Mon Sep 17 00:00:00 2001 From: Fabricio Sobral <fabricio.sobral@webjump.com.br> Date: Tue, 26 Nov 2019 13:52:15 -0300 Subject: [PATCH 2223/2437] add changes in blank theme --- .../blank/Magento_Checkout/web/css/source/module/_cart.less | 4 ++++ .../luma/Magento_Checkout/web/css/source/module/_cart.less | 1 + 2 files changed, 5 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less index d3d15019f0e87..3142d5de64be1 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less @@ -346,6 +346,10 @@ .widget { float: left; + + &.block { + margin-bottom: @indent__base; + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index ac0ab760ead62..cbf1d185a5a08 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -563,6 +563,7 @@ .widget { float: left; + &.block { margin-bottom: @indent__base; } From 6991ebae4d56490f6cd7ba06fe74dfd1cc08ae81 Mon Sep 17 00:00:00 2001 From: Dmytro Yushkin <dyushkin@adobe.com> Date: Mon, 25 Nov 2019 16:42:05 -0600 Subject: [PATCH 2224/2437] MC-18459: Remove Special Price From and To Date fields from the Mass Update page --- .../Helper/Product/Edit/Action/Attribute.php | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php b/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php index 2c1bacdb99e12..09d53427a3043 100644 --- a/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php +++ b/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php @@ -3,15 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Adminhtml catalog product action attribute update helper - */ namespace Magento\Catalog\Helper\Product\Edit\Action; /** - * Class Attribute + * Adminhtml catalog product action attribute update helper. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Attribute extends \Magento\Backend\Helper\Data { @@ -32,7 +32,7 @@ class Attribute extends \Magento\Backend\Helper\Data /** * Excluded from batch update attribute codes * - * @var string[] + * @var array */ protected $_excludedAttributes = ['url_key']; @@ -92,6 +92,7 @@ public function __construct( /** * Return product collection with selected product filter + * * Product collection didn't load * * @return \Magento\Catalog\Model\ResourceModel\Product\Collection @@ -171,8 +172,8 @@ public function getAttributes() $this->getProductsSetIds() ); - if ($this->_excludedAttributes) { - $this->_attributes->addFieldToFilter('attribute_code', ['nin' => $this->_excludedAttributes]); + if ($excludedAttributes = $this->getExcludedAttributes()) { + $this->_attributes->addFieldToFilter('attribute_code', ['nin' => $excludedAttributes]); } // check product type apply to limitation and remove attributes that impossible to change in mass-update @@ -193,11 +194,24 @@ public function getAttributes() } /** + * Gets website id. + * * @param int $storeId * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getStoreWebsiteId($storeId) { return $this->_storeManager->getStore($storeId)->getWebsiteId(); } + + /** + * Retrieve excluded attributes. + * + * @return array + */ + public function getExcludedAttributes(): array + { + return $this->_excludedAttributes; + } } From b9ef877799ab4e94e5d5fd5a50342f7c8c8c8c6a Mon Sep 17 00:00:00 2001 From: Andrii Beziazychnyi <a.beziazychnyi@atwix.com> Date: Tue, 26 Nov 2019 20:54:14 +0200 Subject: [PATCH 2225/2437] Magento#25739: fixed grunt jobs - added paths to directory "generated/" - fixed clean options --- dev/tools/grunt/configs/clean.js | 15 +++++++++++++-- dev/tools/grunt/configs/path.js | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/dev/tools/grunt/configs/clean.js b/dev/tools/grunt/configs/clean.js index 2edf358670473..6e5512ab8a69d 100644 --- a/dev/tools/grunt/configs/clean.js +++ b/dev/tools/grunt/configs/clean.js @@ -38,7 +38,6 @@ var cleanOptions = { "dot": true, "src": [ "<%= path.tmp %>/cache/**/*", - "<%= path.tmp %>/generation/**/*", "<%= path.tmp %>/log/**/*", "<%= path.tmp %>/maps/**/*", "<%= path.tmp %>/page_cache/**/*", @@ -89,7 +88,6 @@ var cleanOptions = { "dot": true, "src": [ "<%= path.tmp %>/cache/**/*", - "<%= path.tmp %>/generation/**/*", "<%= path.tmp %>/view_preprocessed/html/**/*", "<%= path.tmp %>/page_cache/**/*" ] @@ -110,6 +108,19 @@ var cleanOptions = { ] } ] + }, + "generation": { + "force": true, + "files": [ + { + "force": true, + "dot": true, + "src": [ + "<%= path.generation %>code/**/*", + "<%= path.generation %>metadata/**/*" + ] + } + ] } }; diff --git a/dev/tools/grunt/configs/path.js b/dev/tools/grunt/configs/path.js index 5a9a4f6ad1598..e2320d702441e 100644 --- a/dev/tools/grunt/configs/path.js +++ b/dev/tools/grunt/configs/path.js @@ -32,5 +32,6 @@ module.exports = { whitelist: 'dev/tests/static/testsuite/Magento/Test/Js/_files/whitelist/', blacklist: 'dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/', tmp: 'validation-files.txt' - } + }, + generation: 'generated/' }; From e0cecbabdabbaf181073cb9af6970e0c7a4198ad Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 26 Nov 2019 14:12:02 -0600 Subject: [PATCH 2226/2437] MC-23217: [GraphQL] Url rewrites is invalid - Fixed the code to add entity type --- .../CatalogUrlRewriteGraphQl/etc/di.xml | 8 +++ .../Model/Resolver/UrlRewrite.php | 69 +++++++++++++++++-- .../GraphQl/Catalog/UrlRewritesTest.php | 34 +++++++-- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml index e99f89477e807..2c9a43d5e6807 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml @@ -22,4 +22,12 @@ </argument> </arguments> </type> + + <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite"> + <arguments> + <argument name="allowedEntityTypes" xsi:type="array"> + <item name="catalog_product" xsi:type="string">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 01583602348d3..2c82d72f25425 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -11,9 +11,11 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\Model\AbstractModel; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\EntityManager\TypeResolver; +use Magento\Framework\EntityManager\MetadataPool; /** * Returns URL rewrites list for the specified product @@ -25,13 +27,37 @@ class UrlRewrite implements ResolverInterface */ private $urlFinder; + /** + * @var array + */ + private $allowedEntityTypes; + + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var TypeResolver + */ + private $typeResolver; + /** * @param UrlFinderInterface $urlFinder + * @param TypeResolver $typeResolver + * @param MetadataPool $metadataPool + * @param array $allowedEntityTypes */ public function __construct( - UrlFinderInterface $urlFinder + UrlFinderInterface $urlFinder, + TypeResolver $typeResolver, + MetadataPool $metadataPool, + array $allowedEntityTypes = [] ) { $this->urlFinder = $urlFinder; + $this->typeResolver = $typeResolver; + $this->metadataPool = $metadataPool; + $this->allowedEntityTypes = $allowedEntityTypes; } /** @@ -48,11 +74,24 @@ public function resolve( throw new LocalizedException(__('"model" value should be specified')); } - /** @var AbstractModel $entity */ + /** @var AbstractModel $entity */ $entity = $value['model']; $entityId = $entity->getEntityId(); - $urlRewriteCollection = $this->urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $entityId]); + $resolveEntityType = $this->typeResolver->resolve($entity); + $metadata = $this->metadataPool->getMetadata($resolveEntityType); + $entityType = $this->getEntityType($metadata->getEavEntityType()); + + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + + $data = [ + UrlRewriteDTO::ENTITY_TYPE => $entityType, + UrlRewriteDTO::ENTITY_ID => $entityId, + UrlRewriteDTO::STORE_ID => $storeId + ]; + + $urlRewriteCollection = $this->urlFinder->findAllByData($data); + $urlRewrites = []; /** @var UrlRewriteDTO $urlRewrite */ @@ -80,9 +119,8 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); - $count = count($targetPathParts) - 1; - for ($i = 3; $i < $count; $i += 2) { + for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { $urlParameters[] = [ 'name' => $targetPathParts[$i], 'value' => $targetPathParts[$i + 1] @@ -91,4 +129,23 @@ private function getUrlParameters(string $targetPath): array return $urlParameters; } + + /** + * Get the entity type + * + * @param string $entityTypeMetadata + * @return string + */ + private function getEntityType(string $entityTypeMetadata) : string + { + $entityType = ''; + if ($entityTypeMetadata) { + switch ($entityTypeMetadata){ + case 'catalog_product': + $entityType = $this->allowedEntityTypes['catalog_product']; + break; + } + } + return $entityType; + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php index 1c32200384e70..977d2e1de95ac 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php @@ -12,6 +12,8 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Store\Model\StoreManagerInterface; /** * Test of getting URL rewrites data from products @@ -54,9 +56,20 @@ public function testProductWithNoCategoriesAssigned() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get('virtual-product', false, null, true); + $storeId = ObjectManager::getInstance()->get(StoreManagerInterface::class)->getStore()->getId(); $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class); + $entityType = ObjectManager::getInstance()->create(EavConfig::class)->getEntityType('catalog_product'); - $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]); + $entityTypeCode = $entityType->getEntityTypeCode(); + if ($entityTypeCode === 'catalog_product') { + $entityTypeCode = 'product'; + } + + $rewritesCollection = $urlFinder->findAllByData([ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ]); /* There should be only one rewrite */ /** @var UrlRewriteDTO $urlRewrite */ @@ -110,12 +123,24 @@ public function testProductWithOneCategoryAssigned() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get('simple', false, null, true); + $storeId = ObjectManager::getInstance()->get(StoreManagerInterface::class)->getStore()->getId(); $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class); + $entityType = ObjectManager::getInstance()->create(EavConfig::class)->getEntityType('catalog_product'); - $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]); - $rewritesCount = count($rewritesCollection); + $entityTypeCode = $entityType->getEntityTypeCode(); + if ($entityTypeCode === 'catalog_product') { + $entityTypeCode = 'product'; + } + $rewritesCollection = $urlFinder->findAllByData([ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ]); + + $rewritesCount = count($rewritesCollection); $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]); + $this->assertCount(1, $response['products']['items'][0]['url_rewrites']); $this->assertCount($rewritesCount, $response['products']['items'][0]['url_rewrites']); for ($i = 0; $i < $rewritesCount; $i++) { @@ -140,9 +165,8 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); - $count = count($targetPathParts) - 1; - for ($i = 3; $i < $count; $i += 2) { + for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { $urlParameters[] = [ 'name' => $targetPathParts[$i], 'value' => $targetPathParts[$i + 1] From a873aef50ce93b8bc33596d04690bb608c1098ac Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 26 Nov 2019 14:42:04 -0600 Subject: [PATCH 2227/2437] MC-23217: [GraphQL] Url rewrites is invalid - Fixed the code changes from review --- app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml index 2c9a43d5e6807..624b27eff6478 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml @@ -26,7 +26,7 @@ <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite"> <arguments> <argument name="allowedEntityTypes" xsi:type="array"> - <item name="catalog_product" xsi:type="string">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> + <item name="catalog_product" xsi:type="const">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> </argument> </arguments> </type> From 461e398719ac71e8b74a9123b3a73f380725a9ce Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 26 Nov 2019 15:08:47 -0600 Subject: [PATCH 2228/2437] MC-23217: [GraphQL] Url rewrites is invalid - Fixed the code changes from static failures --- .../Model/Resolver/UrlRewrite.php | 7 ++--- .../GraphQl/Catalog/UrlRewritesTest.php | 28 +++++++++++-------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 2c82d72f25425..4ec2a8c1c4cfb 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -119,14 +119,13 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); - - for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { + //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall + for ($i = 3; ($i < count($targetPathParts) - 1); $i += 2) { $urlParameters[] = [ 'name' => $targetPathParts[$i], 'value' => $targetPathParts[$i + 1] ]; } - return $urlParameters; } @@ -140,7 +139,7 @@ private function getEntityType(string $entityTypeMetadata) : string { $entityType = ''; if ($entityTypeMetadata) { - switch ($entityTypeMetadata){ + switch ($entityTypeMetadata) { case 'catalog_product': $entityType = $this->allowedEntityTypes['catalog_product']; break; diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php index 977d2e1de95ac..431c22e60f4f6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php @@ -65,11 +65,13 @@ public function testProductWithNoCategoriesAssigned() $entityTypeCode = 'product'; } - $rewritesCollection = $urlFinder->findAllByData([ - UrlRewriteDTO::ENTITY_ID => $product->getId(), - UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, - UrlRewriteDTO::STORE_ID => $storeId - ]); + $rewritesCollection = $urlFinder->findAllByData( + [ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ] + ); /* There should be only one rewrite */ /** @var UrlRewriteDTO $urlRewrite */ @@ -132,11 +134,13 @@ public function testProductWithOneCategoryAssigned() $entityTypeCode = 'product'; } - $rewritesCollection = $urlFinder->findAllByData([ - UrlRewriteDTO::ENTITY_ID => $product->getId(), - UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, - UrlRewriteDTO::STORE_ID => $storeId - ]); + $rewritesCollection = $urlFinder->findAllByData( + [ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ] + ); $rewritesCount = count($rewritesCollection); $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]); @@ -165,8 +169,8 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); - - for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { + //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall + for ($i = 3; ($i < count($targetPathParts) - 1); $i += 2) { $urlParameters[] = [ 'name' => $targetPathParts[$i], 'value' => $targetPathParts[$i + 1] From 9c5870c080b45e42cd40e05aaaa658110b57054c Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 26 Nov 2019 15:47:22 -0600 Subject: [PATCH 2229/2437] MC-23091: Performance improvements on configurable product - removing complexity on configurable processing --- .../CatalogGraphQl/Model/Resolver/Product.php | 8 +- .../Resolver/Product/MediaGallery/Label.php | 87 ------------------- .../Resolver/Product/MediaGallery/Url.php | 20 +++-- .../Model/Resolver/Product/ProductImage.php | 28 +++++- .../AttributeProcessor.php | 60 ++++++++++++- .../Products/Query/FieldSelection.php | 4 +- .../Magento/CatalogGraphQl/etc/graphql/di.xml | 27 ++++++ .../CatalogGraphQl/etc/schema.graphqls | 2 +- .../Configurable/Attribute/Collection.php | 1 + .../Model/Resolver/ConfigurableVariant.php | 38 +++----- .../Model/Resolver/Variant/Attributes.php | 59 ++++++++++--- .../Model/Variant/Collection.php | 18 +++- .../Theme/Model/Theme/ThemeProvider.php | 6 +- 13 files changed, 212 insertions(+), 146 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php index 86137990cc57d..889735a5f4d88 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php @@ -14,6 +14,7 @@ use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Exception\LocalizedException; /** * @inheritdoc @@ -63,10 +64,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->productDataProvider->addEavAttributes($fields); $result = function () use ($value) { - $data = $this->productDataProvider->getProductBySku($value['sku']); + $data = $value['product'] ?? $this->productDataProvider->getProductBySku($value['sku']); if (empty($data)) { return null; } + if (!isset($data['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } $productModel = $data['model']; /** @var \Magento\Catalog\Model\Product $productModel */ $data = $productModel->getData(); @@ -79,10 +83,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } } } - return array_replace($value, $data); }; - return $this->valueFactory->create($result); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php deleted file mode 100644 index 4ec76fe59ca88..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Model\Resolver\Product\MediaGallery; - -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Store\Api\Data\StoreInterface; - -/** - * Return media label - */ -class Label implements ResolverInterface -{ - /** - * @var ProductResourceModel - */ - private $productResource; - - /** - * @param ProductResourceModel $productResource - */ - public function __construct( - ProductResourceModel $productResource - ) { - $this->productResource = $productResource; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - - if (isset($value['label'])) { - return $value['label']; - } - - if (!isset($value['model'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - - /** @var Product $product */ - $product = $value['model']; - $productId = (int)$product->getEntityId(); - /** @var StoreInterface $store */ - $store = $context->getExtensionAttributes()->getStore(); - $storeId = (int)$store->getId(); - if (!isset($value['image_type'])) { - return $this->getAttributeValue($productId, 'name', $storeId); - } - $imageType = $value['image_type']; - $imageLabel = $this->getAttributeValue($productId, $imageType . '_label', $storeId); - if ($imageLabel == null) { - $imageLabel = $this->getAttributeValue($productId, 'name', $storeId); - } - - return $imageLabel; - } - - /** - * Get attribute value - * - * @param int $productId - * @param string $attributeCode - * @param int $storeId - * @return null|string Null if attribute value is not exists - */ - private function getAttributeValue(int $productId, string $attributeCode, int $storeId): ?string - { - $value = $this->productResource->getAttributeRawValue($productId, $attributeCode, $storeId); - return is_array($value) && empty($value) ? null : $value; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php index eaab159cddae6..359d295095667 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php @@ -24,11 +24,17 @@ class Url implements ResolverInterface * @var ImageFactory */ private $productImageFactory; + /** * @var PlaceholderProvider */ private $placeholderProvider; + /** + * @var string[] + */ + private $placeholderCache = []; + /** * @param ImageFactory $productImageFactory * @param PlaceholderProvider $placeholderProvider @@ -64,12 +70,8 @@ public function resolve( if (isset($value['image_type'])) { $imagePath = $product->getData($value['image_type']); return $this->getImageUrl($value['image_type'], $imagePath); - } - if (isset($value['file'])) { - $image = $this->productImageFactory->create(); - $image->setDestinationSubdir('image')->setBaseFile($value['file']); - $imageUrl = $image->getUrl(); - return $imageUrl; + } elseif (isset($value['file'])) { + return $this->getImageUrl('image', $value['file']); } return []; } @@ -84,12 +86,16 @@ public function resolve( */ private function getImageUrl(string $imageType, ?string $imagePath): string { + if (empty($imagePath) && !empty($this->placeholderCache[$imageType])) { + return $this->placeholderCache[$imageType]; + } $image = $this->productImageFactory->create(); $image->setDestinationSubdir($imageType) ->setBaseFile($imagePath); if ($image->isBaseFilePlaceholder()) { - return $this->placeholderProvider->getPlaceholder($imageType); + $this->placeholderCache[$imageType] = $this->placeholderProvider->getPlaceholder($imageType); + return $this->placeholderCache[$imageType]; } return $image->getUrl(); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index d1566162472b0..7c08f91c922bd 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -18,6 +18,25 @@ */ class ProductImage implements ResolverInterface { + /** @var array */ + private static $catalogImageLabelTypes = [ + 'image' => 'image_label', + 'small_image' => 'small_image_label', + 'thumbnail' => 'thumbnail_label' + ]; + + /** @var array */ + private $imageTypeLabels; + + /** + * @param array $imageTypeLabels + */ + public function __construct( + array $imageTypeLabels = [] + ) { + $this->imageTypeLabels = array_replace(self::$catalogImageLabelTypes, $imageTypeLabels); + } + /** * @inheritdoc */ @@ -34,11 +53,16 @@ public function resolve( /** @var Product $product */ $product = $value['model']; - $imageType = $field->getName(); + $label = $value['name'] ?? null; + if (isset($this->imageTypeLabels[$info->fieldName]) + && !empty($value[$this->imageTypeLabels[$info->fieldName]])) { + $label = $value[$this->imageTypeLabels[$info->fieldName]]; + } return [ 'model' => $product, - 'image_type' => $imageType, + 'image_type' => $field->getName(), + 'label' => $label ]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php index 2ad05fbfa1e08..fef224b12acfc 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php @@ -51,6 +51,27 @@ public function process( /** * Add attribute to collection select * + * Add attributes to the collection where graphql fields names don't match attributes names, or if attributes exist + * on a nested level and they need to be loaded. + * + * Format of the attribute can be string or array while array can have different formats. + * Example: [ + * 'price_range' => + * [ + * 'price' => 'price', + * 'price_type' => 'price_type', + * ], + * 'thumbnail' => //complex array where more than one attribute is needed to compute a value + * [ + * 'label' => + * [ + * 'attribute' => 'thumbnail_label', // the actual attribute + * 'fallback_attribute' => 'name', //used as default value in case attribute value is null + * ], + * 'url' => 'thumbnail', + * ] + * ] + * * @param Collection $collection * @param string $attribute */ @@ -59,9 +80,7 @@ private function addAttribute(Collection $collection, string $attribute): void if (isset($this->fieldToAttributeMap[$attribute])) { $attributeMap = $this->fieldToAttributeMap[$attribute]; if (is_array($attributeMap)) { - foreach ($attributeMap as $attributeName) { - $collection->addAttributeToSelect($attributeName); - } + $this->addAttributeAsArray($collection, $attributeMap); } else { $collection->addAttributeToSelect($attributeMap); } @@ -70,4 +89,39 @@ private function addAttribute(Collection $collection, string $attribute): void $collection->addAttributeToSelect($attribute); } } + + /** + * Add an array defined attribute to the collection + * + * @param Collection $collection + * @param array $attributeMap + * @return void + */ + private function addAttributeAsArray(Collection $collection, array $attributeMap): void + { + foreach ($attributeMap as $attribute) { + if (is_array($attribute)) { + $this->addAttributeComplexArrayToCollection($collection, $attribute); + } else { + $collection->addAttributeToSelect($attribute); + } + } + } + + /** + * Add a complex array defined attribute to the collection + * + * @param Collection $collection + * @param array $attribute + * @return void + */ + private function addAttributeComplexArrayToCollection(Collection $collection, array $attribute): void + { + if (isset($attribute['attribute'])) { + $collection->addAttributeToSelect($attribute['attribute']); + } + if (isset($attribute['fallback_attribute'])) { + $collection->addAttributeToSelect($attribute['fallback_attribute']); + } + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php index ae4f2e911a5b0..ffa0a3e6848e1 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php @@ -50,11 +50,11 @@ private function getProductFields(ResolveInfo $info): array { $fieldNames = []; foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'products') { + if ($node->name->value !== 'products' && $node->name->value !== 'variants') { continue; } foreach ($node->selectionSet->selections as $selection) { - if ($selection->name->value !== 'items') { + if ($selection->name->value !== 'items' && $selection->name->value !== 'product') { continue; } $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames); diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 76456166ded30..066a7b38d8967 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -134,6 +134,33 @@ <item name="price_range" xsi:type="array"> <item name="price" xsi:type="string">price</item> </item> + <item name="thumbnail" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">thumbnail_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">thumbnail</item> + </item> + <item name="small_image" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">small_image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">small_image</item> + </item> + <item name="image" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">image</item> + </item> + <item name="media_gallery" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + </item> </argument> </arguments> </type> diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 81323a50fb424..f70a32a1b549e 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -198,7 +198,7 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the interface MediaGalleryInterface @doc(description: "Contains basic information about a product image or video.") @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\MediaGalleryTypeResolver") { url: String @doc(description: "The URL of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Url") - label: String @doc(description: "The label of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Label") + label: String @doc(description: "The label of the product image or video.") } type ProductImage implements MediaGalleryInterface @doc(description: "Product image information. Contains the image URL and label.") { diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php index 59920a1ade5e0..57f701721a6f3 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php @@ -331,6 +331,7 @@ protected function loadOptions() 'use_default_value' => true ]; } + $item->setOptionsMap($values); $values = array_values($values); $item->setOptions($values); } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php index 3e07fecb2ebe7..f28bf97adf930 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php @@ -18,6 +18,7 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\FieldSelection; /** * @inheritdoc @@ -49,25 +50,33 @@ class ConfigurableVariant implements ResolverInterface */ private $metadataPool; + /** + * @var FieldSelection + */ + private $fieldSelection; + /** * @param Collection $variantCollection * @param OptionCollection $optionCollection * @param ValueFactory $valueFactory * @param AttributeCollection $attributeCollection * @param MetadataPool $metadataPool + * @param FieldSelection $fieldSelection */ public function __construct( Collection $variantCollection, OptionCollection $optionCollection, ValueFactory $valueFactory, AttributeCollection $attributeCollection, - MetadataPool $metadataPool + MetadataPool $metadataPool, + FieldSelection $fieldSelection ) { $this->variantCollection = $variantCollection; $this->optionCollection = $optionCollection; $this->valueFactory = $valueFactory; $this->attributeCollection = $attributeCollection; $this->metadataPool = $metadataPool; + $this->fieldSelection = $fieldSelection; } /** @@ -84,9 +93,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $this->variantCollection->addParentProduct($value['model']); - $fields = $this->getProductFields($info); - $matchedFields = $this->attributeCollection->getRequestAttributes($fields); - $this->variantCollection->addEavAttributes($matchedFields); + $fields = $this->fieldSelection->getProductsFieldSelection($info); + $this->variantCollection->addEavAttributes($fields); $this->optionCollection->addProductId((int)$value[$linkField]); $result = function () use ($value, $linkField) { @@ -103,26 +111,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $this->valueFactory->create($result); } - - /** - * Return field names for all requested product fields. - * - * @param ResolveInfo $info - * @return string[] - */ - private function getProductFields(ResolveInfo $info) - { - $fieldNames = []; - foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'product') { - continue; - } - - foreach ($node->selectionSet->selections as $selectionNode) { - $fieldNames[] = $selectionNode->name->value; - } - } - - return $fieldNames; - } } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php index dd2b84e1da539..db31723183d78 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php @@ -44,7 +44,10 @@ public function resolve( } $data = []; - foreach ($value['options'] as $option) { + foreach ($value['options'] as $optionId => $option) { + if (!isset($option['attribute_code'])) { + continue; + } $code = $option['attribute_code']; /** @var Product|null $model */ $model = $value['product']['model'] ?? null; @@ -52,18 +55,54 @@ public function resolve( continue; } - foreach ($option['values'] as $optionValue) { - if ($optionValue['value_index'] != $model->getData($code)) { - continue; + if (isset($option['options_map'])) { + $optionsFromMap = $this->getOptionsFromMap( + $option['options_map'] ?? [], + $code, + (int) $optionId, + (int) $model->getData($code) + ); + if (!empty($optionsFromMap)) { + $data[] = $optionsFromMap; } - $data[] = [ - 'label' => $optionValue['label'], - 'code' => $code, - 'use_default_value' => $optionValue['use_default_value'], - 'value_index' => $optionValue['value_index'] - ]; } } return $data; } + + /** + * Get options by index mapping + * + * @param array $optionMap + * @param string $code + * @param int $optionId + * @param int $attributeCodeId + * @return array + */ + private function getOptionsFromMap(array $optionMap, string $code, int $optionId, int $attributeCodeId): array + { + $data = []; + if (isset($optionMap[$optionId . ':' . $attributeCodeId])) { + $optionValue = $optionMap[$optionId . ':' . $attributeCodeId]; + $data = $this->getOptionsArray($optionValue, $code); + } + return $data; + } + + /** + * Get options formatted as array + * + * @param array $optionValue + * @param string $code + * @return array + */ + private function getOptionsArray(array $optionValue, string $code) + { + return [ + 'label' => $optionValue['label'] ?? null, + 'code' => $code, + 'use_default_value' => $optionValue['use_default_value'] ?? null, + 'value_index' => $optionValue['value_index'] ?? null, + ]; + } } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php index d517c9aa29bd3..6c4371b23927e 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php @@ -14,6 +14,7 @@ use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor; /** * Collection for fetching configurable child product data. @@ -55,22 +56,30 @@ class Collection */ private $collectionProcessor; + /** + * @var CollectionPostProcessor + */ + private $collectionPostProcessor; + /** * @param CollectionFactory $childCollectionFactory * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param MetadataPool $metadataPool * @param CollectionProcessorInterface $collectionProcessor + * @param CollectionPostProcessor $collectionPostProcessor */ public function __construct( CollectionFactory $childCollectionFactory, SearchCriteriaBuilder $searchCriteriaBuilder, MetadataPool $metadataPool, - CollectionProcessorInterface $collectionProcessor + CollectionProcessorInterface $collectionProcessor, + CollectionPostProcessor $collectionPostProcessor ) { $this->childCollectionFactory = $childCollectionFactory; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->metadataPool = $metadataPool; $this->collectionProcessor = $collectionProcessor; + $this->collectionPostProcessor = $collectionPostProcessor; } /** @@ -126,7 +135,6 @@ public function getChildProductsByParentId(int $id) : array * Fetch all children products from parent id's. * * @return array - * @throws \Exception */ private function fetch() : array { @@ -144,9 +152,11 @@ private function fetch() : array $this->searchCriteriaBuilder->create(), $attributeData ); + $childCollection->load(); + $this->collectionPostProcessor->process($childCollection, $attributeData); /** @var Product $childProduct */ - foreach ($childCollection->getItems() as $childProduct) { + foreach ($childCollection as $childProduct) { $formattedChild = ['model' => $childProduct, 'sku' => $childProduct->getSku()]; $parentId = (int)$childProduct->getParentId(); if (!isset($this->childrenMap[$parentId])) { @@ -168,7 +178,7 @@ private function fetch() : array */ private function getAttributesCodes(Product $currentProduct): array { - $attributeCodes = []; + $attributeCodes = $this->attributeCodes; $allowAttributes = $currentProduct->getTypeInstance()->getConfigurableAttributes($currentProduct); foreach ($allowAttributes as $attribute) { $productAttribute = $attribute->getProductAttribute(); diff --git a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php index cd3f7f8a2dc0e..04e4c131dbcd3 100644 --- a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php +++ b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php @@ -93,8 +93,8 @@ public function getThemeByFullPath($fullPath) if ($theme->getId()) { $this->saveThemeToCache($theme, 'theme' . $fullPath); $this->saveThemeToCache($theme, 'theme-by-id-' . $theme->getId()); - $this->themes[$fullPath] = $theme; } + $this->themes[$fullPath] = $theme; return $theme; } @@ -167,6 +167,8 @@ private function saveThemeToCache(\Magento\Theme\Model\Theme $theme, $cacheId) } /** + * Get theme list + * * @deprecated 100.1.3 * @return ListInterface */ @@ -179,6 +181,8 @@ private function getThemeList() } /** + * Get deployment config + * * @deprecated 100.1.3 * @return DeploymentConfig */ From 8dea65ba967de572e272f700aa747867eb555fb7 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 26 Nov 2019 16:29:01 -0600 Subject: [PATCH 2230/2437] MC-23217: [GraphQL] Url rewrites is invalid - Fixed the code changes from review --- .../UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php | 13 +++++-------- .../Magento/GraphQl/Catalog/UrlRewritesTest.php | 13 +++++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 4ec2a8c1c4cfb..053c746428c6f 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -119,11 +119,12 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); + $count = count($targetPathParts) - 1; //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall - for ($i = 3; ($i < count($targetPathParts) - 1); $i += 2) { + for ($index = 3; $index < $count; $index += 2) { $urlParameters[] = [ - 'name' => $targetPathParts[$i], - 'value' => $targetPathParts[$i + 1] + 'name' => $targetPathParts[$index], + 'value' => $targetPathParts[$index + 1] ]; } return $urlParameters; @@ -139,11 +140,7 @@ private function getEntityType(string $entityTypeMetadata) : string { $entityType = ''; if ($entityTypeMetadata) { - switch ($entityTypeMetadata) { - case 'catalog_product': - $entityType = $this->allowedEntityTypes['catalog_product']; - break; - } + $entityType = $this->allowedEntityTypes[$entityTypeMetadata]; } return $entityType; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php index 431c22e60f4f6..0d808c6dd0696 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php @@ -147,10 +147,10 @@ public function testProductWithOneCategoryAssigned() $this->assertCount(1, $response['products']['items'][0]['url_rewrites']); $this->assertCount($rewritesCount, $response['products']['items'][0]['url_rewrites']); - for ($i = 0; $i < $rewritesCount; $i++) { - $urlRewrite = $rewritesCollection[$i]; + for ($index = 0; $index < $rewritesCount; $index++) { + $urlRewrite = $rewritesCollection[$index]; $this->assertResponseFields( - $response['products']['items'][0]['url_rewrites'][$i], + $response['products']['items'][0]['url_rewrites'][$index], [ "url" => $urlRewrite->getRequestPath(), "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath()) @@ -169,11 +169,12 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); + $count = count($targetPathParts) - 1; //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall - for ($i = 3; ($i < count($targetPathParts) - 1); $i += 2) { + for ($index = 3; $index < $count; $index += 2) { $urlParameters[] = [ - 'name' => $targetPathParts[$i], - 'value' => $targetPathParts[$i + 1] + 'name' => $targetPathParts[$index], + 'value' => $targetPathParts[$index + 1] ]; } From eb13667714a5635b4196adc75e77fc080634eae4 Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Tue, 26 Nov 2019 16:41:07 -0600 Subject: [PATCH 2231/2437] MC-23217: [GraphQL] Url rewrites is invalid - addressed the review comments --- .../UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 053c746428c6f..9fb7127b289bb 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -30,7 +30,7 @@ class UrlRewrite implements ResolverInterface /** * @var array */ - private $allowedEntityTypes; + private $entityTypeMapping; /** * @var MetadataPool @@ -46,18 +46,18 @@ class UrlRewrite implements ResolverInterface * @param UrlFinderInterface $urlFinder * @param TypeResolver $typeResolver * @param MetadataPool $metadataPool - * @param array $allowedEntityTypes + * @param array $entityTypeMapping */ public function __construct( UrlFinderInterface $urlFinder, TypeResolver $typeResolver, MetadataPool $metadataPool, - array $allowedEntityTypes = [] + array $entityTypeMapping = [] ) { $this->urlFinder = $urlFinder; $this->typeResolver = $typeResolver; $this->metadataPool = $metadataPool; - $this->allowedEntityTypes = $allowedEntityTypes; + $this->entityTypeMapping = $entityTypeMapping; } /** @@ -140,7 +140,7 @@ private function getEntityType(string $entityTypeMetadata) : string { $entityType = ''; if ($entityTypeMetadata) { - $entityType = $this->allowedEntityTypes[$entityTypeMetadata]; + $entityType = $this->entityTypeMapping[$entityTypeMetadata]; } return $entityType; } From 51e17d4db5bb7dbed9829c33c5cfd07e4e073294 Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 27 Nov 2019 00:06:53 -0700 Subject: [PATCH 2232/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- .../Model/Rule/ConfigurableProductHandler.php | 29 ++----------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php index d57ce702f70b8..d27c424ed9ea3 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php +++ b/app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/ConfigurableProductHandler.php @@ -1,14 +1,16 @@ <?php /** + * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\CatalogRuleConfigurable\Plugin\CatalogRule\Model\Rule; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; use Magento\CatalogRuleConfigurable\Plugin\CatalogRule\Model\ConfigurableProductsProvider; /** - * Add configurable sub products to catalog rule indexer on reindex + * Add configurable sub products to catalog rule indexer on full reindex */ class ConfigurableProductHandler { @@ -40,31 +42,6 @@ public function __construct( } /** - * Add configurable products during setting product ids for filtering - * - * @param \Magento\CatalogRule\Model\Rule $rule - * @param int|array $productIds - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeSetProductsFilter(\Magento\CatalogRule\Model\Rule $rule, $productIds) - { - if ($productIds) { - $configurableProductIds = $this->configurable->getParentIdsByChild($productIds); - if ($configurableProductIds) { - $productIds = array_merge((array) $productIds, $configurableProductIds); - - } - } - - return [ - $productIds, - ]; - } - - /** - * Add configurable products for matched products - * * @param \Magento\CatalogRule\Model\Rule $rule * @param array $productIds * @return array From f34fd5cf66c9791a6943b8a540257a5b7fce578f Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <horytsky@adobe.com> Date: Wed, 27 Nov 2019 01:53:26 -0700 Subject: [PATCH 2233/2437] MC-19646: [Magento Cloud] - Catalog Product Rule Indexer stuck --- app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 0ca9c544b74c9..1fc53c78985fb 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -293,6 +293,7 @@ protected function doReindexByIds($ids) /** @var Rule[] $activeRules */ $activeRules = $this->getActiveRules()->getItems(); foreach ($activeRules as $rule) { + $rule->setProductsFilter($ids); $this->reindexRuleProduct->execute($rule, $this->batchCount); } From e6ea90ef32bff884a97c34b48ff5c9400fa5891f Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 27 Nov 2019 11:52:37 +0200 Subject: [PATCH 2234/2437] MC-28956: Discounted shipping price is incorrectly displayed on multishipping review order page --- .../Test/Unit/Model/Checkout/Type/MultishippingTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index 731365974c235..fba3245bec68d 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -420,13 +420,16 @@ public function testSetShippingMethods() $methodsArray = [1 => 'flatrate_flatrate', 2 => 'tablerate_bestway']; $addressId = 1; $addressMock = $this->getMockBuilder(QuoteAddress::class) - ->setMethods(['getId', 'setShippingMethod']) + ->setMethods(['getId', 'setShippingMethod', 'setCollectShippingRates']) ->disableOriginalConstructor() ->getMock(); $addressMock->expects($this->once())->method('getId')->willReturn($addressId); $this->quoteMock->expects($this->once())->method('getAllShippingAddresses')->willReturn([$addressMock]); $addressMock->expects($this->once())->method('setShippingMethod')->with($methodsArray[$addressId]); + $addressMock->expects($this->once()) + ->method('setCollectShippingRates') + ->with(true); $this->quoteMock->expects($this->once()) ->method('__call') ->with('setTotalsCollectedFlag', [false]) From ed92b99eae3b856a46aa903f8514ac118d1d808e Mon Sep 17 00:00:00 2001 From: chorniy_andrey <mr.chornij@gmail.com> Date: Wed, 27 Nov 2019 12:30:21 +0200 Subject: [PATCH 2235/2437] delete code which doesn't have any sense here --- .../Catalog/view/frontend/templates/product/listing.phtml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml index b776fd4f7e193..6cebd51284f48 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml @@ -16,7 +16,6 @@ */ ?> <?php -$start = microtime(true); $_productCollection = $block->getLoadedProductCollection(); $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> @@ -98,4 +97,3 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); </div> <?= $block->getToolbarHtml() ?> <?php endif; ?> -<?= $time_taken = microtime(true) - $start ?> From a738307bdf9ff9153deb96569f8e3363337b15d3 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 27 Nov 2019 14:46:43 +0200 Subject: [PATCH 2236/2437] Refactoring Action Groups --- ...NavigateToNewRelicConfigurationActionGroup.xml | 15 +++++++++++++++ ...ToggleNewRelicReportingEnabledActionGroup.xml} | 2 +- ...nUncheckNewRelicUseSystemValueActionGroup.xml} | 2 +- ...ewRelicConfigFieldIsNotVisibleActionGroup.xml} | 2 +- ...inNewRelicConfigFieldIsVisibleActionGroup.xml} | 2 +- .../AdminChecksSystemConfigDependencyTest.xml | 10 +++++----- 6 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml rename app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/{AdminTogglesEnabledConfigActionGroup.xml => AdminToggleNewRelicReportingEnabledActionGroup.xml} (88%) rename app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/{AdminUnchecksUseSystemValueActionGroup.xml => AdminUncheckNewRelicUseSystemValueActionGroup.xml} (86%) rename app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/{AssertAdminDoesntSeeConfigFieldActionGroup.xml => AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml} (86%) rename app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/{AssertAdminSeesConfigFieldActionGroup.xml => AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml} (87%) diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml new file mode 100644 index 0000000000000..9e8314792b0bd --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateToNewRelicConfigurationActionGroup"> + <amOnPage url="{{AdminNewRelicConfigPage.url}}" stepKey="navigateToNewRelicConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.xml similarity index 88% rename from app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml rename to app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.xml index f9632784f4a92..602484189dda4 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminTogglesEnabledConfigActionGroup.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminTogglesEnabledConfigActionGroup"> + <actionGroup name="AdminToggleNewRelicReportingEnabledActionGroup"> <arguments> <argument name="state" type="string"/> </arguments> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml similarity index 86% rename from app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml rename to app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml index 7d90c1d8dd478..31c57c680b2ef 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUnchecksUseSystemValueActionGroup.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminUnchecksUseSystemValueActionGroup"> + <actionGroup name="AdminUncheckNewRelicUseSystemValueActionGroup"> <uncheckOption selector="{{AdminNewRelicConfigSystemSection.useSystemValueForEnabled}}" stepKey="uncheckCheckbox"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml similarity index 86% rename from app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml rename to app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml index 6e87dbbfbbf4b..1c347512c1737 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminDoesntSeeConfigFieldActionGroup.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminDoesntSeeConfigFieldActionGroup"> + <actionGroup name="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup"> <arguments> <argument name="config" type="string"/> </arguments> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml similarity index 87% rename from app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml rename to app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml index 4df7099e50b28..fd3b3e47719c0 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminSeesConfigFieldActionGroup.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminSeesConfigFieldActionGroup"> + <actionGroup name="AssertAdminNewRelicConfigFieldIsVisibleActionGroup"> <arguments> <argument name="config" type="string"/> </arguments> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml index eabacffe9b181..676d1dcacf75f 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml @@ -20,21 +20,21 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> - <actionGroup ref="AdminExpandNewRelicConfigTabActionGroup" stepKey="expandGeneralTab"> + <actionGroup ref="AdminExpandNewRelicConfigTabActionGroup" stepKey="expandingGeneralTab"> <argument name="tabName" value="general"/> </actionGroup> </before> <after> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> - <actionGroup ref="AssertAdminDontSeeConfigFieldActionGroup" stepKey="checkIfApiUrlIsNotVisible"> + <actionGroup ref="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup" stepKey="checkingIfApiUrlIsNotVisible"> <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> </actionGroup> - <actionGroup ref="AdminUnchecksUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"/> - <actionGroup ref="AdminTogglesEnabledConfigActionGroup" stepKey="enableNewRelic"> + <actionGroup ref="AdminUncheckNewRelicUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"/> + <actionGroup ref="AdminToggleNewRelicReportingEnabledActionGroup" stepKey="enablingNewRelicReporting"> <argument name="state" value="Yes"/> </actionGroup> - <actionGroup ref="AssertAdminSeesConfigFieldActionGroup" stepKey="checkIfApiUrlIsVisible"> + <actionGroup ref="AssertAdminNewRelicConfigFieldIsVisibleActionGroup" stepKey="checkingIfApiUrlIsVisible"> <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> </actionGroup> </test> From 42a7983135d0017ed5765f3252186634b5e29dbc Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 27 Nov 2019 15:12:43 +0200 Subject: [PATCH 2237/2437] Giving the possibility to have a config dependency based on empty field value --- .../Config/Structure/Element/Dependency/Field.php | 2 +- .../Structure/Element/Dependency/FieldTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 8f4d82eed51c5..6ed9be0d10e11 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -41,7 +41,7 @@ public function __construct(array $fieldData = [], $fieldPrefix = "") if (isset($fieldData['separator'])) { $this->_values = explode($fieldData['separator'], $fieldData['value']); } else { - $this->_values = [$fieldData['value']]; + $this->_values = [isset($fieldData['value']) ? $fieldData['value'] : '']; } $fieldId = $fieldPrefix . (isset( $fieldData['dependPath'] diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php index 750a829eef7ec..22cf979a9bd63 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php @@ -12,6 +12,8 @@ class FieldTest extends \PHPUnit\Framework\TestCase */ const SIMPLE_VALUE = 'someValue'; + const EMPTY_VALUE = ''; + const COMPLEX_VALUE1 = 'value_1'; const COMPLEX_VALUE2 = 'value_2'; @@ -150,8 +152,19 @@ public function getValuesDataProvider() return [ [$this->_getSimpleData(), true, [self::SIMPLE_VALUE]], [$this->_getSimpleData(), false, [self::SIMPLE_VALUE]], + [$this->_getSimpleEmptyData(), false, [static::EMPTY_VALUE]], [$this->_getComplexData(), true, $complexDataValues], [$this->_getComplexData(), false, $complexDataValues] ]; } + + /** + * Providing a field data with no field value + * + * @return array + */ + protected function _getSimpleEmptyData(): array + { + return ['dependPath' => ['section_2', 'group_3', 'field_4']]; + } } From dd12c89adf4b5f780f7f4b6f1e93e61c494b3a79 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Tue, 26 Nov 2019 17:29:35 -0600 Subject: [PATCH 2238/2437] MC-21821: Magento 2.2.8 Can't save products after updating - Fix error message not showing when there are invalid fields in advanced inventory --- .../DataProvider/Product/Form/Modifier/AdvancedInventory.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php index 22896c5e47567..aafde14a28584 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php @@ -255,6 +255,9 @@ private function prepareMeta() 'actionName' => 'toggleModal', ], ], + 'imports' => [ + 'childError' => 'product_form.product_form.advanced_inventory_modal.stock_data:error', + ], 'title' => __('Advanced Inventory'), 'provider' => false, 'additionalForGroup' => true, From a9d33f0ab89c354dd835bac6398770dd7b8f0923 Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Wed, 27 Nov 2019 16:39:51 +0200 Subject: [PATCH 2239/2437] MC-29212: Magento\LayeredNavigation\Block\Navigation\Category\SelectFilterTest.testGetFiltersWithCustomAttribute fails on nightly builds with mysql search --- .../_files/product_dropdown_attribute.php | 67 +++++++++++++++++++ .../product_dropdown_attribute_rollback.php | 25 +++++++ .../Navigation/Category/SelectFilterTest.php | 2 +- 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php new file mode 100644 index 0000000000000..ae0c1d3613380 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Catalog\Setup\CategorySetup; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var $attribute Attribute */ +$attribute = $objectManager->create(Attribute::class); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +/** @var $installer CategorySetup */ +$installer = $objectManager->create(CategorySetup::class); +$entityTypeId = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); + +if (!$attribute->loadByCode($entityTypeId, 'dropdown_attribute')->getId()) { + $attribute->setData( + [ + 'attribute_code' => 'dropdown_attribute', + 'entity_type_id' => $entityTypeId, + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Drop-Down Attribute'], + 'backend_type' => 'int', + 'option' => [ + 'value' => [ + 'option_1' => ['Option 1'], + 'option_2' => ['Option 2'], + 'option_3' => ['Option 3'], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'Attributes', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php new file mode 100644 index 0000000000000..b48acc0ca0ac6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; + +$objectManager = Bootstrap::getObjectManager(); +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('dropdown_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php index c0677bbc6b72b..76217e9683993 100644 --- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php +++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/SelectFilterTest.php @@ -19,7 +19,7 @@ class SelectFilterTest extends AbstractFiltersTest { /** - * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php * @dataProvider getFiltersWithCustomAttributeDataProvider * @param array $products From fa123f4d201318518cffd7b4998c2a789f985119 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 27 Nov 2019 09:44:33 -0600 Subject: [PATCH 2240/2437] MC-23091: Performance improvements on configurable product - fixing static --- .../Model/Resolver/Variant/Attributes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php index db31723183d78..faf666144422c 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php @@ -96,7 +96,7 @@ private function getOptionsFromMap(array $optionMap, string $code, int $optionId * @param string $code * @return array */ - private function getOptionsArray(array $optionValue, string $code) + private function getOptionsArray(array $optionValue, string $code): array { return [ 'label' => $optionValue['label'] ?? null, From bad59fd91b87711891d46521d28b1a1ce49f36fa Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 27 Nov 2019 12:25:02 -0600 Subject: [PATCH 2241/2437] MC-23217: [GraphQL] Url rewrites is invalid - addressed the missing name chznge in di.xml --- app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml index 624b27eff6478..8724972e71b17 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml @@ -25,7 +25,7 @@ <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite"> <arguments> - <argument name="allowedEntityTypes" xsi:type="array"> + <argument name="entityTypeMapping" xsi:type="array"> <item name="catalog_product" xsi:type="const">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> </argument> </arguments> From 05294b54282e16a774cf6bfd39bad18f2e60c6aa Mon Sep 17 00:00:00 2001 From: Anusha Vattam <avattam@adobe.com> Date: Wed, 27 Nov 2019 12:32:20 -0600 Subject: [PATCH 2242/2437] MC-23217: [GraphQL] Url rewrites is invalid - addressed the missing name chznge in di.xml and resolver --- app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml | 2 +- .../Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml index 624b27eff6478..8724972e71b17 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml @@ -25,7 +25,7 @@ <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite"> <arguments> - <argument name="allowedEntityTypes" xsi:type="array"> + <argument name="entityTypeMapping" xsi:type="array"> <item name="catalog_product" xsi:type="const">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> </argument> </arguments> diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index 9fb7127b289bb..55cd505928f42 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -120,6 +120,9 @@ private function getUrlParameters(string $targetPath): array $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); $count = count($targetPathParts) - 1; + + /** $index starts from 3 to eliminate catalog/product/view/ part and fetch only name, + value data from from target path */ //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall for ($index = 3; $index < $count; $index += 2) { $urlParameters[] = [ From 9a3d147d9947e83a9af80453acdcf07207449076 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 27 Nov 2019 22:10:39 +0200 Subject: [PATCH 2243/2437] Refactoring the test coverage --- .../AdminExpandConfigSectionActionGroup.xml | 22 +++++++++++++++++++ .../Test/Mftf/Section/AdminConfigSection.xml | 3 +++ ...dminExpandNewRelicConfigTabActionGroup.xml | 17 -------------- ...checkNewRelicUseSystemValueActionGroup.xml | 8 +++++-- .../AdminNewRelicConfigSystemSection.xml | 3 +-- ...eckNewRelicSystemConfigDependencyTest.xml} | 12 +++++----- 6 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml delete mode 100644 app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml rename app/code/Magento/NewRelicReporting/Test/Mftf/Test/{AdminChecksSystemConfigDependencyTest.xml => AdminCheckNewRelicSystemConfigDependencyTest.xml} (78%) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml new file mode 100644 index 0000000000000..03a3a45ef0947 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminExpandConfigSectionActionGroup"> + <annotations> + <description>Expands configuration section passed via argument as Section Name.</description> + </annotations> + <arguments> + <argument name="sectionName" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminConfigSection.collapsibleSectionByTitle(sectionName)}}" dependentSelector="{{AdminConfigSection.expandedSectionByTitle(sectionName)}}" visible="false" stepKey="expandSection" /> + <waitForElement selector="{{AdminConfigSection.expandedSectionByTitle(sectionName)}}" stepKey="waitOpenedSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index ffe3f0076ca8d..3bad136935734 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -19,5 +19,8 @@ <element name="defaultConfigButton" type="button" selector="#store-change-button" timeout="30"/> <element name="defaultConfigDropdown" type="button" selector="//ul[@class='dropdown-menu']" timeout="30"/> <element name="fieldError" type="text" selector="label.mage-error"/> + <element name="useSystemValue" type="checkbox" selector="#{{element}} > .use-default > input" parameterized="true"/> + <element name="collapsibleSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> + <element name="expandedSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config active'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml deleted file mode 100644 index 4dc3d2ea8ea34..0000000000000 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminExpandNewRelicConfigTabActionGroup.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminExpandNewRelicConfigTabActionGroup"> - <arguments> - <argument name="tabName" type="string"/> - </arguments> - <conditionalClick selector="{{AdminNewRelicConfigSystemSection.tab(tabName)}}" dependentSelector="{{AdminNewRelicConfigSystemSection.status}}" visible="false" stepKey="expandTab"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml index 31c57c680b2ef..41f18f0f90d8d 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml @@ -8,7 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminUncheckNewRelicUseSystemValueActionGroup"> - <uncheckOption selector="{{AdminNewRelicConfigSystemSection.useSystemValueForEnabled}}" stepKey="uncheckCheckbox"/> + <actionGroup name="AdminUncheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <uncheckOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="uncheckCheckbox"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml index 79625273b988e..8cecf62999bc3 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml @@ -9,9 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewRelicConfigSystemSection"> - <element name="tab" type="button" selector="#newrelicreporting_{{tab}}-head" parameterized="true"/> + <element name="statusRowId" type="text" selector="#row_newrelicreporting_general_enable"/> <element name="status" type="select" selector="#row_newrelicreporting_general_enable [data-ui-id='select-groups-general-fields-enable-value']"/> - <element name="useSystemValueForEnabled" type="checkbox" selector="#newrelicreporting_general_enable_inherit"/> <element name="apiUrl" type="select" selector="input#newrelicreporting_general_api_url"/> </section> </sections> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml similarity index 78% rename from app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml rename to app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml index 676d1dcacf75f..c343f93de2c58 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminChecksSystemConfigDependencyTest.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml @@ -8,20 +8,20 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminChecksSystemConfigDependencyTest"> + <test name="AdminCheckNewRelicSystemConfigDependencyTest"> <annotations> <features value="NewRelicReporting"/> <stories value="Admin is able to see the configuration fields only after enabling the feature"/> <title value="Admin can see the configuration fields only after enabling the feature"/> <description value="The system configs should be available only after enabling the New Relic feature."/> - <severity value="CRITICAL"/> + <severity value="MINOR"/> <group value="NewRelicReporting"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> - <actionGroup ref="AdminExpandNewRelicConfigTabActionGroup" stepKey="expandingGeneralTab"> - <argument name="tabName" value="general"/> + <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGeneralSection"> + <argument name="sectionName" value="General"/> </actionGroup> </before> <after> @@ -30,7 +30,9 @@ <actionGroup ref="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup" stepKey="checkingIfApiUrlIsNotVisible"> <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> </actionGroup> - <actionGroup ref="AdminUncheckNewRelicUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"/> + <actionGroup ref="AdminUncheckUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"> + <argument name="rowId" value="row_newrelicreporting_general_enable"/> + </actionGroup> <actionGroup ref="AdminToggleNewRelicReportingEnabledActionGroup" stepKey="enablingNewRelicReporting"> <argument name="state" value="Yes"/> </actionGroup> From d504f549ba789758a6ce271b23e63ab058611043 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 27 Nov 2019 22:12:39 +0200 Subject: [PATCH 2244/2437] Refactoring the test coverage --- .../Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml index 8cecf62999bc3..5bf849cd0134e 100644 --- a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml @@ -9,8 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewRelicConfigSystemSection"> - <element name="statusRowId" type="text" selector="#row_newrelicreporting_general_enable"/> <element name="status" type="select" selector="#row_newrelicreporting_general_enable [data-ui-id='select-groups-general-fields-enable-value']"/> - <element name="apiUrl" type="select" selector="input#newrelicreporting_general_api_url"/> + <element name="apiUrl" type="input" selector="input#newrelicreporting_general_api_url"/> </section> </sections> From f78f0c1da3f770d2fc2128b5ec846bfadb1509e8 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 27 Nov 2019 22:23:25 +0200 Subject: [PATCH 2245/2437] Renaming the selector --- .../Magento/Config/Test/Mftf/Section/AdminConfigSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index 3bad136935734..a4fb3c7e32975 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -19,7 +19,7 @@ <element name="defaultConfigButton" type="button" selector="#store-change-button" timeout="30"/> <element name="defaultConfigDropdown" type="button" selector="//ul[@class='dropdown-menu']" timeout="30"/> <element name="fieldError" type="text" selector="label.mage-error"/> - <element name="useSystemValue" type="checkbox" selector="#{{element}} > .use-default > input" parameterized="true"/> + <element name="useSystemValue" type="checkbox" selector="#{{configRowId}} > .use-default > input" parameterized="true"/> <element name="collapsibleSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> <element name="expandedSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config active'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> </section> From e695e09b98621ed5d8b593f079299d8e5cdefc40 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Wed, 27 Nov 2019 14:18:22 -0600 Subject: [PATCH 2246/2437] MC-17545: Remove deprecation annotations from the PatchVersionInterface --- .../Magento/Framework/Setup/Patch/PatchVersionInterface.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php index 1638d67041a11..c1f4439d692a4 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php @@ -7,6 +7,8 @@ /** * For backward compatibility with versioned style module installation. + * The interface should be used for migration from the legacy installation approach to the declarative installation + * mechanism. The usage of this interface prohibited for the new data or schema patches. * */ interface PatchVersionInterface From d3f5c94a9582f3cce62e56affa18b3886b634e6b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 27 Nov 2019 16:30:56 -0600 Subject: [PATCH 2247/2437] MC-29033: admin catalog can't load css in the cloud --- .../Framework/View/Asset/Minification.php | 49 ++++++++++++++++- .../MinificationFilenameResolver.php | 54 +++++++++++++++++-- .../View/Asset/PreProcessor/Minify.php | 48 +++++++++++++++-- 3 files changed, 141 insertions(+), 10 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Asset/Minification.php b/lib/internal/Magento/Framework/View/Asset/Minification.php index 087d57ffa4162..40ac4dd1f3ba3 100644 --- a/lib/internal/Magento/Framework/View/Asset/Minification.php +++ b/lib/internal/Magento/Framework/View/Asset/Minification.php @@ -83,7 +83,7 @@ public function addMinifiedSign($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); - if ($this->isEnabled($extension) && + if ($this->isEnabledForArea($filename) && !$this->isExcluded($filename) && !$this->isMinifiedFilename($filename) ) { @@ -102,7 +102,7 @@ public function removeMinifiedSign($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); - if ($this->isEnabled($extension) && + if ($this->isEnabledForArea($filename) && !$this->isExcluded($filename) && $this->isMinifiedFilename($filename) ) { @@ -180,4 +180,49 @@ private function getMinificationExcludeValues($key) } return array_values($configValues); } + + /** + * Check whether asset minification is on for specified content type and for area + * + * @param string $filename + * @return bool + */ + private function isEnabledForArea(string $filename): bool + { + $area = $this->getAreaFromPath($filename); + $extension = pathinfo($filename, PATHINFO_EXTENSION); + + if ($area !== 'adminhtml') { + $result = $this->isEnabled($extension); + } else { + $cacheConfigKey = $area . '_' . $extension; + if (!isset($this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$cacheConfigKey])) { + $this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$cacheConfigKey] = + $this->appState->getMode() != State::MODE_DEVELOPER && + $this->scopeConfig->isSetFlag( + sprintf(self::XML_PATH_MINIFICATION_ENABLED, $extension), + 'default' + ); + } + + $result = $this->configCache[self::XML_PATH_MINIFICATION_ENABLED][$cacheConfigKey]; + } + return $result; + } + + /** + * Get area from the path + * + * @param string $filename + * @return string + */ + private function getAreaFromPath(string $filename): string + { + $area = ''; + $pathParts = explode('/', $filename); + if (!empty($pathParts) && isset($pathParts[0])) { + $area = $pathParts[0]; + } + return $area; + } } diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php index 313f2daa80bcd..a6d7b3de00104 100644 --- a/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php @@ -6,6 +6,8 @@ namespace Magento\Framework\View\Asset\PreProcessor; use Magento\Framework\View\Asset\Minification; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; /** * Class MinificationFilenameResolver @@ -22,14 +24,31 @@ class MinificationFilenameResolver implements FilenameResolverInterface */ private $minification; + /** + * @var State + */ + private $appState; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * Constructor * * @param Minification $minification + * @param ScopeConfigInterface $scopeConfig + * @param State $appState */ - public function __construct(Minification $minification) - { + public function __construct( + Minification $minification, + ScopeConfigInterface $scopeConfig, + State $appState + ) { $this->minification = $minification; + $this->scopeConfig = $scopeConfig; + $this->appState = $appState; } /** @@ -40,10 +59,35 @@ public function __construct(Minification $minification) */ public function resolve($path) { - if (!$this->minification->isEnabled(pathinfo($path, PATHINFO_EXTENSION))) { - return $path; + $result = $path; + if ($this->isEnabledForArea($path)) { + $result = str_replace(self::FILE_PART, '.', $path); } - return str_replace(self::FILE_PART, '.', $path); + return $result; + } + + /** + * Check whether asset minification is on for specified content type and for area + * + * @param string $filename + * @return bool + */ + private function isEnabledForArea(string $filename): bool + { + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $result = $this->minification->isEnabled($extension); + $pathParts = explode('/', $filename); + if (!empty($pathParts) && isset($pathParts[0])) { + $area = $pathParts[0]; + if ($area === 'adminhtml') { + $result = $this->appState->getMode() != State::MODE_DEVELOPER && + $this->scopeConfig->isSetFlag( + sprintf(Minification::XML_PATH_MINIFICATION_ENABLED, $extension), + 'default' + ); + } + } + return $result; } } diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php index c447285bc692b..4cf5b0cc8c70c 100644 --- a/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Asset\Minification; use Magento\Framework\View\Asset\PreProcessor; use Magento\Framework\View\Asset\PreProcessorInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; /** * Assets minification pre-processor @@ -25,14 +27,30 @@ class Minify implements PreProcessorInterface */ protected $minification; + /** + * @var State + */ + private $appState; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param AdapterInterface $adapter * @param Minification $minification */ - public function __construct(AdapterInterface $adapter, Minification $minification) - { + public function __construct( + AdapterInterface $adapter, + Minification $minification, + ScopeConfigInterface $scopeConfig, + State $appState + ) { $this->adapter = $adapter; $this->minification = $minification; + $this->scopeConfig = $scopeConfig; + $this->appState = $appState; } /** @@ -43,7 +61,7 @@ public function __construct(AdapterInterface $adapter, Minification $minificatio */ public function process(PreProcessor\Chain $chain) { - if ($this->minification->isEnabled(pathinfo($chain->getTargetAssetPath(), PATHINFO_EXTENSION)) && + if ($this->isEnabledForArea($chain->getTargetAssetPath()) && $this->minification->isMinifiedFilename($chain->getTargetAssetPath()) && !$this->minification->isMinifiedFilename($chain->getOrigAssetPath()) ) { @@ -51,4 +69,28 @@ public function process(PreProcessor\Chain $chain) $chain->setContent($content); } } + + /** + * Check whether asset minification is on for specified content type and for area + * + * @param string $filename + * @return bool + */ + private function isEnabledForArea(string $filename): bool + { + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $result = $this->minification->isEnabled($extension); + $pathParts = explode('/', $filename); + if (!empty($pathParts) && isset($pathParts[0])) { + $area = $pathParts[0]; + if ($area === 'adminhtml') { + $result = $this->appState->getMode() != State::MODE_DEVELOPER && + $this->scopeConfig->isSetFlag( + sprintf(Minification::XML_PATH_MINIFICATION_ENABLED, $extension), + 'default' + ); + } + } + return $result; + } } From efa91afa14117efcbef33e880243177ba7841994 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 27 Nov 2019 17:21:56 -0600 Subject: [PATCH 2248/2437] MC-29033: admin catalog can't load css in the cloud --- .../MinificationConfigProvider.php | 72 +++++++++++++++++++ .../MinificationFilenameResolver.php | 46 ++---------- .../View/Asset/PreProcessor/Minify.php | 44 ++---------- .../MinificationFilenameResolverTest.php | 13 ++-- .../Unit/Asset/PreProcessor/MinifyTest.php | 18 +++-- 5 files changed, 106 insertions(+), 87 deletions(-) create mode 100644 lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationConfigProvider.php diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationConfigProvider.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationConfigProvider.php new file mode 100644 index 0000000000000..c779e84a5a51c --- /dev/null +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationConfigProvider.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare (strict_types=1); + +namespace Magento\Framework\View\Asset\PreProcessor; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Framework\View\Asset\Minification; + +/** + * Minification configuration provider + */ +class MinificationConfigProvider +{ + /** + * @var Minification + */ + private $minification; + + /** + * @var State + */ + private $appState; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param Minification $minification + * @param ScopeConfigInterface $scopeConfig + * @param State $appState + */ + public function __construct( + Minification $minification, + ScopeConfigInterface $scopeConfig, + State $appState + ) { + $this->minification = $minification; + $this->scopeConfig = $scopeConfig; + $this->appState = $appState; + } + + /** + * Check whether asset minification is on + * + * @param string $filename + * @return bool + */ + public function isMinificationEnabled(string $filename): bool + { + $extension = pathinfo($filename, PATHINFO_EXTENSION); + $result = $this->minification->isEnabled($extension); + $pathParts = explode('/', $filename); + if (!empty($pathParts) && isset($pathParts[0])) { + $area = $pathParts[0]; + if ($area === 'adminhtml') { + $result = $this->appState->getMode() != State::MODE_DEVELOPER && + $this->scopeConfig->isSetFlag( + sprintf(Minification::XML_PATH_MINIFICATION_ENABLED, $extension), + 'default' + ); + } + } + return $result; + } +} diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php index a6d7b3de00104..6a2e953a4c22d 100644 --- a/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/MinificationFilenameResolver.php @@ -6,8 +6,6 @@ namespace Magento\Framework\View\Asset\PreProcessor; use Magento\Framework\View\Asset\Minification; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\State; /** * Class MinificationFilenameResolver @@ -25,30 +23,22 @@ class MinificationFilenameResolver implements FilenameResolverInterface private $minification; /** - * @var State + * @var MinificationConfigProvider */ - private $appState; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; + private $minificationConfig; /** * Constructor * * @param Minification $minification - * @param ScopeConfigInterface $scopeConfig - * @param State $appState + * @param MinificationConfigProvider $minificationConfig */ public function __construct( Minification $minification, - ScopeConfigInterface $scopeConfig, - State $appState + MinificationConfigProvider $minificationConfig ) { $this->minification = $minification; - $this->scopeConfig = $scopeConfig; - $this->appState = $appState; + $this->minificationConfig = $minificationConfig; } /** @@ -60,34 +50,10 @@ public function __construct( public function resolve($path) { $result = $path; - if ($this->isEnabledForArea($path)) { + if ($this->minificationConfig->isMinificationEnabled($path)) { $result = str_replace(self::FILE_PART, '.', $path); } return $result; } - - /** - * Check whether asset minification is on for specified content type and for area - * - * @param string $filename - * @return bool - */ - private function isEnabledForArea(string $filename): bool - { - $extension = pathinfo($filename, PATHINFO_EXTENSION); - $result = $this->minification->isEnabled($extension); - $pathParts = explode('/', $filename); - if (!empty($pathParts) && isset($pathParts[0])) { - $area = $pathParts[0]; - if ($area === 'adminhtml') { - $result = $this->appState->getMode() != State::MODE_DEVELOPER && - $this->scopeConfig->isSetFlag( - sprintf(Minification::XML_PATH_MINIFICATION_ENABLED, $extension), - 'default' - ); - } - } - return $result; - } } diff --git a/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php b/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php index 4cf5b0cc8c70c..3fa26b794c524 100644 --- a/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php +++ b/lib/internal/Magento/Framework/View/Asset/PreProcessor/Minify.php @@ -9,8 +9,6 @@ use Magento\Framework\View\Asset\Minification; use Magento\Framework\View\Asset\PreProcessor; use Magento\Framework\View\Asset\PreProcessorInterface; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Framework\App\State; /** * Assets minification pre-processor @@ -28,29 +26,23 @@ class Minify implements PreProcessorInterface protected $minification; /** - * @var State + * @var MinificationConfigProvider */ - private $appState; - - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; + private $minificationConfig; /** * @param AdapterInterface $adapter * @param Minification $minification + * @param MinificationConfigProvider $minificationConfig */ public function __construct( AdapterInterface $adapter, Minification $minification, - ScopeConfigInterface $scopeConfig, - State $appState + MinificationConfigProvider $minificationConfig ) { $this->adapter = $adapter; $this->minification = $minification; - $this->scopeConfig = $scopeConfig; - $this->appState = $appState; + $this->minificationConfig = $minificationConfig; } /** @@ -61,7 +53,7 @@ public function __construct( */ public function process(PreProcessor\Chain $chain) { - if ($this->isEnabledForArea($chain->getTargetAssetPath()) && + if ($this->minificationConfig->isMinificationEnabled($chain->getTargetAssetPath()) && $this->minification->isMinifiedFilename($chain->getTargetAssetPath()) && !$this->minification->isMinifiedFilename($chain->getOrigAssetPath()) ) { @@ -69,28 +61,4 @@ public function process(PreProcessor\Chain $chain) $chain->setContent($content); } } - - /** - * Check whether asset minification is on for specified content type and for area - * - * @param string $filename - * @return bool - */ - private function isEnabledForArea(string $filename): bool - { - $extension = pathinfo($filename, PATHINFO_EXTENSION); - $result = $this->minification->isEnabled($extension); - $pathParts = explode('/', $filename); - if (!empty($pathParts) && isset($pathParts[0])) { - $area = $pathParts[0]; - if ($area === 'adminhtml') { - $result = $this->appState->getMode() != State::MODE_DEVELOPER && - $this->scopeConfig->isSetFlag( - sprintf(Minification::XML_PATH_MINIFICATION_ENABLED, $extension), - 'default' - ); - } - } - return $result; - } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinificationFilenameResolverTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinificationFilenameResolverTest.php index bcd1d03a629ce..c398aa502bb5b 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinificationFilenameResolverTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinificationFilenameResolverTest.php @@ -7,6 +7,7 @@ use Magento\Framework\View\Asset\Minification; use Magento\Framework\View\Asset\PreProcessor\MinificationFilenameResolver; +use Magento\Framework\View\Asset\PreProcessor\MinificationConfigProvider; /** * Class MinificationFilenameResolverTest @@ -29,13 +30,15 @@ public function testResolve($isMin, $input, $expected) $minificationMock = $this->getMockBuilder(Minification::class) ->disableOriginalConstructor() ->getMock(); - - $minificationMock->expects(self::once()) - ->method('isEnabled') - ->with('ext') + $minificationConfigMock = $this->getMockBuilder(MinificationConfigProvider::class) + ->disableOriginalConstructor() + ->getMock(); + $minificationConfigMock->expects(self::once()) + ->method('isMinificationEnabled') + ->with($input) ->willReturn($isMin); - $resolver = new MinificationFilenameResolver($minificationMock); + $resolver = new MinificationFilenameResolver($minificationMock, $minificationConfigMock); self::assertEquals($expected, $resolver->resolve($input)); } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinifyTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinifyTest.php index 51ad9886d87a8..a46df9f27e19b 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinifyTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/PreProcessor/MinifyTest.php @@ -7,6 +7,7 @@ namespace Magento\Framework\View\Test\Unit\Asset\PreProcessor; use Magento\Framework\View\Asset\PreProcessor\Minify; +use Magento\Framework\View\Asset\PreProcessor\MinificationConfigProvider; /** * Unit test for Magento\Framework\View\Asset\PreProcessor\Minify @@ -28,6 +29,11 @@ class MinifyTest extends \PHPUnit\Framework\TestCase */ protected $minificationMock; + /** + * @var MinificationConfigProvider|\PHPUnit_Framework_MockObject_MockObject + */ + private $minificationConfigMock; + /** * {@inheritDoc} */ @@ -40,10 +46,14 @@ protected function setUp() $this->minificationMock = $this->getMockBuilder(\Magento\Framework\View\Asset\Minification::class) ->disableOriginalConstructor() ->getMock(); + $this->minificationConfigMock = $this->getMockBuilder(MinificationConfigProvider::class) + ->disableOriginalConstructor() + ->getMock(); $this->minify = new Minify( $this->adapterMock, - $this->minificationMock + $this->minificationMock, + $this->minificationConfigMock ); } @@ -84,10 +94,10 @@ public function testProcess($targetPath, $originalPath, $minifyCalls, $setConten ->with('original content') ->willReturn('minified content'); - $this->minificationMock + $this->minificationConfigMock ->expects($this->any()) - ->method('isEnabled') - ->willReturnMap([['css', $isEnabled]]); + ->method('isMinificationEnabled') + ->willReturnMap([[$targetPath, $isEnabled]]); $this->minificationMock ->expects($this->any()) From 4697d936287d368d9beb258c6c58bc868b13cd93 Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Thu, 28 Nov 2019 14:27:34 +0530 Subject: [PATCH 2249/2437] Fixed the usage of deprecated methods of Messagemanager --- .../Magento/Customer/Controller/Account/Confirm.php | 2 +- .../Customer/Controller/Account/CreatePost.php | 12 ++++++------ .../Magento/Customer/Controller/Account/EditPost.php | 6 +++--- .../Customer/Controller/Account/LoginPost.php | 6 +++--- .../Controller/Account/ResetPasswordPost.php | 12 ++++++------ .../Magento/Customer/Controller/Address/Delete.php | 4 ++-- .../Adminhtml/Customer/InvalidateToken.php | 6 +++--- .../Customer/Controller/Adminhtml/Group/Delete.php | 6 +++--- .../Customer/Controller/Adminhtml/Group/Save.php | 4 ++-- .../Adminhtml/Index/AbstractMassAction.php | 2 +- .../Customer/Controller/Adminhtml/Index/Delete.php | 6 +++--- .../Customer/Observer/AfterAddressSaveObserver.php | 6 +++--- .../Test/Unit/Controller/Account/ConfirmTest.php | 4 ++-- .../Test/Unit/Controller/Account/CreatePostTest.php | 4 ++-- .../Test/Unit/Controller/Account/LoginPostTest.php | 10 +++++----- .../Test/Unit/Controller/Address/DeleteTest.php | 4 ++-- .../Unit/Controller/Adminhtml/Group/SaveTest.php | 4 ++-- .../Controller/Adminhtml/Index/InlineEditTest.php | 4 ++-- .../Adminhtml/Index/MassAssignGroupTest.php | 2 +- .../Controller/Adminhtml/Index/MassDeleteTest.php | 2 +- .../Controller/Adminhtml/Index/MassSubscribeTest.php | 2 +- .../Adminhtml/Index/MassUnsubscribeTest.php | 2 +- .../Unit/Observer/AfterAddressSaveObserverTest.php | 6 +++--- 23 files changed, 58 insertions(+), 58 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index adca90c5e5f24..3aa08cfbf847e 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -168,7 +168,7 @@ public function execute() $metadata->setPath('/'); $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata); } - $this->messageManager->addSuccess($this->getSuccessMessage()); + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index a2be0f68b56cb..ebece9ef5a4a0 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -367,7 +367,7 @@ public function execute() if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); // @codingStandardsIgnoreStart - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __( 'You must confirm your account. Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.', $email @@ -378,7 +378,7 @@ public function execute() $resultRedirect->setUrl($this->_redirect->success($url)); } else { $this->session->setCustomerDataAsLoggedIn($customer); - $this->messageManager->addSuccess($this->getSuccessMessage()); + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); $requestedRedirect = $this->accountRedirect->getRedirectCookie(); if (!$this->scopeConfig->getValue('customer/startup/redirect_dashboard') && $requestedRedirect) { $resultRedirect->setUrl($this->_redirect->success($requestedRedirect)); @@ -402,14 +402,14 @@ public function execute() $url ); // @codingStandardsIgnoreEnd - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } catch (InputException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($this->escaper->escapeHtml($error->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (LocalizedException $e) { - $this->messageManager->addError($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); } catch (\Exception $e) { $this->messageManager->addException($e, __('We can\'t save the customer.')); } diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index 4eb41cedea29a..04b5b72ae776b 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -216,7 +216,7 @@ public function execute() $isPasswordChanged ); $this->dispatchSuccessEvent($customerCandidateDataObject); - $this->messageManager->addSuccess(__('You saved the account information.')); + $this->messageManager->addSuccessMessage(__('You saved the account information.')); return $resultRedirect->setPath('customer/account'); } catch (InvalidEmailOrPasswordException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -227,7 +227,7 @@ public function execute() ); $this->session->logout(); $this->session->start(); - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); return $resultRedirect->setPath('customer/account/login'); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -235,7 +235,7 @@ public function execute() $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addException($e, __('We can\'t save the customer.')); } diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 04051fbbf366b..a7632401933e6 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -217,17 +217,17 @@ public function execute() $message = $e->getMessage(); } catch (\Exception $e) { // PA DSS violation: throwing or logging an exception here can disclose customer password - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('An unspecified error occurred. Please contact us for assistance.') ); } finally { if (isset($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); $this->session->setUsername($login['username']); } } } else { - $this->messageManager->addError(__('A login and a password are required.')); + $this->messageManager->addErrorMessage(__('A login and a password are required.')); } } diff --git a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php index 27a00f86dd95d..a127f2acf538f 100644 --- a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php +++ b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php @@ -73,13 +73,13 @@ public function execute() $passwordConfirmation = (string)$this->getRequest()->getPost('password_confirmation'); if ($password !== $passwordConfirmation) { - $this->messageManager->addError(__("New Password and Confirm New Password values didn't match.")); + $this->messageManager->addErrorMessage(__("New Password and Confirm New Password values didn't match.")); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; } if (iconv_strlen($password) <= 0) { - $this->messageManager->addError(__('Please enter a new password.')); + $this->messageManager->addErrorMessage(__('Please enter a new password.')); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; @@ -92,17 +92,17 @@ public function execute() $password ); $this->session->unsRpToken(); - $this->messageManager->addSuccess(__('You updated your password.')); + $this->messageManager->addSuccessMessage(__('You updated your password.')); $resultRedirect->setPath('*/*/login'); return $resultRedirect; } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($error->getMessage()); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (\Exception $exception) { - $this->messageManager->addError(__('Something went wrong while saving the new password.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving the new password.')); } $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index a30e15db4b3f8..2024b2c58b8ef 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -27,9 +27,9 @@ public function execute() $address = $this->_addressRepository->getById($addressId); if ($address->getCustomerId() === $this->_getSession()->getCustomerId()) { $this->_addressRepository->deleteById($addressId); - $this->messageManager->addSuccess(__('You deleted the address.')); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); } else { - $this->messageManager->addError(__('We can\'t delete the address right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the address right now.')); } } catch (\Exception $other) { $this->messageManager->addException($other, __('We can\'t delete the address right now.')); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php index b69410ecbfce7..7747d80595cdc 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php @@ -139,14 +139,14 @@ public function execute() if ($customerId = $this->getRequest()->getParam('customer_id')) { try { $this->tokenService->revokeCustomerAccessToken($customerId); - $this->messageManager->addSuccess(__('You have revoked the customer\'s tokens.')); + $this->messageManager->addSuccessMessage(__('You have revoked the customer\'s tokens.')); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } } else { - $this->messageManager->addError(__('We can\'t find a customer to revoke.')); + $this->messageManager->addErrorMessage(__('We can\'t find a customer to revoke.')); $resultRedirect->setPath('customer/index/index'); } return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index ab32ea08a44aa..819a49178a24d 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -24,12 +24,12 @@ public function execute() if ($id) { try { $this->groupRepository->deleteById($id); - $this->messageManager->addSuccess(__('You deleted the customer group.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer group.')); } catch (NoSuchEntityException $e) { - $this->messageManager->addError(__('The customer group no longer exists.')); + $this->messageManager->addErrorMessage(__('The customer group no longer exists.')); return $resultRedirect->setPath('customer/*/'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('customer/group/edit', ['id' => $id]); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 5ffce4cbcd989..64c94fa230fb1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -93,10 +93,10 @@ public function execute() $this->groupRepository->save($customerGroup); - $this->messageManager->addSuccess(__('You saved the customer group.')); + $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index e26b49aaebe7a..08c6e5148ade5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -64,7 +64,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php index ab39ca098162f..4b2f2614948cf 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php @@ -31,7 +31,7 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addError(__('Customer could not be deleted.')); + $this->messageManager->addErrorMessage(__('Customer could not be deleted.')); return $resultRedirect->setPath('customer/index'); } @@ -39,9 +39,9 @@ public function execute() if (!empty($customerId)) { try { $this->_customerRepository->deleteById($customerId); - $this->messageManager->addSuccess(__('You deleted the customer.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer.')); } catch (\Exception $exception) { - $this->messageManager->addError($exception->getMessage()); + $this->messageManager->addErrorMessage($exception->getMessage()); } } diff --git a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php index 41311abee5da8..8677abfa89904 100644 --- a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php +++ b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php @@ -255,7 +255,7 @@ protected function addValidMessage($customerAddress, $validationResult) : (string)__('You will not be charged tax.'); } - $this->messageManager->addSuccess(implode(' ', $message)); + $this->messageManager->addSuccessMessage(implode(' ', $message)); return $this; } @@ -280,7 +280,7 @@ protected function addInvalidMessage($customerAddress) $message[] = (string)__('You will be charged tax.'); } - $this->messageManager->addError(implode(' ', $message)); + $this->messageManager->addErrorMessage(implode(' ', $message)); return $this; } @@ -307,7 +307,7 @@ protected function addErrorMessage($customerAddress) $email = $this->scopeConfig->getValue('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE); $message[] = (string)__('If you believe this is an error, please contact us at %1', $email); - $this->messageManager->addError(implode(' ', $message)); + $this->messageManager->addErrorMessage(implode(' ', $message)); return $this; } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 28f897adf9176..5565a807b8135 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -282,7 +282,7 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); @@ -402,7 +402,7 @@ public function testSuccessRedirect( ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php index f8f47eedba3ef..ac52c395d6787 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/CreatePostTest.php @@ -371,7 +371,7 @@ public function testSuccessMessage( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); @@ -502,7 +502,7 @@ public function testSuccessRedirect( ->with($this->equalTo($customerId)); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->will($this->returnSelf()); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 762c76b695dee..51b84d807dc13 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -222,7 +222,7 @@ public function testExecuteEmptyLoginData() ->willReturn([]); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('A login and a password are required.')) ->willReturnSelf(); @@ -551,7 +551,7 @@ protected function mockExceptions($exception, $username) $url ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); @@ -563,7 +563,7 @@ protected function mockExceptions($exception, $username) case \Magento\Framework\Exception\AuthenticationException::class: $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' @@ -580,7 +580,7 @@ protected function mockExceptions($exception, $username) case '\Exception': $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('An unspecified error occurred. Please contact us for assistance.')) ->willReturnSelf(); break; @@ -591,7 +591,7 @@ protected function mockExceptions($exception, $username) . 'Please wait and try again later.' ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); $this->session->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 4064b8586257d..3af3cc60010bb 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -146,7 +146,7 @@ public function testExecute() ->method('deleteById') ->with($addressId); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You deleted the address.')); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -183,7 +183,7 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t delete the address right now.')) ->willThrowException($exception); $this->messageManager->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index 5f7064d5b124b..c9f885315b0ef 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -167,7 +167,7 @@ public function testExecuteWithTaxClassAndException() ->method('save') ->with($this->group); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the customer group.')); $exception = new \Exception('Exception'); $this->resultRedirect->expects($this->at(0)) @@ -175,7 +175,7 @@ public function testExecuteWithTaxClassAndException() ->with('customer/group') ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Exception'); $this->dataObjectProcessorMock->expects($this->once()) ->method('buildOutputDataArray') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index 45e64f6557d51..c198eb3a212fa 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -366,7 +366,7 @@ public function testExecuteLocalizedException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('[Customer ID: 12] Exception message'); $this->logger->expects($this->once()) ->method('critical') @@ -394,7 +394,7 @@ public function testExecuteException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('[Customer ID: 12] We can\'t save the customer.'); $this->logger->expects($this->once()) ->method('critical') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php index cb5ff88ab704a..4157359959ae4 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -199,7 +199,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php index 1f39e6306b996..b436b5b137c78 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php @@ -179,7 +179,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index 90bff0b61bcbf..33e578224400b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 1bffa836f5034..971efc0e490bc 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -195,7 +195,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 8592d1bda66c1..4501b611aa11f 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -575,7 +575,7 @@ public function testAfterAddressSaveNewGroup( if ($resultValidMessage) { $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($resultValidMessage) ->willReturnSelf(); } @@ -585,7 +585,7 @@ public function testAfterAddressSaveNewGroup( ->with($vatId) ->willReturn($vatId); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($resultInvalidMessage) ->willReturnSelf(); } @@ -595,7 +595,7 @@ public function testAfterAddressSaveNewGroup( ->with('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE) ->willReturn('admin@example.com'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($resultErrorMessage) ->willReturnSelf(); } From 097228c7f2aba5cef9b079225940c7878892dfc5 Mon Sep 17 00:00:00 2001 From: Maximilian Fickers <m.fickers@basecom.de> Date: Thu, 28 Nov 2019 11:34:40 +0100 Subject: [PATCH 2250/2437] #20463 Fix order summary view details label misalign --- .../luma/Magento_Checkout/web/css/source/module/_minicart.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index c5788d9c6fa51..43ccee738a45e 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -315,7 +315,8 @@ .toggle { &:extend(.abs-toggling-title all); border: 0; - padding: 0 @indent__m @indent__xs 0; + padding: 0 0 @indent__xs 0; + white-space: nowrap; &:after { .lib-css(color, @color-gray56); From a9d15943f0f6f35a1d42eee15c5b992ed66810ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torben=20Ho=CC=88hn?= <torhoehn@gmail.com> Date: Thu, 28 Nov 2019 11:58:48 +0100 Subject: [PATCH 2251/2437] add unit test for attribute creation --- .../Product/Attribute/ValidateTest.php | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php index 742148b1bf7f1..4f797cdd763c0 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php @@ -439,6 +439,127 @@ public function provideEmptyOption() ]; } + /** + * Check that admin scope labels which only contain spaces will trigger error. + * + * @dataProvider provideWhitespaceOption + * @param array $options + * @param $result + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function testWhitespaceOption(array $options, $result) + { + $serializedOptions = '{"key":"value"}'; + $this->requestMock->expects($this->any()) + ->method('getParam') + ->willReturnMap([ + ['frontend_label', null, null], + ['frontend_input', 'select', 'multipleselect'], + ['attribute_code', null, "test_attribute_code"], + ['new_attribute_set_name', null, 'test_attribute_set_name'], + ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'], + ['serialized_options', '[]', $serializedOptions], + ]); + + $this->formDataSerializerMock + ->expects($this->once()) + ->method('unserialize') + ->with($serializedOptions) + ->willReturn($options); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->willReturn($this->attributeMock); + + $this->attributeMock->expects($this->once()) + ->method('loadByCode') + ->willReturnSelf(); + + $this->attributeCodeValidatorMock->expects($this->once()) + ->method('isValid') + ->with('test_attribute_code') + ->willReturn(true); + + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->resultJson); + + $this->resultJson->expects($this->once()) + ->method('setJsonData') + ->willReturnArgument(0); + + $response = $this->getModel()->execute(); + $responseObject = json_decode($response); + $this->assertEquals($responseObject, $result); + } + + /** + * Dataprovider for testWhitespaceOption. + * + * @return array + */ + public function provideWhitespaceOption() + { + return [ + 'whitespace admin scope options' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + ], + ], + (object) [ + 'error' => true, + 'message' => 'The value of Admin scope can\'t be empty.', + ] + ], + 'not empty admin scope options' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => ['asdads'], + ], + ], + ], + (object) [ + 'error' => false, + ] + ], + 'whitespace admin scope options and deleted' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + 'delete' => [ + 'option_0' => '1', + ], + ], + ], + (object) [ + 'error' => false, + ], + ], + 'whitespace admin scope options and not deleted' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + 'delete' => [ + 'option_0' => '0', + ], + ], + ], + (object) [ + 'error' => true, + 'message' => 'The value of Admin scope can\'t be empty.', + ], + ], + ]; + } + /** * @throws \Magento\Framework\Exception\NotFoundException */ From c71d695be0142a370fb220599282f62ff425153b Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Thu, 28 Nov 2019 16:59:29 +0530 Subject: [PATCH 2252/2437] Unit test case change reverted --- .../Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php index c198eb3a212fa..45e64f6557d51 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php @@ -366,7 +366,7 @@ public function testExecuteLocalizedException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('[Customer ID: 12] Exception message'); $this->logger->expects($this->once()) ->method('critical') @@ -394,7 +394,7 @@ public function testExecuteException() ->with($this->customerData) ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with('[Customer ID: 12] We can\'t save the customer.'); $this->logger->expects($this->once()) ->method('critical') From 9abcf547c0d5160de4cfa0c5e3b907056fb8f311 Mon Sep 17 00:00:00 2001 From: Maximilian Fickers <m.fickers@basecom.de> Date: Thu, 28 Nov 2019 13:05:28 +0100 Subject: [PATCH 2253/2437] #25042 Remove logo dimension parameters from layout files for blank and luma theme --- app/code/Magento/Theme/Block/Html/Header/Logo.php | 4 ++-- app/code/Magento/Theme/view/frontend/layout/default.xml | 7 +------ .../frontend/Magento/luma/Magento_Theme/layout/default.xml | 6 ------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Theme/Block/Html/Header/Logo.php b/app/code/Magento/Theme/Block/Html/Header/Logo.php index b51f624c20339..626a771b4e309 100644 --- a/app/code/Magento/Theme/Block/Html/Header/Logo.php +++ b/app/code/Magento/Theme/Block/Html/Header/Logo.php @@ -98,7 +98,7 @@ public function getLogoWidth() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } - return (int)$this->_data['logo_width'] ? : (int)$this->getLogoImgWidth(); + return (int)$this->_data['logo_width']; } /** @@ -114,7 +114,7 @@ public function getLogoHeight() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } - return (int)$this->_data['logo_height'] ? : (int)$this->getLogoImgHeight(); + return (int)$this->_data['logo_height']; } /** diff --git a/app/code/Magento/Theme/view/frontend/layout/default.xml b/app/code/Magento/Theme/view/frontend/layout/default.xml index 81cffe8c040b3..8eaac4aa3e794 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default.xml @@ -50,12 +50,7 @@ </container> </container> <container name="header-wrapper" label="Page Header" as="header-wrapper" htmlTag="div" htmlClass="header content"> - <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"> - <arguments> - <argument name="logo_img_width" xsi:type="number">189</argument> - <argument name="logo_img_height" xsi:type="number">64</argument> - </arguments> - </block> + <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"/> </container> </referenceContainer> <referenceContainer name="page.top"> diff --git a/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml b/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml index 5f0f3b92ab44a..ce397fad64f44 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml +++ b/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml @@ -14,12 +14,6 @@ </arguments> </block> </referenceContainer> - <referenceBlock name="logo"> - <arguments> - <argument name="logo_img_width" xsi:type="number">148</argument> - <argument name="logo_img_height" xsi:type="number">43</argument> - </arguments> - </referenceBlock> <referenceContainer name="footer"> <block class="Magento\Store\Block\Switcher" name="store_switcher" as="store_switcher" after="footer_links" template="Magento_Store::switch/stores.phtml"/> </referenceContainer> From 16e6fdace4f1e297a43fe396b4d8f45432e82702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 28 Nov 2019 13:40:58 +0100 Subject: [PATCH 2254/2437] REFACTOR: Extract Action Groups to separate files (according to MFTF best practices) --- ...eDynamicBlocksRotatorWidgetActionGroup.xml | 21 +++++ ...dminCreateProductLinkWidgetActionGroup.xml | 28 ++++++ ...minCreateProductsListWidgetActionGroup.xml | 27 ++++++ .../AdminCreateWidgetActionGroup.xml | 94 ------------------- .../AdminDeleteWidgetActionGroup.xml | 32 +++++++ ...pecificPageWidgetMainFieldsActionGroup.xml | 36 +++++++ 6 files changed, 144 insertions(+), 94 deletions(-) create mode 100644 app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml create mode 100644 app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml create mode 100644 app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml create mode 100644 app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml create mode 100644 app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml new file mode 100644 index 0000000000000..16efe55202bb0 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateDynamicBlocksRotatorWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Dynamic Block Rotate Widget.</description> + </annotations> + + <selectOption selector="{{AdminNewWidgetSection.displayMode}}" userInput="{{widget.display_mode}}" stepKey="selectDisplayMode"/> + <selectOption selector="{{AdminNewWidgetSection.restrictTypes}}" userInput="{{widget.restrict_type}}" stepKey="selectRestrictType"/> + <click selector="{{AdminNewWidgetSection.saveAndContinue}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml new file mode 100644 index 0000000000000..cb82f5ad068fd --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateProductLinkWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget using the provided Product. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> + <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> + <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> + <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> + <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml new file mode 100644 index 0000000000000..e3845adc9cd4a --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget. Validates that the Success Message is present and correct.</description> + </annotations> + + <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> + <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> + <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> + <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index d304b1876b7e3..e657b3eb73b53 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -30,98 +30,4 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> </actionGroup> - <actionGroup name="AdminFillSpecificPageWidgetMainFieldsActionGroup"> - <annotations> - <description>Fill widget main fields and widget layout by index for specified page DisplayOn option</description> - </annotations> - <arguments> - <argument name="widget" type="entity" defaultValue="ProductsListWidget"/> - <argument name="index" type="string" defaultValue="0"/> - </arguments> - <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> - <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> - <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> - <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" parameterArray="{{widget.store_ids}}" stepKey="setWidgetStoreIds"/> - <fillField selector="{{AdminNewWidgetSection.widgetSortOrder}}" userInput="{{widget.sort_order}}" stepKey="fillSortOrder"/> - <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> - <waitForElementVisible selector="{{AdminNewWidgetSection.selectDisplayOn}}" stepKey="waitForSelectElement"/> - <selectOption selector="{{AdminNewWidgetSection.displayOnByIndex(index)}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> - <waitForPageLoad stepKey="waitForDisplayOnChangesApplied"/> - <selectOption selector="{{AdminNewWidgetSection.layoutByIndex(index)}}" userInput="{{widget.page}}" stepKey="selectPage"/> - <selectOption selector="{{AdminNewWidgetSection.templateByIndex(index)}}" userInput="{{widget.template}}" stepKey="selectTemplate"/> - <scrollTo selector="{{AdminNewWidgetSection.containerByIndex(index)}}" stepKey="scrollToSelectContainerElement"/> - <waitForPageLoad stepKey="waitForScroll"/> - <selectOption selector="{{AdminNewWidgetSection.containerByIndex(index)}}" userInput="{{widget.container}}" stepKey="setContainer"/> - <waitForPageLoad stepKey="waitForContainerChangesApplied"/> - </actionGroup> - <!--Create Product List Widget--> - <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget. Validates that the Success Message is present and correct.</description> - </annotations> - - <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> - <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> - <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> - <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> - <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> - <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> - - <!--Create Dynamic Block Rotate Widget--> - <actionGroup name="AdminCreateDynamicBlocksRotatorWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Dynamic Block Rotate Widget.</description> - </annotations> - - <selectOption selector="{{AdminNewWidgetSection.displayMode}}" userInput="{{widget.display_mode}}" stepKey="selectDisplayMode"/> - <selectOption selector="{{AdminNewWidgetSection.restrictTypes}}" userInput="{{widget.restrict_type}}" stepKey="selectRestrictType"/> - <click selector="{{AdminNewWidgetSection.saveAndContinue}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> - - <actionGroup name="AdminDeleteWidgetActionGroup"> - <annotations> - <description>Goes to the Admin Widget grid page. Deletes the provided Widget. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="widget"/> - </arguments> - - <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> - <waitForPageLoad stepKey="waitWidgetsLoad"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> - <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> - <waitForPageLoad stepKey="waitForResultLoad"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForDeleteLoad"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> - </actionGroup> - - <actionGroup name="AdminCreateProductLinkWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget using the provided Product. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> - <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> - <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> - <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> - <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml new file mode 100644 index 0000000000000..889c8a9477534 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteWidgetActionGroup"> + <annotations> + <description>Goes to the Admin Widget grid page. Deletes the provided Widget. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="widget"/> + </arguments> + + <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> + <waitForPageLoad stepKey="waitWidgetsLoad"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> + <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> + <waitForPageLoad stepKey="waitForResultLoad"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteLoad"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml new file mode 100644 index 0000000000000..c0fa53da7c688 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillSpecificPageWidgetMainFieldsActionGroup"> + <annotations> + <description>Fill widget main fields and widget layout by index for specified page DisplayOn option</description> + </annotations> + <arguments> + <argument name="widget" type="entity" defaultValue="ProductsListWidget"/> + <argument name="index" type="string" defaultValue="0"/> + </arguments> + <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> + <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> + <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> + <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" parameterArray="{{widget.store_ids}}" stepKey="setWidgetStoreIds"/> + <fillField selector="{{AdminNewWidgetSection.widgetSortOrder}}" userInput="{{widget.sort_order}}" stepKey="fillSortOrder"/> + <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.selectDisplayOn}}" stepKey="waitForSelectElement"/> + <selectOption selector="{{AdminNewWidgetSection.displayOnByIndex(index)}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> + <waitForPageLoad stepKey="waitForDisplayOnChangesApplied"/> + <selectOption selector="{{AdminNewWidgetSection.layoutByIndex(index)}}" userInput="{{widget.page}}" stepKey="selectPage"/> + <selectOption selector="{{AdminNewWidgetSection.templateByIndex(index)}}" userInput="{{widget.template}}" stepKey="selectTemplate"/> + <scrollTo selector="{{AdminNewWidgetSection.containerByIndex(index)}}" stepKey="scrollToSelectContainerElement"/> + <waitForPageLoad stepKey="waitForScroll"/> + <selectOption selector="{{AdminNewWidgetSection.containerByIndex(index)}}" userInput="{{widget.container}}" stepKey="setContainer"/> + <waitForPageLoad stepKey="waitForContainerChangesApplied"/> + </actionGroup> +</actionGroups> From 35f470c05ddbe439960903957798f5d9a8cf8999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 28 Nov 2019 13:43:42 +0100 Subject: [PATCH 2255/2437] REFACTOR: Extract Action Groups to separate files (according to MFTF best practices) --- .../CreateCustomVariableActionGroup.xml | 16 ------------ .../DeleteCustomVariableActionGroup.xml | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml diff --git a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml index 1aa9a3fa6c6ab..75f09b478170e 100644 --- a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml +++ b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml @@ -21,20 +21,4 @@ <fillField selector="{{CustomVariableSection.variablePlain}}" userInput="{{customVariable.plain}}" stepKey="fillVariablePlain"/> <click selector="{{CustomVariableSection.saveCustomVariable}}" stepKey="clickSaveVariable"/> </actionGroup> - - <actionGroup name="DeleteCustomVariableActionGroup"> - <annotations> - <description>Goes to the Custom Variable grid page. Deletes the Custom Variable. PLEASE NOTE: The Custom Variable that is deleted is Hardcoded using 'customVariable'.</description> - </annotations> - - <amOnPage url="admin/admin/system_variable/" stepKey="goToVarialeGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{CustomVariableSection.GridCustomVariableCode(customVariable.code)}}" stepKey="goToCustomVariableEditPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForElementVisible selector="{{CustomVariableSection.delete}}" stepKey="waitForDeleteBtn"/> - <click selector="{{CustomVariableSection.delete}}" stepKey="deleteCustomVariable"/> - <waitForText userInput="Are you sure you want to do this?" stepKey="waitForText"/> - <click selector="{{CustomVariableSection.confirmDelete}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml b/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml new file mode 100644 index 0000000000000..df77d7d1fb9d0 --- /dev/null +++ b/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomVariableActionGroup"> + <annotations> + <description>Goes to the Custom Variable grid page. Deletes the Custom Variable. PLEASE NOTE: The Custom Variable that is deleted is Hardcoded using 'customVariable'.</description> + </annotations> + + <amOnPage url="admin/admin/system_variable/" stepKey="goToVarialeGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CustomVariableSection.GridCustomVariableCode(customVariable.code)}}" stepKey="goToCustomVariableEditPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForElementVisible selector="{{CustomVariableSection.delete}}" stepKey="waitForDeleteBtn"/> + <click selector="{{CustomVariableSection.delete}}" stepKey="deleteCustomVariable"/> + <waitForText userInput="Are you sure you want to do this?" stepKey="waitForText"/> + <click selector="{{CustomVariableSection.confirmDelete}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + </actionGroup> +</actionGroups> From f15a22068807efd413de7826fd38ea0e8da2a8f2 Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Thu, 28 Nov 2019 19:15:51 +0530 Subject: [PATCH 2256/2437] PHPDoc block updates & reverted observer fix --- .../Magento/Customer/Controller/Adminhtml/Group/Delete.php | 4 +++- .../Magento/Customer/Observer/AfterAddressSaveObserver.php | 6 +++--- .../Test/Unit/Observer/AfterAddressSaveObserverTest.php | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index 819a49178a24d..661ef1cace69b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -9,6 +8,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; +/** + * Class Delete + */ class Delete extends \Magento\Customer\Controller\Adminhtml\Group implements HttpPostActionInterface { /** diff --git a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php index 8677abfa89904..41311abee5da8 100644 --- a/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php +++ b/app/code/Magento/Customer/Observer/AfterAddressSaveObserver.php @@ -255,7 +255,7 @@ protected function addValidMessage($customerAddress, $validationResult) : (string)__('You will not be charged tax.'); } - $this->messageManager->addSuccessMessage(implode(' ', $message)); + $this->messageManager->addSuccess(implode(' ', $message)); return $this; } @@ -280,7 +280,7 @@ protected function addInvalidMessage($customerAddress) $message[] = (string)__('You will be charged tax.'); } - $this->messageManager->addErrorMessage(implode(' ', $message)); + $this->messageManager->addError(implode(' ', $message)); return $this; } @@ -307,7 +307,7 @@ protected function addErrorMessage($customerAddress) $email = $this->scopeConfig->getValue('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE); $message[] = (string)__('If you believe this is an error, please contact us at %1', $email); - $this->messageManager->addErrorMessage(implode(' ', $message)); + $this->messageManager->addError(implode(' ', $message)); return $this; } diff --git a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php index 4501b611aa11f..8592d1bda66c1 100644 --- a/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Observer/AfterAddressSaveObserverTest.php @@ -575,7 +575,7 @@ public function testAfterAddressSaveNewGroup( if ($resultValidMessage) { $this->messageManager->expects($this->once()) - ->method('addSuccessMessage') + ->method('addSuccess') ->with($resultValidMessage) ->willReturnSelf(); } @@ -585,7 +585,7 @@ public function testAfterAddressSaveNewGroup( ->with($vatId) ->willReturn($vatId); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($resultInvalidMessage) ->willReturnSelf(); } @@ -595,7 +595,7 @@ public function testAfterAddressSaveNewGroup( ->with('trans_email/ident_support/email', ScopeInterface::SCOPE_STORE) ->willReturn('admin@example.com'); $this->messageManager->expects($this->once()) - ->method('addErrorMessage') + ->method('addError') ->with($resultErrorMessage) ->willReturnSelf(); } From 50be43cb0bb17729998f7af113473163751d56ed Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Thu, 28 Nov 2019 19:40:43 +0530 Subject: [PATCH 2257/2437] Static test issue fix --- .../Customer/Controller/Adminhtml/Index/AbstractMassAction.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index 08c6e5148ade5..8ba3c4416f8a7 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -73,6 +73,7 @@ public function execute() /** * Return component referer url + * * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string From bde67879529a3e5a543658b3c1b16fbac3bc79e1 Mon Sep 17 00:00:00 2001 From: Ajith <ajithkumar.maragathavel@ziffity.com> Date: Thu, 28 Nov 2019 19:46:18 +0530 Subject: [PATCH 2258/2437] No marginal white space validation added --- .../Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml index ad0f33df59d4e..a2ce0ec1b8740 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml @@ -105,6 +105,7 @@ <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="no-marginal-whitespace" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">Identifier</label> From 598ab813e77f347d5bd44b2ed53795278737ffc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 28 Nov 2019 15:28:47 +0100 Subject: [PATCH 2259/2437] REFACTOR: Extract Action Groups to separate files (according to MFTF best practices) --- ...leRatesShippingMethodStatusActionGroup.xml | 10 --------- ...ileTableRatesShippingMethodActionGroup.xml | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml index e0fec2a6dc4d2..e506ca3a7662f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml @@ -17,14 +17,4 @@ <uncheckOption selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" stepKey="uncheckUseSystemValue"/> <selectOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" userInput="{{status}}" stepKey="changeTableRatesMethodStatus"/> </actionGroup> - <actionGroup name="AdminImportFileTableRatesShippingMethodActionGroup"> - <annotations> - <description>Import a file in Table Rates tab in Shipping Method config page.</description> - </annotations> - <arguments> - <argument name="file" type="string" defaultValue="test_tablerates.csv"/> - </arguments> - <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> - <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="{{file}}" stepKey="attachFileForImport"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..bfae3d8b76f19 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminImportFileTableRatesShippingMethodActionGroup"> + <annotations> + <description>Import a file in Table Rates tab in Shipping Method config page.</description> + </annotations> + <arguments> + <argument name="file" type="string" defaultValue="test_tablerates.csv"/> + </arguments> + <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> + <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="{{file}}" stepKey="attachFileForImport"/> + </actionGroup> +</actionGroups> From c22df930468840b4212821b26bdebc0911c7c44f Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Thu, 28 Nov 2019 20:15:58 +0530 Subject: [PATCH 2260/2437] static test whitespace removed --- .../Customer/Controller/Adminhtml/Index/AbstractMassAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index 8ba3c4416f8a7..e2bde42351d45 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -73,7 +73,7 @@ public function execute() /** * Return component referer url - * + * * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string From 665c525d26b4f53a81888271b4f4712599673a8e Mon Sep 17 00:00:00 2001 From: Kostiantyn Lozhkin <k@kml.email> Date: Thu, 28 Nov 2019 16:03:32 +0100 Subject: [PATCH 2261/2437] magento/adobe-stock-integration#584: DataExtractor unit tests --- .../Test/Unit/Model/DataExtractorTest.php | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php new file mode 100644 index 0000000000000..b64834e5decee --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset; +use Magento\MediaGallery\Model\Keyword; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGallery\Model\DataExtractor; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class DataExtractorTest extends TestCase +{ + /** + * @var DataExtractor|MockObject + */ + private $dataExtractor; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->dataExtractor = new DataExtractor(); + } + + /** + * Test extract object data by interface + * + * @dataProvider assetProvider + * + * @param string $class + * @param string|null $interfaceClass + * @param array $expectedData + * + * @throws \ReflectionException + */ + public function testExtractData(string $class, $interfaceClass, array $expectedData): void + { + $data = []; + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $data[$expectedDataKey] = $expectedDataItem['value']; + } + $model = (new ObjectManager($this))->getObject( + $class, + [ + 'data' => $data, + ] + ); + $receivedData = $this->dataExtractor->extract($model, $interfaceClass); + $this->checkValues($expectedData, $receivedData, $model); + } + + /** + * @param array $expectedData + * @param array $data + * @param object $model + */ + protected function checkValues(array $expectedData, array $data, $model) + { + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); + $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); + } + $this->assertEquals(array_keys($expectedData), array_keys($expectedData)); + } + + /** + * @return array + */ + public function assetProvider() + { + return [ + 'Test case asset 1' => [ + Asset::class, + null, + [ + 'id' => [ + 'value' => 1, + 'method' => 'getId', + ], + 'path' => [ + 'value' => 'path', + 'method' => 'getPath', + ], + 'title' => [ + 'value' => 'title', + 'method' => 'getTitle', + ], + 'source' => [ + 'value' => 'source', + 'method' => 'getSource', + ], + 'content_type' => [ + 'value' => 'content_type', + 'method' => 'getContentType', + ], + 'width' => [ + 'value' => 1, + 'method' => 'getWidth', + ], + 'height' => [ + 'value' => 2, + 'method' => 'getHeight', + ], + 'created_at' => [ + 'value' => '2019-11-28 10:40:09', + 'method' => 'getCreatedAt', + ], + 'updated_at' => [ + 'value' => '2019-11-28 10:41:08', + 'method' => 'getUpdatedAt', + ], + ], + ], + 'Test case asset 2' => [ + Asset::class, + AssetInterface::class, + [ + 'id' => [ + 'value' => 2, + 'method' => 'getId', + ], + 'path' => [ + 'value' => 'path', + 'method' => 'getPath', + ], + 'title' => [ + 'value' => 'title', + 'method' => 'getTitle', + ], + 'source' => [ + 'value' => 'source', + 'method' => 'getSource', + ], + 'content_type' => [ + 'value' => 'content_type', + 'method' => 'getContentType', + ], + 'width' => [ + 'value' => 3, + 'method' => 'getWidth', + ], + 'height' => [ + 'value' => 4, + 'method' => 'getHeight', + ], + 'created_at' => [ + 'value' => '2019-11-28 10:40:09', + 'method' => 'getCreatedAt', + ], + 'updated_at' => [ + 'value' => '2019-11-28 10:41:08', + 'method' => 'getUpdatedAt', + ], + ], + ], + 'Test case keyword 1' => [ + Keyword::class, + null, + [ + 'id' => [ + 'value' => 2, + 'method' => 'getId', + ], + 'keyword' => [ + 'value' => 'keyword', + 'method' => 'getKeyword', + ], + ], + ], + 'Test case keyword 2' => [ + Keyword::class, + KeywordInterface::class, + [ + 'id' => [ + 'value' => 3, + 'method' => 'getId', + ], + 'keyword' => [ + 'value' => 'keyword2', + 'method' => 'getKeyword', + ], + ], + ], + ]; + } +} From df6c294861e48ee81dcf4de0c1dec3986d6c09bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 28 Nov 2019 17:04:36 +0100 Subject: [PATCH 2262/2437] REFACTOR: Extract Action Groups to separate files (according to MFTF best practices) --- .../GenerateOrderReportActionGroup.xml | 20 +------------ ...rateOrderReportForNotCancelActionGroup.xml | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml index 29bbe91afbd11..1250c7b0ac370 100644 --- a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml @@ -16,29 +16,11 @@ <argument name="orderFromDate" type="string"/> <argument name="orderToDate" type="string"/> </arguments> - + <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Any" stepKey="selectAnyOption"/> <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> </actionGroup> - - <actionGroup name="GenerateOrderReportForNotCancelActionGroup"> - <annotations> - <description>Clicks on 'here' to refresh the grid data. Enters the provided Order From/To Dates and provided Order Status. Clicks on 'Show Report'.</description> - </annotations> - <arguments> - <argument name="orderFromDate" type="string"/> - <argument name="orderToDate" type="string"/> - <argument name="statuses" type="string"/> - </arguments> - - <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> - <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> - <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> - <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Specified" stepKey="selectSpecifiedOption"/> - <selectOption selector="{{OrderReportFilterSection.orderStatusSpecified}}" parameterArray="{{statuses}}" stepKey="selectSpecifiedOptionStatus"/> - <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml new file mode 100644 index 0000000000000..32c82308c8f99 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GenerateOrderReportForNotCancelActionGroup"> + <annotations> + <description>Clicks on 'here' to refresh the grid data. Enters the provided Order From/To Dates and provided Order Status. Clicks on 'Show Report'.</description> + </annotations> + <arguments> + <argument name="orderFromDate" type="string"/> + <argument name="orderToDate" type="string"/> + <argument name="statuses" type="string"/> + </arguments> + + <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> + <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> + <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> + <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Specified" stepKey="selectSpecifiedOption"/> + <selectOption selector="{{OrderReportFilterSection.orderStatusSpecified}}" parameterArray="{{statuses}}" stepKey="selectSpecifiedOptionStatus"/> + <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> + </actionGroup> +</actionGroups> From 91584fc4b7cb4d600a9846ad6943fa53fda46a6c Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Fri, 29 Nov 2019 00:07:07 +0530 Subject: [PATCH 2263/2437] Group save error message with escaped html --- .../Customer/Controller/Adminhtml/Group/Save.php | 12 ++++++++++-- .../Unit/Controller/Adminhtml/Group/SaveTest.php | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 64c94fa230fb1..fd6f0ed7daf54 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -20,6 +20,11 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Group implements HttpP */ protected $dataObjectProcessor; + /** + * @var \Magento\Framework\Escaper + */ + protected $escaper; + /** * * @param \Magento\Backend\App\Action\Context $context @@ -29,6 +34,7 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Group implements HttpP * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + * @param \Magento\Framework\Escaper $escaper */ public function __construct( \Magento\Backend\App\Action\Context $context, @@ -37,9 +43,11 @@ public function __construct( GroupInterfaceFactory $groupDataFactory, \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, + \Magento\Framework\Escaper $escaper ) { $this->dataObjectProcessor = $dataObjectProcessor; + $this->escaper = $escaper; parent::__construct( $context, $coreRegistry, @@ -96,7 +104,7 @@ public function execute() $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index c9f885315b0ef..7edc631fc71c1 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -87,6 +87,10 @@ protected function setUp() $this->dataObjectProcessorMock = $this->getMockBuilder(DataObjectProcessor::class) ->disableOriginalConstructor() ->getMock(); + $this->escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) ->getMockForAbstractClass(); $this->resultRedirectFactory = $this->getMockBuilder(RedirectFactory::class) @@ -129,7 +133,8 @@ protected function setUp() $this->groupInterfaceFactoryMock, $this->forwardFactoryMock, $this->pageFactoryMock, - $this->dataObjectProcessorMock + $this->dataObjectProcessorMock, + $this->escaper ); } From e6926df80eb7f180b55d2b135156de48c6ab1599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 28 Nov 2019 21:55:41 +0100 Subject: [PATCH 2264/2437] REFACTOR: Extract Action Groups to separate files (according to MFTF best practices) --- ...inCheckDataForImportProductActionGroup.xml | 25 +++++++++++++++++++ .../AdminImportProductsActionGroup.xml | 24 ------------------ ...tsWithCheckValidationResultActionGroup.xml | 20 +++++++++++++++ 3 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml create mode 100644 app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml new file mode 100644 index 0000000000000..8628d246248b1 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckDataForImportProductActionGroup"> + <arguments> + <argument name="behavior" type="string" defaultValue="Add/Update"/> + <argument name="importFile" type="string"/> + </arguments> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="adminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportBehaviorOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 12da5974bdbe8..956822fc3cbef 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -36,28 +36,4 @@ <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{importNoticeMessage}}" stepKey="seeNoticeMessage"/> <see selector="{{AdminImportValidationMessagesSection.messageByType(importMessageType)}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> </actionGroup> - - <actionGroup name="AdminImportProductsWithCheckValidationResultActionGroup" extends="AdminImportProductsActionGroup"> - <arguments> - <argument name="validationNoticeMessage" type="string"/> - <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> - </arguments> - <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" after="clickCheckDataButton" stepKey="waitForValidationNoticeMessage"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{validationNoticeMessage}}" after="waitForValidationNoticeMessage" stepKey="seeValidationNoticeMessage"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="{{validationMessage}}" after="seeValidationNoticeMessage" stepKey="seeValidationMessage"/> - </actionGroup> - <actionGroup name="AdminCheckDataForImportProductActionGroup"> - <arguments> - <argument name="behavior" type="string" defaultValue="Add/Update"/> - <argument name="importFile" type="string"/> - </arguments> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> - <waitForPageLoad stepKey="adminImportMainSectionLoad"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportBehaviorOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml new file mode 100644 index 0000000000000..a631ec72a5a72 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminImportProductsWithCheckValidationResultActionGroup" extends="AdminImportProductsActionGroup"> + <arguments> + <argument name="validationNoticeMessage" type="string"/> + <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> + </arguments> + <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" after="clickCheckDataButton" stepKey="waitForValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{validationNoticeMessage}}" after="waitForValidationNoticeMessage" stepKey="seeValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="{{validationMessage}}" after="seeValidationNoticeMessage" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> From e5bcfcdb9fede393d57d6d3f5c695aa20e008db4 Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Fri, 29 Nov 2019 02:44:22 +0530 Subject: [PATCH 2265/2437] Escape html removed from error message --- .../Customer/Controller/Adminhtml/Group/Save.php | 12 ++---------- .../Unit/Controller/Adminhtml/Group/SaveTest.php | 7 +------ .../Customer/Controller/Adminhtml/GroupTest.php | 4 ++-- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index fd6f0ed7daf54..64c94fa230fb1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -20,11 +20,6 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Group implements HttpP */ protected $dataObjectProcessor; - /** - * @var \Magento\Framework\Escaper - */ - protected $escaper; - /** * * @param \Magento\Backend\App\Action\Context $context @@ -34,7 +29,6 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Group implements HttpP * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor - * @param \Magento\Framework\Escaper $escaper */ public function __construct( \Magento\Backend\App\Action\Context $context, @@ -43,11 +37,9 @@ public function __construct( GroupInterfaceFactory $groupDataFactory, \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, - \Magento\Framework\Escaper $escaper + \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor ) { $this->dataObjectProcessor = $dataObjectProcessor; - $this->escaper = $escaper; parent::__construct( $context, $coreRegistry, @@ -104,7 +96,7 @@ public function execute() $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); + $this->messageManager->addErrorMessage($e->getMessage()); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index 7edc631fc71c1..c9f885315b0ef 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -87,10 +87,6 @@ protected function setUp() $this->dataObjectProcessorMock = $this->getMockBuilder(DataObjectProcessor::class) ->disableOriginalConstructor() ->getMock(); - $this->escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class) - ->disableOriginalConstructor() - ->setMethods(['escapeHtml']) - ->getMock(); $this->request = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) ->getMockForAbstractClass(); $this->resultRedirectFactory = $this->getMockBuilder(RedirectFactory::class) @@ -133,8 +129,7 @@ protected function setUp() $this->groupInterfaceFactoryMock, $this->forwardFactoryMock, $this->pageFactoryMock, - $this->dataObjectProcessorMock, - $this->escaper + $this->dataObjectProcessorMock ); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php index db1cc4995e676..8daf74d037249 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php @@ -199,7 +199,7 @@ public function testSaveActionCreateNewGroupWithoutCode() $this->dispatch('backend/customer/group/save'); $this->assertSessionMessages( - $this->equalTo(['"code" is required. Enter and try again.']), + $this->equalTo([htmlspecialchars('"code" is required. Enter and try again.')]), MessageInterface::TYPE_ERROR ); } @@ -292,7 +292,7 @@ public function testSaveActionNewGroupWithoutGroupCode() $this->dispatch('backend/customer/group/save'); $this->assertSessionMessages( - $this->equalTo(['"code" is required. Enter and try again.']), + $this->equalTo([htmlspecialchars('"code" is required. Enter and try again.')]), MessageInterface::TYPE_ERROR ); $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); From 6f35328696700008a49e0387e726cb3734b0b0d7 Mon Sep 17 00:00:00 2001 From: Subash Natarajan <subash.natarajan@ziffity.com> Date: Fri, 29 Nov 2019 09:40:09 +0530 Subject: [PATCH 2266/2437] Useless method overriding issue fix --- .../Magento/Customer/Controller/Adminhtml/GroupTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php index 8daf74d037249..d584fb46cda02 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php @@ -39,15 +39,6 @@ public function setUp() $this->groupRepository = $objectManager->get(\Magento\Customer\Api\GroupRepositoryInterface::class); } - /** - * @inheritDoc - */ - public function tearDown() - { - parent::tearDown(); - //$this->session->unsCustomerGroupData(); - } - /** * Test new group form. */ From 0c35eb76e1de680f349de67a4f004f21222a408c Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 29 Nov 2019 10:33:53 +0100 Subject: [PATCH 2267/2437] Fixed static tests --- .../Test/Unit/Model/DataExtractorTest.php | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php index b64834e5decee..30f355a9298fe 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -175,21 +175,7 @@ public function assetProvider() 'method' => 'getKeyword', ], ], - ], - 'Test case keyword 2' => [ - Keyword::class, - KeywordInterface::class, - [ - 'id' => [ - 'value' => 3, - 'method' => 'getId', - ], - 'keyword' => [ - 'value' => 'keyword2', - 'method' => 'getKeyword', - ], - ], - ], + ] ]; } } From acd42b86af5425d799750f0ceb6c529cd17abba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20K=C3=B6pcke?= <marco.koepcke@tudock.de> Date: Thu, 28 Nov 2019 13:15:59 +0100 Subject: [PATCH 2268/2437] magento/magento2#24229: Don't disable FPC for maintenance, instead don't set public cache headers - Removes the observer for disabling and re-enabling the FPC during maintenande mode switch - Disables setting public cache headers, if maintenance mode is enabled - phpcs:ignore entries were added in places where no actual code was changed by this commit, but static tests failed --- .../PageCache/Model/Layout/LayoutPlugin.php | 16 +- .../Observer/SwitchPageCacheOnMaintenance.php | 108 ------------ .../PageCacheState.php | 1 + .../Unit/Model/Layout/LayoutPluginTest.php | 26 ++- .../SwitchPageCacheOnMaintenanceTest.php | 164 ------------------ app/code/Magento/PageCache/etc/events.xml | 3 - 6 files changed, 33 insertions(+), 285 deletions(-) delete mode 100644 app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php delete mode 100644 app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index ef85d6faf2371..68cd0eb8cc1e9 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -20,22 +20,31 @@ class LayoutPlugin */ protected $response; + /** + * @var \Magento\Framework\App\MaintenanceMode + */ + private $maintenanceMode; + /** * Constructor * * @param \Magento\Framework\App\ResponseInterface $response - * @param \Magento\PageCache\Model\Config $config + * @param \Magento\PageCache\Model\Config $config + * @param \Magento\Framework\App\MaintenanceMode $maintenanceMode */ public function __construct( \Magento\Framework\App\ResponseInterface $response, - \Magento\PageCache\Model\Config $config + \Magento\PageCache\Model\Config $config, + \Magento\Framework\App\MaintenanceMode $maintenanceMode ) { $this->response = $response; $this->config = $config; + $this->maintenanceMode = $maintenanceMode; } /** * Set appropriate Cache-Control headers + * * We have to set public headers in order to tell Varnish and Builtin app that page should be cached * * @param \Magento\Framework\View\Layout $subject @@ -44,7 +53,7 @@ public function __construct( */ public function afterGenerateXml(\Magento\Framework\View\Layout $subject, $result) { - if ($subject->isCacheable() && $this->config->isEnabled()) { + if ($subject->isCacheable() && !$this->maintenanceMode->isOn() && $this->config->isEnabled()) { $this->response->setPublicHeaders($this->config->getTtl()); } return $result; @@ -68,6 +77,7 @@ public function afterGetOutput(\Magento\Framework\View\Layout $subject, $result) if ($isVarnish && $isEsiBlock) { continue; } + // phpcs:ignore $tags = array_merge($tags, $block->getIdentities()); } } diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php deleted file mode 100644 index 7017da27eee93..0000000000000 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\PageCache\Observer; - -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Event\Observer; -use Magento\Framework\App\Cache\Manager; -use Magento\PageCache\Model\Cache\Type as PageCacheType; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; - -/** - * Switch Page Cache on maintenance. - */ -class SwitchPageCacheOnMaintenance implements ObserverInterface -{ - /** - * @var Manager - */ - private $cacheManager; - - /** - * @var PageCacheState - */ - private $pageCacheStateStorage; - - /** - * @param Manager $cacheManager - * @param PageCacheState $pageCacheStateStorage - */ - public function __construct(Manager $cacheManager, PageCacheState $pageCacheStateStorage) - { - $this->cacheManager = $cacheManager; - $this->pageCacheStateStorage = $pageCacheStateStorage; - } - - /** - * Switches Full Page Cache. - * - * Depending on enabling or disabling Maintenance Mode it turns off or restores Full Page Cache state. - * - * @param Observer $observer - * @return void - */ - public function execute(Observer $observer): void - { - if ($observer->getData('isOn')) { - $this->pageCacheStateStorage->save($this->isFullPageCacheEnabled()); - $this->turnOffFullPageCache(); - } else { - $this->restoreFullPageCacheState(); - } - } - - /** - * Turns off Full Page Cache. - * - * @return void - */ - private function turnOffFullPageCache(): void - { - if (!$this->isFullPageCacheEnabled()) { - return; - } - - $this->cacheManager->clean([PageCacheType::TYPE_IDENTIFIER]); - $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], false); - } - - /** - * Full Page Cache state. - * - * @return bool - */ - private function isFullPageCacheEnabled(): bool - { - $cacheStatus = $this->cacheManager->getStatus(); - - if (!array_key_exists(PageCacheType::TYPE_IDENTIFIER, $cacheStatus)) { - return false; - } - - return (bool)$cacheStatus[PageCacheType::TYPE_IDENTIFIER]; - } - - /** - * Restores Full Page Cache state. - * - * Returns FPC to previous state that was before maintenance mode turning on. - * - * @return void - */ - private function restoreFullPageCacheState(): void - { - $storedPageCacheState = $this->pageCacheStateStorage->isEnabled(); - $this->pageCacheStateStorage->flush(); - - if ($storedPageCacheState) { - $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], true); - } - } -} diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php index e4cadf728f2ea..91a305003002e 100644 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -14,6 +14,7 @@ /** * Page Cache state. + * @deprecated Originally used by now removed observer SwitchPageCacheOnMaintenance */ class PageCacheState { diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php index 6c39fe1e7979c..e2bc7f237ab0a 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php @@ -27,6 +27,11 @@ class LayoutPluginTest extends \PHPUnit\Framework\TestCase */ protected $configMock; + /** + * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit\Framework\MockObject\MockObject + */ + private $maintenanceModeMock; + protected function setUp() { $this->layoutMock = $this->getMockForAbstractClass( @@ -40,27 +45,33 @@ protected function setUp() ); $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); $this->configMock = $this->createMock(\Magento\PageCache\Model\Config::class); + $this->maintenanceModeMock = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); $this->model = new \Magento\PageCache\Model\Layout\LayoutPlugin( $this->responseMock, - $this->configMock + $this->configMock, + $this->maintenanceModeMock ); } /** * @param $cacheState * @param $layoutIsCacheable + * @param $maintenanceModeIsEnabled + * * @dataProvider afterGenerateXmlDataProvider */ - public function testAfterGenerateXml($cacheState, $layoutIsCacheable) + public function testAfterGenerateXml($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled) { $maxAge = 180; $result = 'test'; $this->layoutMock->expects($this->once())->method('isCacheable')->will($this->returnValue($layoutIsCacheable)); $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); + $this->maintenanceModeMock->expects($this->any())->method('isOn') + ->will($this->returnValue($maintenanceModeIsEnabled)); - if ($layoutIsCacheable && $cacheState) { + if ($layoutIsCacheable && $cacheState && !$maintenanceModeIsEnabled) { $this->configMock->expects($this->once())->method('getTtl')->will($this->returnValue($maxAge)); $this->responseMock->expects($this->once())->method('setPublicHeaders')->with($maxAge); } else { @@ -76,10 +87,11 @@ public function testAfterGenerateXml($cacheState, $layoutIsCacheable) public function afterGenerateXmlDataProvider() { return [ - 'Full_cache state is true, Layout is cache-able' => [true, true], - 'Full_cache state is true, Layout is not cache-able' => [true, false], - 'Full_cache state is false, Layout is not cache-able' => [false, false], - 'Full_cache state is false, Layout is cache-able' => [false, true] + 'Full_cache state is true, Layout is cache-able' => [true, true, false], + 'Full_cache state is true, Layout is not cache-able' => [true, false, false], + 'Full_cache state is false, Layout is not cache-able' => [false, false, false], + 'Full_cache state is false, Layout is cache-able' => [false, true, false], + 'Full_cache state is true, Layout is cache-able, Maintenance mode is enabled' => [true, true, true], ]; } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php deleted file mode 100644 index 2dbb815c70925..0000000000000 --- a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\PageCache\Test\Unit\Observer; - -use PHPUnit\Framework\TestCase; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\App\Cache\Manager; -use Magento\Framework\Event\Observer; -use Magento\PageCache\Model\Cache\Type as PageCacheType; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; - -/** - * SwitchPageCacheOnMaintenance observer test. - */ -class SwitchPageCacheOnMaintenanceTest extends TestCase -{ - /** - * @var SwitchPageCacheOnMaintenance - */ - private $model; - - /** - * @var Manager|\PHPUnit\Framework\MockObject\MockObject - */ - private $cacheManager; - - /** - * @var PageCacheState|\PHPUnit\Framework\MockObject\MockObject - */ - private $pageCacheStateStorage; - - /** - * @var Observer|\PHPUnit\Framework\MockObject\MockObject - */ - private $observer; - - /** - * @inheritdoc - */ - protected function setUp(): void - { - $objectManager = new ObjectManager($this); - $this->cacheManager = $this->createMock(Manager::class); - $this->pageCacheStateStorage = $this->createMock(PageCacheState::class); - $this->observer = $this->createMock(Observer::class); - - $this->model = $objectManager->getObject(SwitchPageCacheOnMaintenance::class, [ - 'cacheManager' => $this->cacheManager, - 'pageCacheStateStorage' => $this->pageCacheStateStorage, - ]); - } - - /** - * Tests execute when setting maintenance mode to on. - * - * @param array $cacheStatus - * @param bool $cacheState - * @param int $flushCacheCalls - * @return void - * @dataProvider enablingPageCacheStateProvider - */ - public function testExecuteWhileMaintenanceEnabling( - array $cacheStatus, - bool $cacheState, - int $flushCacheCalls - ): void { - $this->observer->method('getData') - ->with('isOn') - ->willReturn(true); - $this->cacheManager->method('getStatus') - ->willReturn($cacheStatus); - - // Page Cache state will be stored. - $this->pageCacheStateStorage->expects($this->once()) - ->method('save') - ->with($cacheState); - - // Page Cache will be cleaned and disabled - $this->cacheManager->expects($this->exactly($flushCacheCalls)) - ->method('clean') - ->with([PageCacheType::TYPE_IDENTIFIER]); - $this->cacheManager->expects($this->exactly($flushCacheCalls)) - ->method('setEnabled') - ->with([PageCacheType::TYPE_IDENTIFIER], false); - - $this->model->execute($this->observer); - } - - /** - * Tests execute when setting Maintenance Mode to off. - * - * @param bool $storedCacheState - * @param int $enableCacheCalls - * @return void - * @dataProvider disablingPageCacheStateProvider - */ - public function testExecuteWhileMaintenanceDisabling(bool $storedCacheState, int $enableCacheCalls): void - { - $this->observer->method('getData') - ->with('isOn') - ->willReturn(false); - - $this->pageCacheStateStorage->method('isEnabled') - ->willReturn($storedCacheState); - - // Nullify Page Cache state. - $this->pageCacheStateStorage->expects($this->once()) - ->method('flush'); - - // Page Cache will be enabled. - $this->cacheManager->expects($this->exactly($enableCacheCalls)) - ->method('setEnabled') - ->with([PageCacheType::TYPE_IDENTIFIER]); - - $this->model->execute($this->observer); - } - - /** - * Page Cache state data provider. - * - * @return array - */ - public function enablingPageCacheStateProvider(): array - { - return [ - 'page_cache_is_enable' => [ - 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 1], - 'cache_state' => true, - 'flush_cache_calls' => 1, - ], - 'page_cache_is_missing_in_system' => [ - 'cache_status' => [], - 'cache_state' => false, - 'flush_cache_calls' => 0, - ], - 'page_cache_is_disable' => [ - 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 0], - 'cache_state' => false, - 'flush_cache_calls' => 0, - ], - ]; - } - - /** - * Page Cache state data provider. - * - * @return array - */ - public function disablingPageCacheStateProvider(): array - { - return [ - ['stored_cache_state' => true, 'enable_cache_calls' => 1], - ['stored_cache_state' => false, 'enable_cache_calls' => 0], - ]; - } -} diff --git a/app/code/Magento/PageCache/etc/events.xml b/app/code/Magento/PageCache/etc/events.xml index 3f0a2532ae60a..7584f5f36d69c 100644 --- a/app/code/Magento/PageCache/etc/events.xml +++ b/app/code/Magento/PageCache/etc/events.xml @@ -57,7 +57,4 @@ <event name="customer_logout"> <observer name="FlushFormKey" instance="Magento\PageCache\Observer\FlushFormKey"/> </event> - <event name="maintenance_mode_changed"> - <observer name="page_cache_switcher_for_maintenance" instance="Magento\PageCache\Observer\SwitchPageCacheOnMaintenance"/> - </event> </config> From d1b81aa514224b25f1c0fea43400a6ab24e1caeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20K=C3=B6pcke?= <marco.koepcke@tudock.de> Date: Fri, 29 Nov 2019 11:38:55 +0100 Subject: [PATCH 2269/2437] magento/magento2#24229: Code Style fix --- .../Observer/SwitchPageCacheOnMaintenance/PageCacheState.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php index 91a305003002e..5c52aa055ef16 100644 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -14,6 +14,7 @@ /** * Page Cache state. + * * @deprecated Originally used by now removed observer SwitchPageCacheOnMaintenance */ class PageCacheState From 50805d4bf94ab71230d5c42d005075b67db68bc5 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 29 Nov 2019 11:39:42 +0100 Subject: [PATCH 2270/2437] Fixed static tests --- .../Test/Unit/Model/DataExtractorTest.php | 46 +------------------ 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php index 30f355a9298fe..7d57f32449f56 100644 --- a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -78,49 +78,7 @@ protected function checkValues(array $expectedData, array $data, $model) public function assetProvider() { return [ - 'Test case asset 1' => [ - Asset::class, - null, - [ - 'id' => [ - 'value' => 1, - 'method' => 'getId', - ], - 'path' => [ - 'value' => 'path', - 'method' => 'getPath', - ], - 'title' => [ - 'value' => 'title', - 'method' => 'getTitle', - ], - 'source' => [ - 'value' => 'source', - 'method' => 'getSource', - ], - 'content_type' => [ - 'value' => 'content_type', - 'method' => 'getContentType', - ], - 'width' => [ - 'value' => 1, - 'method' => 'getWidth', - ], - 'height' => [ - 'value' => 2, - 'method' => 'getHeight', - ], - 'created_at' => [ - 'value' => '2019-11-28 10:40:09', - 'method' => 'getCreatedAt', - ], - 'updated_at' => [ - 'value' => '2019-11-28 10:41:08', - 'method' => 'getUpdatedAt', - ], - ], - ], - 'Test case asset 2' => [ + 'Asset conversion with interface' => [ Asset::class, AssetInterface::class, [ @@ -162,7 +120,7 @@ public function assetProvider() ], ], ], - 'Test case keyword 1' => [ + 'Keyword conversion without interface' => [ Keyword::class, null, [ From 686502a0dd168287672488d307f6a242a6d5b69a Mon Sep 17 00:00:00 2001 From: Maximilian Fickers <m.fickers@basecom.de> Date: Fri, 29 Nov 2019 12:20:57 +0100 Subject: [PATCH 2271/2437] #20463 Fix view details label misalign on order summary for Blank theme --- .../blank/Magento_Checkout/web/css/source/module/_minicart.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index 5f69db5acec4b..133dd0fe721bb 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -263,6 +263,7 @@ ); cursor: pointer; position: relative; + white-space: nowrap; &:after { position: static; From 136cb0400b7e8beb50be8a4bef75c62ff08b0937 Mon Sep 17 00:00:00 2001 From: Maximilian Fickers <m.fickers@basecom.de> Date: Fri, 29 Nov 2019 12:45:37 +0100 Subject: [PATCH 2272/2437] 25042 Add unit tests for header Logo block image dimension getters --- .../Test/Unit/Block/Html/Header/LogoTest.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php index 91af1a9bf6078..077f12e578dca 100644 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php @@ -44,4 +44,38 @@ public function testGetLogoSrc() $this->assertEquals('http://localhost/pub/media/logo/default/image.gif', $block->getLogoSrc()); } + + /** + * cover \Magento\Theme\Block\Html\Header\Logo::getLogoHeight + */ + public function testGetLogoHeight() + { + $scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $scopeConfig->expects($this->once())->method('getValue')->willReturn(null); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $arguments = [ + 'scopeConfig' => $scopeConfig, + ]; + $block = $objectManager->getObject(\Magento\Theme\Block\Html\Header\Logo::class, $arguments); + + $this->assertEquals(null, $block->getLogoHeight()); + } + + /** + * @covers \Magento\Theme\Block\Html\Header\Logo::getLogoWidth + */ + public function testGetLogoWidth() + { + $scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $scopeConfig->expects($this->once())->method('getValue')->willReturn('170'); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $arguments = [ + 'scopeConfig' => $scopeConfig, + ]; + $block = $objectManager->getObject(\Magento\Theme\Block\Html\Header\Logo::class, $arguments); + + $this->assertEquals('170', $block->getLogoHeight()); + } } From da0de42e50758ce52f66f32867e53ec6cccb61c8 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 1 Dec 2019 23:36:13 +0700 Subject: [PATCH 2273/2437] Cover Change Encryption key By MFTF Test. --- ...nEncryptionKeyChangeKeyAutoActionGroup.xml | 25 ++++++++++++++ ...ncryptionKeyChangeKeyManualActionGroup.xml | 26 ++++++++++++++ ...tionKeyNavigateToChangePageActionGroup.xml | 18 ++++++++++ .../Test/Mftf/Data/AdminEncryptionKeyData.xml | 18 ++++++++++ .../Page/AdminEncryptionKeyChangeFormPage.xml | 12 +++++++ .../AdminEncryptionKeyChangeFormSection.xml | 16 +++++++++ .../AdminEncryptionKeyAutoGenerateKeyTest.xml | 34 +++++++++++++++++++ ...dminEncryptionKeyManualGenerateKeyTest.xml | 34 +++++++++++++++++++ 8 files changed, 183 insertions(+) create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml create mode 100644 app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml new file mode 100644 index 0000000000000..62568ebb551e1 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyChangeKeyAutoActionGroup"> + <annotations> + <description>Change Encryption Key Auto Generate Action Group.</description> + </annotations> + <arguments> + <argument name="encryptionKeyData" defaultValue="AdminEncryptionKeyAutoGenerate"/> + </arguments> + + <selectOption selector="{{AdminEncryptionKeyChangeFormSection.autoGenerate}}" userInput="{{encryptionKeyData.autoGenerate}}" stepKey="selectGenerateMode"/> + <click selector="{{AdminEncryptionKeyChangeFormSection.changeEncryptionKey}}" stepKey="clickChangeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The encryption key has been changed." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml new file mode 100644 index 0000000000000..0880bd07a0739 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyChangeKeyManualActionGroup"> + <annotations> + <description>Change Encryption Key - No-Auto Generate Action Group.</description> + </annotations> + <arguments> + <argument name="encryptionKeyData" defaultValue="AdminEncryptionKeyManualGenerate"/> + </arguments> + + <selectOption selector="{{AdminEncryptionKeyChangeFormSection.autoGenerate}}" userInput="{{encryptionKeyData.autoGenerate}}" stepKey="selectGenerateMode"/> + <fillField selector="{{AdminEncryptionKeyChangeFormSection.cryptKey}}" userInput="{{encryptionKeyData.cryptKey}}" stepKey="fillCryptKey"/> + <click selector="{{AdminEncryptionKeyChangeFormSection.changeEncryptionKey}}" stepKey="clickChangeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The encryption key has been changed." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml new file mode 100644 index 0000000000000..69847526a15a0 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyNavigateToChangePageActionGroup"> + <annotations> + <description>Navigate to change encryption key page.</description> + </annotations> + <amOnPage url="{{AdminEncryptionKeyChangeFormPage.url}}" stepKey="navigateToChangeEncryptionPage" /> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml new file mode 100644 index 0000000000000..ff1fe3fd2e10c --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminEncryptionKeyAutoGenerate"> + <data key="autoGenerate">Yes</data> + </entity> + <entity name="AdminEncryptionKeyManualGenerate"> + <data key="autoGenerate">No</data> + <data key="cryptKey">9d469ae32ec27dfec0206cb5d63f135d</data> + </entity> +</entities> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml new file mode 100644 index 0000000000000..b3b8b33fc0364 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminEncryptionKeyChangeFormPage" url="admin/crypt_key/index" area="admin" module="Magento_EncryptionKey"> + <section name="AdminCustomerConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml new file mode 100644 index 0000000000000..7ce37af60fd7f --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminEncryptionKeyChangeFormSection"> + <element name="autoGenerate" type="select" selector="#generate_random"/> + <element name="cryptKey" type="input" selector="#crypt_key"/> + <element name="changeEncryptionKey" type="button" selector=".page-actions-buttons #save" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml new file mode 100644 index 0000000000000..ded57f4aad019 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminEncryptionKeyAutoGenerateKeyTest"> + <annotations> + <features value="Encryption Key"/> + <stories value="Change Encryption Key"/> + <title value="Change Encryption Key by Auto Generate Key"/> + <description value="Change Encryption Key by Auto Generate Key"/> + <severity value="CRITICAL"/> + <group value="encryption_key"/> + </annotations> + + <before> + <!--Login to Admin Area--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + </before> + + <after> + <!--Logout from Admin Area--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> + <actionGroup ref="AdminEncryptionKeyChangeKeyAutoActionGroup" stepKey="changeKeyAutoGenerate"/> + </test> +</tests> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml new file mode 100644 index 0000000000000..f3a9849969263 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminEncryptionKeyManualGenerateKeyTest"> + <annotations> + <features value="Encryption Key"/> + <stories value="Change Encryption Key"/> + <title value="Change Encryption Key by Manual Key"/> + <description value="Change Encryption Key by Manual Key"/> + <severity value="CRITICAL"/> + <group value="encryption_key"/> + </annotations> + + <before> + <!--Login to Admin Area--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + </before> + + <after> + <!--Logout from Admin Area--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> + <actionGroup ref="AdminEncryptionKeyChangeKeyManualActionGroup" stepKey="changeKeyManualGenerate"/> + </test> +</tests> From 23f0ced28aa4c065a771bcb0ec6368b8e85059aa Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Sun, 1 Dec 2019 20:25:48 +0100 Subject: [PATCH 2274/2437] Prevent RequireJS from adding .min.js suffix to external files Fixes #25690 --- .../Magento/Framework/RequireJs/Config.php | 37 +++++++++++++------ .../RequireJs/Test/Unit/ConfigTest.php | 15 +++++--- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/internal/Magento/Framework/RequireJs/Config.php b/lib/internal/Magento/Framework/RequireJs/Config.php index ae45e29f38911..f158aa8b1ebce 100644 --- a/lib/internal/Magento/Framework/RequireJs/Config.php +++ b/lib/internal/Magento/Framework/RequireJs/Config.php @@ -196,11 +196,14 @@ public function getConfigFileRelativePath() */ public function getMixinsFileRelativePath() { - $map = $this->getRepositoryFilesMap(Config::MIXINS_FILE_NAME, [ - 'area' => $this->staticContext->getAreaCode(), - 'theme' => $this->staticContext->getThemePath(), - 'locale' => $this->staticContext->getLocale(), - ]); + $map = $this->getRepositoryFilesMap( + Config::MIXINS_FILE_NAME, + [ + 'area' => $this->staticContext->getAreaCode(), + 'theme' => $this->staticContext->getThemePath(), + 'locale' => $this->staticContext->getLocale(), + ] + ); if ($map) { $relativePath = implode('/', $map) . '/' . Config::MIXINS_FILE_NAME; } else { @@ -254,11 +257,14 @@ public function getMinResolverRelativePath() */ public function getUrlResolverFileRelativePath() { - $map = $this->getRepositoryFilesMap(Config::URL_RESOLVER_FILE_NAME, [ - 'area' => $this->staticContext->getAreaCode(), - 'theme' => $this->staticContext->getThemePath(), - 'locale' => $this->staticContext->getLocale(), - ]); + $map = $this->getRepositoryFilesMap( + Config::URL_RESOLVER_FILE_NAME, + [ + 'area' => $this->staticContext->getAreaCode(), + 'theme' => $this->staticContext->getThemePath(), + 'locale' => $this->staticContext->getLocale(), + ] + ); if ($map) { $relativePath = implode('/', $map) . '/' . Config::URL_RESOLVER_FILE_NAME; } else { @@ -278,6 +284,8 @@ public function getMapFileRelativePath() } /** + * Get path to configuration file + * * @return string */ protected function getConfigFileName() @@ -286,11 +294,13 @@ protected function getConfigFileName() } /** + * Get resolver code which RequireJS fetch minified files instead + * * @return string */ public function getMinResolverCode() { - $excludes = []; + $excludes = ['url.indexOf(baseUrl) === 0']; foreach ($this->minification->getExcludes('js') as $expression) { $excludes[] = '!url.match(/' . str_replace('/', '\/', $expression) . '/)'; } @@ -298,7 +308,8 @@ public function getMinResolverCode() $result = <<<code var ctx = require.s.contexts._, - origNameToUrl = ctx.nameToUrl; + origNameToUrl = ctx.nameToUrl, + baseUrl = ctx.config.baseUrl; ctx.nameToUrl = function() { var url = origNameToUrl.apply(ctx, arguments); @@ -317,6 +328,8 @@ public function getMinResolverCode() } /** + * Get map for given file. + * * @param string $fileId * @param array $params * @return array diff --git a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php index 369449d1f046a..05f040e8f406d 100644 --- a/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php +++ b/lib/internal/Magento/Framework/RequireJs/Test/Unit/ConfigTest.php @@ -101,9 +101,13 @@ public function testGetConfig() { $this->fileReader->expects($this->any()) ->method('readAll') - ->will($this->returnCallback(function ($file) { - return $file . ' content'; - })); + ->will( + $this->returnCallback( + function ($file) { + return $file . ' content'; + } + ) + ); $fileOne = $this->createMock(\Magento\Framework\View\File::class); $fileOne->expects($this->once()) ->method('getFilename') @@ -180,11 +184,12 @@ public function testGetMinResolverCode() $expected = <<<code var ctx = require.s.contexts._, - origNameToUrl = ctx.nameToUrl; + origNameToUrl = ctx.nameToUrl, + baseUrl = ctx.config.baseUrl; ctx.nameToUrl = function() { var url = origNameToUrl.apply(ctx, arguments); - if (!url.match(/\.min\./)) { + if (url.indexOf(baseUrl) === 0&&!url.match(/\.min\./)) { url = url.replace(/(\.min)?\.js$/, '.min.js'); } return url; From 666cca825d83d82ad85e44b003df56eb92c2393f Mon Sep 17 00:00:00 2001 From: Mateusz Krzeszowiak <mateusz.krzeszowiak@creativestyle.pl> Date: Tue, 26 Nov 2019 21:33:30 +0100 Subject: [PATCH 2275/2437] Removes hardcoded references to country selector component --- .../view/frontend/web/js/view/shipping.js | 9 +- .../base/web/js/form/element/post-code.js | 25 ++-- .../view/base/web/js/form/element/region.js | 73 +++------- .../frontend/js/view/shipping.test.js | 24 ++-- .../Ui/base/js/form/element/post-code.test.js | 75 ++++++++-- .../Ui/base/js/form/element/region.test.js | 131 ++++++++++++++++-- 6 files changed, 237 insertions(+), 100 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 1c3f38a37c7f9..fe8d7782e5eae 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -60,7 +60,10 @@ define([ template: 'Magento_Checkout/shipping', shippingFormTemplate: 'Magento_Checkout/shipping-address/form', shippingMethodListTemplate: 'Magento_Checkout/shipping-address/shipping-method-list', - shippingMethodItemTemplate: 'Magento_Checkout/shipping-address/shipping-method-item' + shippingMethodItemTemplate: 'Magento_Checkout/shipping-address/shipping-method-item', + imports: { + countryOptions: '${ $.parentName }.shippingAddress.shipping-address-fieldset.country_id:indexedOptions' + } }, visible: ko.observable(!quote.isVirtual()), errorValidationMessage: ko.observable(false), @@ -276,9 +279,7 @@ define([ loginFormSelector = 'form[data-role=email-with-possible-login]', emailValidationResult = customer.isLoggedIn(), field, - country = registry.get(this.parentName + '.shippingAddress.shipping-address-fieldset.country_id'), - countryIndexedOptions = country.indexedOptions, - option = countryIndexedOptions[quote.shippingAddress().countryId], + option = _.isObject(this.countryOptions) && this.countryOptions[quote.shippingAddress().countryId], messageContainer = registry.get('checkout.errors').messageContainer; if (!quote.shippingMethod()) { diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 0eaacdc32567b..72c352f353239 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -8,14 +8,14 @@ */ define([ 'underscore', - 'uiRegistry', './abstract' -], function (_, registry, Abstract) { +], function (_, Abstract) { 'use strict'; return Abstract.extend({ defaults: { imports: { + countryOptions: '${ $.parentName }.country_id:indexedOptions', update: '${ $.parentName }.country_id:value' } }, @@ -41,31 +41,32 @@ define([ }, /** - * @param {String} value + * Method called every time country selector's value gets changed. + * Updates all validations and requirements for certain country. + * @param {String} value - Selected country ID. */ update: function (value) { - var country = registry.get(this.parentName + '.' + 'country_id'), - options = country.indexedOptions, - option = null; + var isZipCodeOptional, + option; if (!value) { return; } - option = options[value]; + option = _.isObject(this.countryOptions) && this.countryOptions[value]; if (!option) { return; } - if (option['is_zipcode_optional']) { + isZipCodeOptional = !!option['is_zipcode_optional']; + + if (isZipCodeOptional) { this.error(false); - this.validation = _.omit(this.validation, 'required-entry'); - } else { - this.validation['required-entry'] = true; } - this.required(!option['is_zipcode_optional']); + this.validation['required-entry'] = !isZipCodeOptional; + this.required(!isZipCodeOptional); } }); }); diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/region.js b/app/code/Magento/Ui/view/base/web/js/form/element/region.js index f6eafcf49284d..cd9c2aee85dc6 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/region.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/region.js @@ -18,81 +18,54 @@ define([ defaults: { skipValidation: false, imports: { + countryOptions: '${ $.parentName }.country_id:indexedOptions', update: '${ $.parentName }.country_id:value' } }, /** - * @param {String} value + * Method called every time country selector's value gets changed. + * Updates all validations and requirements for certain country. + * @param {String} value - Selected country ID. */ update: function (value) { - var country = registry.get(this.parentName + '.' + 'country_id'), - options = country.indexedOptions, - isRegionRequired, + var isRegionRequired, option; if (!value) { return; } - option = options[value]; - if (typeof option === 'undefined') { + option = _.isObject(this.countryOptions) && this.countryOptions[value]; + + if (!option) { return; } defaultPostCodeResolver.setUseDefaultPostCode(!option['is_zipcode_optional']); - if (this.skipValidation) { - this.validation['required-entry'] = false; - this.required(false); - } else { - if (option && !option['is_region_required']) { - this.error(false); - this.validation = _.omit(this.validation, 'required-entry'); - registry.get(this.customName, function (input) { - input.validation['required-entry'] = false; - input.required(false); - }); - } else { - this.validation['required-entry'] = true; - } + if (option['is_region_visible'] === false) { + // Hide select and corresponding text input field if region must not be shown for selected country. + this.setVisible(false); - if (option && !this.options().length) { - registry.get(this.customName, function (input) { - isRegionRequired = !!option['is_region_required']; - input.validation['required-entry'] = isRegionRequired; - input.validation['validate-not-number-first'] = true; - input.required(isRegionRequired); - }); + if (this.customEntry) { // eslint-disable-line max-depth + this.toggleInput(false); } - - this.required(!!option['is_region_required']); } - }, - /** - * Filters 'initialOptions' property by 'field' and 'value' passed, - * calls 'setOptions' passing the result to it - * - * @param {*} value - * @param {String} field - */ - filter: function (value, field) { - var superFn = this._super; - - registry.get(this.parentName + '.' + 'country_id', function (country) { - var option = country.indexedOptions[value]; + isRegionRequired = !this.skipValidation && !!option['is_region_required']; - superFn.call(this, value, field); + if (!isRegionRequired) { + this.error(false); + } - if (option && option['is_region_visible'] === false) { - // hide select and corresponding text input field if region must not be shown for selected country - this.setVisible(false); + this.required(isRegionRequired); + this.validation['required-entry'] = isRegionRequired; - if (this.customEntry) {// eslint-disable-line max-depth - this.toggleInput(false); - } - } + registry.get(this.customName, function (input) { + input.required(isRegionRequired); + input.validation['required-entry'] = isRegionRequired; + input.validation['validate-not-number-first'] = !this.options().length; }.bind(this)); } }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js index 9b9b1ce5b1614..46d9e1974bdb7 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Checkout/frontend/js/view/shipping.test.js @@ -174,16 +174,13 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq describe('"validateShippingInformation" method', function () { it('Check method call on negative cases.', function () { + /* jscs:disable */ var country = { - 'indexedOptions': { - 'AD': - { - label: 'Andorra', - labeltitle: 'Andorra', - value: 'AD' - } - } + on: function () {}, + get: function () {}, + set: function () {} }; + /* jscs:enable */ registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); registry.set('checkout.errors', {}); @@ -202,9 +199,20 @@ define(['squire', 'ko', 'jquery', 'uiRegistry', 'jquery/validate'], function (Sq expect(obj.validateShippingInformation()).toBeFalsy(); }); it('Check method call on positive case.', function () { + /* jscs:disable */ + var country = { + on: function () {}, + get: function () {}, + set: function () {} + }; + /* jscs:enable */ + $('body').append('<form data-role="email-with-possible-login">' + '<input type="text" name="username" />' + '</form>'); + + registry.set('test.shippingAddress.shipping-address-fieldset.country_id', country); + registry.set('checkout.errors', {}); obj.source = { get: jasmine.createSpy().and.returnValue(true), set: jasmine.createSpy(), diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js index 96bca1bbd8c6b..d369d66a92225 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/post-code.test.js @@ -47,22 +47,71 @@ define([ }); describe('update method', function () { - it('check for default', function () { - var value = 'Value', - country = { - indexedOptions: { - 'Value': { - 'is_zipcode_optional': true - } - } - }; - - spyOn(mocks['Magento_Ui/js/lib/registry/registry'], 'get').and.returnValue(country); + it('makes field optional when there is no corresponding country', function () { + var value = 'Value'; + + model.countryOptions = {}; + + model.update(value); + + expect(model.required()).toEqual(false); + }); + + it('makes field optional when post code is optional for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_zipcode_optional': true + } + }; + model.update(value); - expect(mocks['Magento_Ui/js/lib/registry/registry'].get).toHaveBeenCalled(); - expect(model.error()).toEqual(false); + expect(model.required()).toEqual(false); }); + + it('removes field required validation when post code is optional for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_zipcode_optional': true + } + }; + + model.update(value); + + expect(model.validation['required-entry']).toBeFalsy(); + }); + + it('makes field required when post code is required for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_zipcode_optional': false + } + }; + + model.update(value); + + expect(model.required()).toEqual(true); + }); + + it('sets field required validation when post code is required for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_zipcode_optional': false + } + }; + + model.update(value); + + expect(model.validation['required-entry']).toEqual(true); + }); }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js index a36d3b0b7fefa..a957db5d1c119 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/element/region.test.js @@ -48,22 +48,127 @@ define([ }); describe('update method', function () { - it('check for default', function () { - var value = 'Value', - country = { - indexedOptions: { - 'Value': { - 'is_zipcode_optional': true - } - } - }; - - spyOn(mocks['Magento_Ui/js/lib/registry/registry'], 'get').and.returnValue(country); + it('makes field optional when there is no corresponding country', function () { + var value = 'Value'; + + model.countryOptions = {}; + + model.update(value); + + expect(model.required()).toEqual(false); + }); + + it('makes field optional when region is optional for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': false + } + }; + model.update(value); - expect(mocks['Magento_Ui/js/lib/registry/registry'].get).toHaveBeenCalled(); - expect(model.error()).toEqual(false); + expect(model.required()).toEqual(false); }); + + it('removes field required validation when region is optional for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': false + } + }; + + model.update(value); + + expect(model.validation['required-entry']).toBeFalsy(); + }); + + it('makes field required when region is required for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': true + } + }; + + model.update(value); + + expect(model.required()).toEqual(true); + }); + + it('sets field required validation when region is required for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': true + } + }; + + model.update(value); + + expect(model.validation['required-entry']).toEqual(true); + }); + + it('keeps region visible by default', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': {} + }; + + model.update(value); + + expect(model.visible()).toEqual(true); + }); + + it('hides region field when it should be hidden for certain country', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_visible': false + } + }; + + model.update(value); + + expect(model.visible()).toEqual(false); + }); + + it('makes field optional when validation should be skipped', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': true + } + }; + + model.skipValidation = true; + model.update(value); + + expect(model.required()).toEqual(false); + }); + + it('removes field validation when validation should be skipped', function () { + var value = 'Value'; + + model.countryOptions = { + 'Value': { + 'is_region_required': true + } + }; + + model.skipValidation = true; + model.update(value); + + expect(model.validation['required-entry']).toBeFalsy(); + }); }); }); }); From 7c9ca2f038d3fa8a12a496fa0b63c1c43e405cb3 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Mon, 2 Dec 2019 22:46:52 +0700 Subject: [PATCH 2276/2437] [Captcha] Cover Model Config by Unit Test --- .../Test/Unit/Model/Config/FontTest.php | 100 ++++++++++++++++++ .../Unit/Model/Config/Form/BackendTest.php | 100 ++++++++++++++++++ .../Unit/Model/Config/Form/FrontendTest.php | 99 +++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php create mode 100644 app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php create mode 100644 app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php new file mode 100644 index 0000000000000..fd6b59a6ad388 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config; +use PHPUnit\Framework\TestCase; +use Magento\Captcha\Helper\Data as HelperData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Captcha\Model\Config\Font; + +class FontTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Font + */ + private $model; + + /** + * @var HelperData|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperDataMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->helperDataMock = $this->createMock(HelperData::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Font::class, + [ + 'captchaData' => $this->helperDataMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param array $fonts + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($fonts, $expectedResult) + { + $this->helperDataMock->expects($this->any())->method('getFonts') + ->willReturn($fonts); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty get font' => [ + [], + [] + ], + 'Get font result' => [ + [ + 'arial' => [ + 'label' => 'Arial', + 'path' => '/www/magento/fonts/arial.ttf' + ], + 'verdana' => [ + 'label' => 'Verdana', + 'path' => '/www/magento/fonts/verdana.ttf' + ] + ], + [ + [ + 'label' => 'Arial', + 'value' => 'arial' + ], + [ + 'label' => 'Verdana', + 'value' => 'verdana' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php new file mode 100644 index 0000000000000..054cc71af61bc --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config\Form; + +use Magento\Captcha\Model\Config\Form\Backend; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class BackendTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Backend + */ + private $model; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->configMock = $this->createMock(ScopeConfigInterface::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Backend::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param string|array $config + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($config, $expectedResult) + { + $this->configMock->expects($this->any())->method('getValue') + ->with('captcha/backend/areas', 'default') + ->willReturn($config); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty captcha backend areas' => [ + '', + [] + ], + 'With two captcha backend area' => [ + [ + 'backend_login' => [ + 'label' => 'Admin Login' + ], + 'backend_forgotpassword' => [ + 'label' => 'Admin Forgot Password' + ] + ], + [ + [ + 'label' => 'Admin Login', + 'value' => 'backend_login' + ], + [ + 'label' => 'Admin Forgot Password', + 'value' => 'backend_forgotpassword' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php new file mode 100644 index 0000000000000..25681ac975cef --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config\Form; +use Magento\Captcha\Model\Config\Form\Frontend; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class FrontendTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Frontend + */ + private $model; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->configMock = $this->createMock(ScopeConfigInterface::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Frontend::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param string|array $config + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($config, $expectedResult) + { + $this->configMock->expects($this->any())->method('getValue') + ->with('captcha/frontend/areas', 'default') + ->willReturn($config); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty captcha frontend areas' => [ + '', + [] + ], + 'With two captcha frontend area' => [ + [ + 'product_sendtofriend_form' => [ + 'label' => 'Send To Friend Form' + ], + 'sales_rule_coupon_request' => [ + 'label' => 'Applying coupon code' + ] + ], + [ + [ + 'label' => 'Send To Friend Form', + 'value' => 'product_sendtofriend_form' + ], + [ + 'label' => 'Applying coupon code', + 'value' => 'sales_rule_coupon_request' + ] + ] + ] + ]; + } +} From 3b27be628e5d1b94a83bcfba1d84e7e172360d43 Mon Sep 17 00:00:00 2001 From: Roman Hanin <rganin@adobe.com> Date: Mon, 2 Dec 2019 11:57:24 -0600 Subject: [PATCH 2277/2437] B2B-271: Remove Ship To field from Order Search --- .../Magento/Sales/view/frontend/templates/order/history.phtml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml index ef56ad69dcb1b..746d02a668a87 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -19,7 +19,6 @@ <th scope="col" class="col id"><?= $block->escapeHtml(__('Order #')) ?></th> <th scope="col" class="col date"><?= $block->escapeHtml(__('Date')) ?></th> <?= $block->getChildHtml('extra.column.header') ?> - <th scope="col" class="col shipping"><?= $block->escapeHtml(__('Ship To')) ?></th> <th scope="col" class="col total"><?= $block->escapeHtml(__('Order Total')) ?></th> <th scope="col" class="col status"><?= $block->escapeHtml(__('Status')) ?></th> <th scope="col" class="col actions"><?= $block->escapeHtml(__('Action')) ?></th> @@ -35,7 +34,6 @@ <?php $extra->setOrder($_order); ?> <?= $extra->getChildHtml() ?> <?php endif; ?> - <td data-th="<?= $block->escapeHtml(__('Ship To')) ?>" class="col shipping"><?= $_order->getShippingAddress() ? $block->escapeHtml($_order->getShippingAddress()->getName()) : ' ' ?></td> <td data-th="<?= $block->escapeHtml(__('Order Total')) ?>" class="col total"><?= /* @noEscape */ $_order->formatPrice($_order->getGrandTotal()) ?></td> <td data-th="<?= $block->escapeHtml(__('Status')) ?>" class="col status"><?= $block->escapeHtml($_order->getStatusLabel()) ?></td> <td data-th="<?= $block->escapeHtml(__('Actions')) ?>" class="col actions"> From 28542922e9914d169ddb41ce1f83ce44cc063982 Mon Sep 17 00:00:00 2001 From: Dmytro Voskoboinikov <voskoboi@adobe.com> Date: Mon, 2 Dec 2019 15:06:05 -0600 Subject: [PATCH 2278/2437] MC-23177: Update of Moment.js library --- lib/web/moment.js | 557 +--------------------------------------------- 1 file changed, 6 insertions(+), 551 deletions(-) diff --git a/lib/web/moment.js b/lib/web/moment.js index d34d1c6329e59..ee598a90a83f0 100644 --- a/lib/web/moment.js +++ b/lib/web/moment.js @@ -1,551 +1,6 @@ -//! moment.js -//! version : 2.17.1 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com -!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return od.apply(null,arguments)} -// This is done to register the method called with moment() -// without creating circular dependencies. - function b(a){od=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){ -// IE8 will treat undefined and null as object if it wasn't for -// input != null - return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a) -// even if its not own property I'd still call it non-empty - return!1;return!0}function f(a){return"number"==typeof a||"[object Number]"===Object.prototype.toString.call(a)}function g(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function h(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function i(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function j(a,b){for(var c in b)i(b,c)&&(a[c]=b[c]);return i(b,"toString")&&(a.toString=b.toString),i(b,"valueOf")&&(a.valueOf=b.valueOf),a}function k(a,b,c,d){return rb(a,b,c,d,!0).utc()}function l(){ -// We need to deep clone this object. - return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null}}function m(a){return null==a._pf&&(a._pf=l()),a._pf}function n(a){if(null==a._isValid){var b=m(a),c=qd.call(b.parsedDateParts,function(a){return null!=a}),d=!isNaN(a._d.getTime())&&b.overflow<0&&!b.empty&&!b.invalidMonth&&!b.invalidWeekday&&!b.nullInput&&!b.invalidFormat&&!b.userInvalidated&&(!b.meridiem||b.meridiem&&c);if(a._strict&&(d=d&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour),null!=Object.isFrozen&&Object.isFrozen(a))return d;a._isValid=d}return a._isValid}function o(a){var b=k(NaN);return null!=a?j(m(b),a):m(b).userInvalidated=!0,b}function p(a){return void 0===a}function q(a,b){var c,d,e;if(p(b._isAMomentObject)||(a._isAMomentObject=b._isAMomentObject),p(b._i)||(a._i=b._i),p(b._f)||(a._f=b._f),p(b._l)||(a._l=b._l),p(b._strict)||(a._strict=b._strict),p(b._tzm)||(a._tzm=b._tzm),p(b._isUTC)||(a._isUTC=b._isUTC),p(b._offset)||(a._offset=b._offset),p(b._pf)||(a._pf=m(b)),p(b._locale)||(a._locale=b._locale),rd.length>0)for(c in rd)d=rd[c],e=b[d],p(e)||(a[d]=e);return a} -// Moment prototype object - function r(b){q(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)), -// Prevent infinite loop in case updateOffset creates new moment -// objects. - sd===!1&&(sd=!0,a.updateOffset(this),sd=!1)}function s(a){return a instanceof r||null!=a&&null!=a._isAMomentObject}function t(a){return a<0?Math.ceil(a)||0:Math.floor(a)}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=t(b)),c} -// compare two arrays, return the number of differences - function v(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;d<e;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function w(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function x(b,c){var d=!0;return j(function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,b),d){for(var e,f=[],g=0;g<arguments.length;g++){if(e="","object"==typeof arguments[g]){e+="\n["+g+"] ";for(var h in arguments[0])e+=h+": "+arguments[0][h]+", ";e=e.slice(0,-2)}else e=arguments[g];f.push(e)}w(b+"\nArguments: "+Array.prototype.slice.call(f).join("")+"\n"+(new Error).stack),d=!1}return c.apply(this,arguments)},c)}function y(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),td[b]||(w(c),td[b]=!0)}function z(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function A(a){var b,c;for(c in a)b=a[c],z(b)?this[c]=b:this["_"+c]=b;this._config=a, -// Lenient ordinal parsing accepts just a number in addition to -// number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function B(a,b){var c,e=j({},a);for(c in b)i(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},j(e[c],a[c]),j(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)i(a,c)&&!i(b,c)&&d(a[c])&&( -// make sure changes to properties don't modify parent config - e[c]=j({},e[c]));return e}function C(a){null!=a&&this.set(a)}function D(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return z(d)?d.call(b,c):d}function E(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function F(){return this._invalidDate}function G(a){return this._ordinal.replace("%d",a)}function H(a,b,c,d){var e=this._relativeTime[c];return z(e)?e(a,b,c,d):e.replace(/%d/i,a)}function I(a,b){var c=this._relativeTime[a>0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Dd[c]=Dd[c+"s"]=Dd[b]=a}function K(a){return"string"==typeof a?Dd[a]||Dd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)i(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Ed[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Ed[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)} -// MOMENTS - function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d<c.length;d++)this[c[d].unit](a[c[d].unit])}else if(a=K(a),z(this[a]))return this[a](b);return this}function T(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d} -// token: 'M' -// padded: ['MM', 2] -// ordinal: 'Mo' -// callback: function () { this.month() + 1 } - function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Id[a]=e),b&&(Id[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Id[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Fd);for(b=0,c=d.length;b<c;b++)Id[d[b]]?d[b]=Id[d[b]]:d[b]=V(d[b]);return function(b){var e,f="";for(e=0;e<c;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}} -// format date using native date object - function X(a,b){return a.isValid()?(b=Y(b,a.localeData()),Hd[b]=Hd[b]||W(b),Hd[b](a)):a.localeData().invalidDate()}function Y(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Gd.lastIndex=0;d>=0&&Gd.test(a);)a=a.replace(Gd,c),Gd.lastIndex=0,d-=1;return a}function Z(a,b,c){$d[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return i($d,a)?$d[a](b._strict,b._locale):new RegExp(_(a))} -// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),f(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c<a.length;c++)_d[a[c]]=d}function ca(a,b){ba(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function da(a,b,c){null!=b&&i(_d,a)&&_d[a](b,c._a,c,a)}function ea(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function fa(a,b){return a?c(this._months)?this._months[a.month()]:this._months[(this._months.isFormat||ke).test(b)?"format":"standalone"][a.month()]:this._months}function ga(a,b){return a?c(this._monthsShort)?this._monthsShort[a.month()]:this._monthsShort[ke.test(b)?"format":"standalone"][a.month()]:this._monthsShort}function ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._monthsParse)for( -// this is not used - this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],d=0;d<12;++d)f=k([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=je.call(this._shortMonthsParse,g),e!==-1?e:null):(e=je.call(this._longMonthsParse,g),e!==-1?e:null):"MMM"===b?(e=je.call(this._shortMonthsParse,g),e!==-1?e:(e=je.call(this._longMonthsParse,g),e!==-1?e:null)):(e=je.call(this._longMonthsParse,g),e!==-1?e:(e=je.call(this._shortMonthsParse,g),e!==-1?e:null))}function ia(a,b,c){var d,e,f;if(this._monthsParseExact)return ha.call(this,a,b,c); -// TODO: add sorting -// Sorting makes sure if one month (or abbr) is a prefix of another -// see sorting in computeMonthsParse - for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;d<12;d++){ -// test the regex - if( -// make the regex if we don't have it already - e=k([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}} -// MOMENTS - function ja(a,b){var c;if(!a.isValid()) -// No op - return a;if("string"==typeof b)if(/^\d+$/.test(b))b=u(b);else -// TODO: Another silent failure? - if(b=a.localeData().monthsParse(b),!f(b))return a;return c=Math.min(a.date(),ea(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ka(b){return null!=b?(ja(this,b),a.updateOffset(this,!0),this):P(this,"Month")}function la(){return ea(this.year(),this.month())}function ma(a){return this._monthsParseExact?(i(this,"_monthsRegex")||oa.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(i(this,"_monthsShortRegex")||(this._monthsShortRegex=ne),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function na(a){return this._monthsParseExact?(i(this,"_monthsRegex")||oa.call(this),a?this._monthsStrictRegex:this._monthsRegex):(i(this,"_monthsRegex")||(this._monthsRegex=oe),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function oa(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;b<12;b++) -// make the regex if we don't have it already - c=k([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for( -// Sorting makes sure if one month (or abbr) is a prefix of another it -// will match the longer piece. - d.sort(a),e.sort(a),f.sort(a),b=0;b<12;b++)d[b]=aa(d[b]),e[b]=aa(e[b]);for(b=0;b<24;b++)f[b]=aa(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")} -// HELPERS - function pa(a){return qa(a)?366:365}function qa(a){return a%4===0&&a%100!==0||a%400===0}function ra(){return qa(this.year())}function sa(a,b,c,d,e,f,g){ -//can't just apply() to create a date: -//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var h=new Date(a,b,c,d,e,f,g); -//the date constructor remaps years 0-99 to 1900-1999 - return a<100&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments)); -//the Date.UTC function remaps years 0-99 to 1900-1999 - return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b} -// start-of-first-week - start-of-year - function ua(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other) - d=7+b-c, -// first-week day local weekday -- which local weekday is fwd - e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1} -//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7} -// HELPERS -// LOCALES - function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy} -// MOMENTS - function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")} -// HELPERS - function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:this._weekdays}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=k([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=je.call(this._minWeekdaysParse,g),e!==-1?e:(e=je.call(this._weekdaysParse,g),e!==-1?e:(e=je.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){ -// test the regex - if( -// make the regex if we don't have it already - e=k([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}} -// MOMENTS - function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN; -// behaves the same as moment#day except -// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) -// as a setter, sunday should belong to the previous week. - if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(i(this,"_weekdaysRegex")||(this._weekdaysRegex=ue),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(i(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ve),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(i(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(i(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=we),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++) -// make the regex if we don't have it already - c=k([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for( -// Sorting makes sure if one weekday (or abbr) is a prefix of another it -// will match the longer piece. - g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")} -// FORMATTING - function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})} -// PARSING - function Ua(a,b){return b._meridiemParse} -// LOCALES - function Va(a){ -// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays -// Using charAt should be more compatible. - return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a} -// pick the locale from the array -// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each -// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function Ya(a){for(var b,c,d,e,f=0;f<a.length;){for(e=Xa(a[f]).split("-"),b=e.length,c=Xa(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1) -//the next array item is better than a shallower substring of this one - break;b--}f++}return null}function Za(a){var b=null; -// TODO: Find a better way to register and load all the locales in Node - if(!Be[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=xe._abbr,require("./locale/"+a), -// because defineLocale currently also sets the global locale, we -// want to undo that for lazy loaded locales - $a(b)}catch(a){}return Be[a]} -// This function will load locale and then set the global locale. If -// no arguments are passed in, it will simply return the current global -// locale key. - function $a(a,b){var c; -// moment.duration._locale = moment._locale = data; - return a&&(c=p(b)?bb(a):_a(a,b),c&&(xe=c)),xe._abbr}function _a(a,b){if(null!==b){var c=Ae;if(b.abbr=a,null!=Be[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Be[a]._config;else if(null!=b.parentLocale){if(null==Be[b.parentLocale])return Ce[b.parentLocale]||(Ce[b.parentLocale]=[]),Ce[b.parentLocale].push({name:a,config:b}),null;c=Be[b.parentLocale]._config} -// backwards compat for now: also set the locale -// make sure we set the locale AFTER all child locales have been -// created, so we won't end up with the child locale set. - return Be[a]=new C(B(c,b)),Ce[a]&&Ce[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Be[a]} -// useful for testing - return delete Be[a],null}function ab(a,b){if(null!=b){var c,d=Ae; -// MERGE - null!=Be[a]&&(d=Be[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Be[a],Be[a]=c, -// backwards compat for now: also set the locale - $a(a)}else -// pass null for config to unupdate, useful for tests - null!=Be[a]&&(null!=Be[a].parentLocale?Be[a]=Be[a].parentLocale:null!=Be[a]&&delete Be[a]);return Be[a]} -// returns locale data - function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return xe;if(!c(a)){if( -//short-circuit everything else - b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return wd(Be)}function db(a){var b,c=a._a;return c&&m(a).overflow===-2&&(b=c[be]<0||c[be]>11?be:c[ce]<1||c[ce]>ea(c[ae],c[be])?ce:c[de]<0||c[de]>24||24===c[de]&&(0!==c[ee]||0!==c[fe]||0!==c[ge])?de:c[ee]<0||c[ee]>59?ee:c[fe]<0||c[fe]>59?fe:c[ge]<0||c[ge]>999?ge:-1,m(a)._overflowDayOfYear&&(b<ae||b>ce)&&(b=ce),m(a)._overflowWeeks&&b===-1&&(b=he),m(a)._overflowWeekday&&b===-1&&(b=ie),m(a).overflow=b),a} -// date from iso format - function eb(a){var b,c,d,e,f,g,h=a._i,i=De.exec(h)||Ee.exec(h);if(i){for(m(a).iso=!0,b=0,c=Ge.length;b<c;b++)if(Ge[b][1].exec(i[1])){e=Ge[b][0],d=Ge[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=He.length;b<c;b++)if(He[b][1].exec(i[3])){ -// match[2] should be 'T' or space - f=(i[2]||" ")+He[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!Fe.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),kb(a)}else a._isValid=!1} -// date from iso format or fallback - function fb(b){var c=Ie.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(eb(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))} -// Pick the first defined of two or three arguments. - function gb(a,b,c){return null!=a?a:null!=b?b:c}function hb(b){ -// hooks is actually the exported moment object - var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]} -// convert an array to a date. -// the array should mirror the parameters below -// note: all values past the year are optional and will default to the lowest possible value. -// [year, month, day , hour, minute, second, millisecond] - function ib(a){var b,c,d,e,f=[];if(!a._d){ -// Default to current date. -// * if no year, month, day of month are given, default to today -// * if day of month is given, default month and year -// * if month is given, default only year -// * if year is given, don't default anything - for(d=hb(a), -//compute day of the year from weeks and weekdays - a._w&&null==a._a[ce]&&null==a._a[be]&&jb(a), -//if the day of the year is set, figure out what it is - a._dayOfYear&&(e=gb(a._a[ae],d[ae]),a._dayOfYear>pa(e)&&(m(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[be]=c.getUTCMonth(),a._a[ce]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b]; -// Zero out whatever was not defaulted, including time - for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b]; -// Check for 24:00:00.000 - 24===a._a[de]&&0===a._a[ee]&&0===a._a[fe]&&0===a._a[ge]&&(a._nextDay=!0,a._a[de]=0),a._d=(a._useUTC?ta:sa).apply(null,f), -// Apply timezone offset from input. The actual utcOffset can be changed -// with parseZone. - null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[de]=24)}}function jb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4, -// TODO: We need to take the current isoWeekYear, but that depends on -// how we interpret now (local, utc, fixed offset). So create -// a now version of current config (take local/utc/offset flags, and -// create now). - c=gb(b.GG,a._a[ae],wa(sb(),1,4).year),d=gb(b.W,1),e=gb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(sb(),f,g);c=gb(b.gg,a._a[ae],j.year), -// Default to current week. - d=gb(b.w,j.week),null!=b.d?( -// weekday -- low day numbers are considered next week - e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?( -// local weekday -- counting starts from begining of week - e=b.e+f,(b.e<0||b.e>6)&&(i=!0)): -// default to begining of week - e=f}d<1||d>xa(c,f,g)?m(a)._overflowWeeks=!0:null!=i?m(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ae]=h.year,a._dayOfYear=h.dayOfYear)} -// date from string and format string - function kb(b){ -// TODO: Move this to another part of the creation flow to prevent circular deps - if(b._f===a.ISO_8601)return void eb(b);b._a=[],m(b).empty=!0; -// This array is used to make a Date, either with `new Date` or `Date.UTC` - var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Fd)||[],c=0;c<e.length;c++)f=e[c],d=(h.match($(f,b))||[])[0], -// console.log('token', token, 'parsedInput', parsedInput, -// 'regex', getParseRegexForToken(token, config)); - d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&m(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length), -// don't parse if it's not a known token - Id[f]?(d?m(b).empty=!1:m(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&m(b).unusedTokens.push(f); -// add remaining unparsed input length to the string - m(b).charsLeftOver=i-j,h.length>0&&m(b).unusedInput.push(h), -// clear _12h flag if hour is <= 12 - b._a[de]<=12&&m(b).bigHour===!0&&b._a[de]>0&&(m(b).bigHour=void 0),m(b).parsedDateParts=b._a.slice(0),m(b).meridiem=b._meridiem, -// handle meridiem - b._a[de]=lb(b._locale,b._a[de],b._meridiem),ib(b),db(b)}function lb(a,b,c){var d; -// Fallback - return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b} -// date from string and array of format strings - function mb(a){var b,c,d,e,f;if(0===a._f.length)return m(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=q({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],kb(b),n(b)&&( -// if there is any input that was not parsed add a penalty for that format - f+=m(b).charsLeftOver, -//or tokens - f+=10*m(b).unusedTokens.length,m(b).score=f,(null==d||f<d)&&(d=f,c=b));j(a,c||b)}function nb(a){if(!a._d){var b=L(a._i);a._a=h([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),ib(a)}}function ob(a){var b=new r(db(pb(a))); -// Adding is smart enough around DST - return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function pb(a){var b=a._i,d=a._f;return a._locale=a._locale||bb(a._l),null===b||void 0===d&&""===b?o({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),s(b)?new r(db(b)):(g(b)?a._d=b:c(d)?mb(a):d?kb(a):qb(a),n(a)||(a._d=null),a))}function qb(b){var d=b._i;void 0===d?b._d=new Date(a.now()):g(d)?b._d=new Date(d.valueOf()):"string"==typeof d?fb(b):c(d)?(b._a=h(d.slice(0),function(a){return parseInt(a,10)}),ib(b)):"object"==typeof d?nb(b):f(d)? -// from milliseconds - b._d=new Date(d):a.createFromInputFallback(b)}function rb(a,b,f,g,h){var i={}; -// object construction must be done this way. -// https://github.com/moment/moment/issues/1423 - return f!==!0&&f!==!1||(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,ob(i)}function sb(a,b,c,d){return rb(a,b,c,d,!1)} -// Pick a moment m from moments so that m[fn](other) is true for all -// other. This relies on the function fn to be transitive. -// -// moments should either be an array of moment objects or an array, whose -// first element is an array of moment objects. - function tb(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return sb();for(d=b[0],e=1;e<b.length;++e)b[e].isValid()&&!b[e][a](d)||(d=b[e]);return d} -// TODO: Use [].sort instead? - function ub(){var a=[].slice.call(arguments,0);return tb("isBefore",a)}function vb(){var a=[].slice.call(arguments,0);return tb("isAfter",a)}function wb(a){var b=L(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0; -// representation for dateAddRemove - this._milliseconds=+k+1e3*j+// 1000 - 6e4*i+// 1000 * 60 - 1e3*h*60*60,//using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 -// Because of dateAddRemove treats 24 hours as different from a -// day when working around DST, we need to store them separately - this._days=+g+7*f, -// It is impossible translate months into days without knowing -// which months you are are talking about, so we have to store -// it separately. - this._months=+e+3*d+12*c,this._data={},this._locale=bb(),this._bubble()}function xb(a){return a instanceof wb}function yb(a){return a<0?Math.round(-1*a)*-1:Math.round(a)} -// FORMATTING - function zb(a,b){U(a,0,0,function(){var a=this.utcOffset(),c="+";return a<0&&(a=-a,c="-"),c+T(~~(a/60),2)+b+T(~~a%60,2)})}function Ab(a,b){var c=(b||"").match(a);if(null===c)return null;var d=c[c.length-1]||[],e=(d+"").match(Me)||["-",0,0],f=+(60*e[1])+u(e[2]);return 0===f?0:"+"===e[0]?f:-f} -// Return a moment from input, that is local/utc/zone equivalent to model. - function Bb(b,c){var d,e; -// Use low-level api, because this fn is low-level api. - return c._isUTC?(d=c.clone(),e=(s(b)||g(b)?b.valueOf():sb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):sb(b).local()}function Cb(a){ -// On Firefox.24 Date#getTimezoneOffset returns a floating point. -// https://github.com/moment/moment/pull/1871 - return 15*-Math.round(a._d.getTimezoneOffset()/15)} -// MOMENTS -// keepLocalTime = true means only change the timezone, without -// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> -// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset -// +0200, so we adjust the time as needed, to be valid. -// -// Keeping the time actually adds/subtracts (one hour) -// from the actual represented time. That is why we call updateOffset -// a second time. In case it wants us to change the offset again -// _changeInProgress == true case, then we have to adjust, because -// there is no such time in the given timezone. - function Db(b,c){var d,e=this._offset||0;if(!this.isValid())return null!=b?this:NaN;if(null!=b){if("string"==typeof b){if(b=Ab(Xd,b),null===b)return this}else Math.abs(b)<16&&(b=60*b);return!this._isUTC&&c&&(d=Cb(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Tb(this,Ob(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?e:Cb(this)}function Eb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Fb(a){return this.utcOffset(0,a)}function Gb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Cb(this),"m")),this}function Hb(){if(null!=this._tzm)this.utcOffset(this._tzm);else if("string"==typeof this._i){var a=Ab(Wd,this._i);null!=a?this.utcOffset(a):this.utcOffset(0,!0)}return this}function Ib(a){return!!this.isValid()&&(a=a?sb(a).utcOffset():0,(this.utcOffset()-a)%60===0)}function Jb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Kb(){if(!p(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=pb(a),a._a){var b=a._isUTC?k(a._a):sb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Lb(){return!!this.isValid()&&!this._isUTC}function Mb(){return!!this.isValid()&&this._isUTC}function Nb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Ob(a,b){var c,d,e,g=a, -// matching against regexp is expensive, do it on demand - h=null;// checks for null or undefined - return xb(a)?g={ms:a._milliseconds,d:a._days,M:a._months}:f(a)?(g={},b?g[b]=a:g.milliseconds=a):(h=Ne.exec(a))?(c="-"===h[1]?-1:1,g={y:0,d:u(h[ce])*c,h:u(h[de])*c,m:u(h[ee])*c,s:u(h[fe])*c,ms:u(yb(1e3*h[ge]))*c}):(h=Oe.exec(a))?(c="-"===h[1]?-1:1,g={y:Pb(h[2],c),M:Pb(h[3],c),w:Pb(h[4],c),d:Pb(h[5],c),h:Pb(h[6],c),m:Pb(h[7],c),s:Pb(h[8],c)}):null==g?g={}:"object"==typeof g&&("from"in g||"to"in g)&&(e=Rb(sb(g.from),sb(g.to)),g={},g.ms=e.milliseconds,g.M=e.months),d=new wb(g),xb(a)&&i(a,"_locale")&&(d._locale=a._locale),d}function Pb(a,b){ -// We'd normally use ~~inp for this, but unfortunately it also -// converts floats to ints. -// inp may be undefined, so careful calling replace on it. - var c=a&&parseFloat(a.replace(",",".")); -// apply sign while we're at it - return(isNaN(c)?0:c)*b}function Qb(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Rb(a,b){var c;return a.isValid()&&b.isValid()?(b=Bb(b,a),a.isBefore(b)?c=Qb(a,b):(c=Qb(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}} -// TODO: remove 'name' arg after deprecation is removed - function Sb(a,b){return function(c,d){var e,f; -//invert the arguments, but complain about it - return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ob(c,d),Tb(this,e,a),this}}function Tb(b,c,d,e){var f=c._milliseconds,g=yb(c._days),h=yb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Ub(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Vb(b,c){ -// We want to compare the start of today, vs this. -// Getting start-of-today depends on whether we're local/utc/offset or not. - var d=b||sb(),e=Bb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,sb(d)))}function Wb(){return new r(this)}function Xb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()<this.clone().startOf(b).valueOf())}function Yb(a,b){var c=s(a)?a:sb(a);return!(!this.isValid()||!c.isValid())&&(b=K(p(b)?"millisecond":b),"millisecond"===b?this.valueOf()<c.valueOf():this.clone().endOf(b).valueOf()<c.valueOf())}function Zb(a,b,c,d){return d=d||"()",("("===d[0]?this.isAfter(a,c):!this.isBefore(a,c))&&(")"===d[1]?this.isBefore(b,c):!this.isAfter(b,c))}function $b(a,b){var c,d=s(a)?a:sb(a);return!(!this.isValid()||!d.isValid())&&(b=K(b||"millisecond"),"millisecond"===b?this.valueOf()===d.valueOf():(c=d.valueOf(),this.clone().startOf(b).valueOf()<=c&&c<=this.clone().endOf(b).valueOf()))}function _b(a,b){return this.isSame(a,b)||this.isAfter(a,b)}function ac(a,b){return this.isSame(a,b)||this.isBefore(a,b)}function bc(a,b,c){var d,e,f,g;// 1000 -// 1000 * 60 -// 1000 * 60 * 60 -// 1000 * 60 * 60 * 24, negate dst -// 1000 * 60 * 60 * 24 * 7, negate dst - return this.isValid()?(d=Bb(a,this),d.isValid()?(e=6e4*(d.utcOffset()-this.utcOffset()),b=K(b),"year"===b||"month"===b||"quarter"===b?(g=cc(this,d),"quarter"===b?g/=3:"year"===b&&(g/=12)):(f=this-d,g="second"===b?f/1e3:"minute"===b?f/6e4:"hour"===b?f/36e5:"day"===b?(f-e)/864e5:"week"===b?(f-e)/6048e5:f),c?g:t(g)):NaN):NaN}function cc(a,b){ -// difference in months - var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()), -// b is in (anchor - 1 month, anchor + 1 month) - f=a.clone().add(e,"months"); -//check for negative zero, return zero if negative zero -// linear across the month -// linear across the month - return b-f<0?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function dc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function ec(){var a=this.clone().utc();return 0<a.year()&&a.year()<=9999?z(Date.prototype.toISOString)?this.toDate().toISOString():X(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):X(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}/** - * Return a human readable representation of a moment that can - * also be evaluated to get a new moment which is the same - * - * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects - */ - function fc(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var a="moment",b="";this.isLocal()||(a=0===this.utcOffset()?"moment.utc":"moment.parseZone",b="Z");var c="["+a+'("]',d=0<this.year()&&this.year()<=9999?"YYYY":"YYYYYY",e="-MM-DD[T]HH:mm:ss.SSS",f=b+'[")]';return this.format(c+d+e+f)}function gc(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=X(this,b);return this.localeData().postformat(c)}function hc(a,b){return this.isValid()&&(s(a)&&a.isValid()||sb(a).isValid())?Ob({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function ic(a){return this.from(sb(),a)}function jc(a,b){return this.isValid()&&(s(a)&&a.isValid()||sb(a).isValid())?Ob({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function kc(a){return this.to(sb(),a)} -// If passed a locale key, it will set the locale for this -// instance. Otherwise, it will return the locale configuration -// variables for this instance. - function lc(a){var b;return void 0===a?this._locale._abbr:(b=bb(a),null!=b&&(this._locale=b),this)}function mc(){return this._locale}function nc(a){ -// the following switch intentionally omits break keywords -// to utilize falling through the cases. - switch(a=K(a)){case"year":this.month(0);/* falls through */ - case"quarter":case"month":this.date(1);/* falls through */ - case"week":case"isoWeek":case"day":case"date":this.hours(0);/* falls through */ - case"hour":this.minutes(0);/* falls through */ - case"minute":this.seconds(0);/* falls through */ - case"second":this.milliseconds(0)} -// weeks are a special case -// quarters are also special - return"week"===a&&this.weekday(0),"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this}function oc(a){ -// 'date' is an alias for 'day', so it should be considered as such. - return a=K(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function pc(){return this._d.valueOf()-6e4*(this._offset||0)}function qc(){return Math.floor(this.valueOf()/1e3)}function rc(){return new Date(this.valueOf())}function sc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function tc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function uc(){ -// new Date(NaN).toJSON() === null - return this.isValid()?this.toISOString():null}function vc(){return n(this)}function wc(){return j({},m(this))}function xc(){return m(this).overflow}function yc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function zc(a,b){U(0,[a,a.length],0,b)} -// MOMENTS - function Ac(a){return Ec.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function Bc(a){return Ec.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Cc(){return xa(this.year(),1,4)}function Dc(){var a=this.localeData()._week;return xa(this.year(),a.dow,a.doy)}function Ec(a,b,c,d,e){var f;return null==a?wa(this,d,e).year:(f=xa(a,d,e),b>f&&(b=f),Fc.call(this,a,b,c,d,e))}function Fc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this} -// MOMENTS - function Gc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)} -// HELPERS -// MOMENTS - function Hc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Ic(a,b){b[ge]=u(1e3*("0."+a))} -// MOMENTS - function Jc(){return this._isUTC?"UTC":""}function Kc(){return this._isUTC?"Coordinated Universal Time":""}function Lc(a){return sb(1e3*a)}function Mc(){return sb.apply(null,arguments).parseZone()}function Nc(a){return a}function Oc(a,b,c,d){var e=bb(),f=k().set(d,b);return e[c](f,a)}function Pc(a,b,c){if(f(a)&&(b=a,a=void 0),a=a||"",null!=b)return Oc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Oc(a,d,c,"month");return e} -// () -// (5) -// (fmt, 5) -// (fmt) -// (true) -// (true, 5) -// (true, fmt, 5) -// (true, fmt) - function Qc(a,b,c,d){"boolean"==typeof a?(f(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,f(b)&&(c=b,b=void 0),b=b||"");var e=bb(),g=a?e._week.dow:0;if(null!=c)return Oc(b,(c+g)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Oc(b,(h+g)%7,d,"day");return i}function Rc(a,b){return Pc(a,b,"months")}function Sc(a,b){return Pc(a,b,"monthsShort")}function Tc(a,b,c){return Qc(a,b,c,"weekdays")}function Uc(a,b,c){return Qc(a,b,c,"weekdaysShort")}function Vc(a,b,c){return Qc(a,b,c,"weekdaysMin")}function Wc(){var a=this._data;return this._milliseconds=Ze(this._milliseconds),this._days=Ze(this._days),this._months=Ze(this._months),a.milliseconds=Ze(a.milliseconds),a.seconds=Ze(a.seconds),a.minutes=Ze(a.minutes),a.hours=Ze(a.hours),a.months=Ze(a.months),a.years=Ze(a.years),this}function Xc(a,b,c,d){var e=Ob(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()} -// supports only 2.0-style add(1, 's') or add(duration) - function Yc(a,b){return Xc(this,a,b,1)} -// supports only 2.0-style subtract(1, 's') or subtract(duration) - function Zc(a,b){return Xc(this,a,b,-1)}function $c(a){return a<0?Math.floor(a):Math.ceil(a)}function _c(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data; -// if we have a mix of positive and negative values, bubble down first -// check: https://github.com/moment/moment/issues/2166 -// The following code bubbles up values, see the tests for -// examples of what that means. -// convert days to months -// 12 months -> 1 year - return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*$c(bd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ad(g)),h+=e,g-=$c(bd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ad(a){ -// 400 years have 146097 days (taking into account leap year rules) -// 400 years have 12 months === 4800 - return 4800*a/146097}function bd(a){ -// the reverse of daysToMonths - return 146097*a/4800}function cd(a){var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ad(b),"month"===a?c:c/12;switch( -// handle milliseconds separately because of floating point math errors (issue #1867) - b=this._days+Math.round(bd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3; -// Math.floor prevents floating point math errors here - case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}} -// TODO: Use this.as('ms')? - function dd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)}function ed(a){return function(){return this.as(a)}}function fd(a){return a=K(a),this[a+"s"]()}function gd(a){return function(){return this._data[a]}}function hd(){return t(this.days()/7)} -// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function id(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function jd(a,b,c){var d=Ob(a).abs(),e=of(d.as("s")),f=of(d.as("m")),g=of(d.as("h")),h=of(d.as("d")),i=of(d.as("M")),j=of(d.as("y")),k=e<pf.s&&["s",e]||f<=1&&["m"]||f<pf.m&&["mm",f]||g<=1&&["h"]||g<pf.h&&["hh",g]||h<=1&&["d"]||h<pf.d&&["dd",h]||i<=1&&["M"]||i<pf.M&&["MM",i]||j<=1&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,id.apply(null,k)} -// This function allows you to set the rounding function for relative time strings - function kd(a){return void 0===a?of:"function"==typeof a&&(of=a,!0)} -// This function allows you to set a threshold for relative time strings - function ld(a,b){return void 0!==pf[a]&&(void 0===b?pf[a]:(pf[a]=b,!0))}function md(a){var b=this.localeData(),c=jd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function nd(){ -// for ISO strings we do not use the normal bubbling rules: -// * milliseconds bubble up until they become hours -// * days do not bubble at all -// * months bubble up until they become years -// This is because there is no context-free conversion between hours and days -// (think of clock changes) -// and also not between days and months (28-31 days per month) - var a,b,c,d=qf(this._milliseconds)/1e3,e=qf(this._days),f=qf(this._months); -// 3600 seconds -> 60 minutes -> 1 hour - a=t(d/60),b=t(a/60),d%=60,a%=60, -// 12 months -> 1 year - c=t(f/12),f%=12; -// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var od,pd;pd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d<c;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1};var qd=pd,rd=a.momentProperties=[],sd=!1,td={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var ud;ud=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)i(a,b)&&c.push(b);return c};var vd,wd=ud,xd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},yd={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},zd="Invalid date",Ad="%d",Bd=/\d{1,2}/,Cd={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Dd={},Ed={},Fd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Gd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Hd={},Id={},Jd=/\d/,Kd=/\d\d/,Ld=/\d{3}/,Md=/\d{4}/,Nd=/[+-]?\d{6}/,Od=/\d\d?/,Pd=/\d\d\d\d?/,Qd=/\d\d\d\d\d\d?/,Rd=/\d{1,3}/,Sd=/\d{1,4}/,Td=/[+-]?\d{1,6}/,Ud=/\d+/,Vd=/[+-]?\d+/,Wd=/Z|[+-]\d\d:?\d\d/gi,Xd=/Z|[+-]\d\d(?::?\d\d)?/gi,Yd=/[+-]?\d+(\.\d{1,3})?/,Zd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,$d={},_d={},ae=0,be=1,ce=2,de=3,ee=4,fe=5,ge=6,he=7,ie=8;vd=Array.prototype.indexOf?Array.prototype.indexOf:function(a){ -// I know - var b;for(b=0;b<this.length;++b)if(this[b]===a)return b;return-1};var je=vd; -// FORMATTING - U("M",["MM",2],"Mo",function(){return this.month()+1}),U("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),U("MMMM",0,0,function(a){return this.localeData().months(this,a)}), -// ALIASES - J("month","M"), -// PRIORITY - M("month",8), -// PARSING - Z("M",Od),Z("MM",Od,Kd),Z("MMM",function(a,b){return b.monthsShortRegex(a)}),Z("MMMM",function(a,b){return b.monthsRegex(a)}),ba(["M","MM"],function(a,b){b[be]=u(a)-1}),ba(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict); -// if we didn't find a month name, mark the date as invalid. - null!=e?b[be]=e:m(c).invalidMonth=a}); -// LOCALES - var ke=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,le="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),me="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),ne=Zd,oe=Zd; -// FORMATTING - U("Y",0,0,function(){var a=this.year();return a<=9999?""+a:"+"+a}),U(0,["YY",2],0,function(){return this.year()%100}),U(0,["YYYY",4],0,"year"),U(0,["YYYYY",5],0,"year"),U(0,["YYYYYY",6,!0],0,"year"), -// ALIASES - J("year","y"), -// PRIORITIES - M("year",1), -// PARSING - Z("Y",Vd),Z("YY",Od,Kd),Z("YYYY",Sd,Md),Z("YYYYY",Td,Nd),Z("YYYYYY",Td,Nd),ba(["YYYYY","YYYYYY"],ae),ba("YYYY",function(b,c){c[ae]=2===b.length?a.parseTwoDigitYear(b):u(b)}),ba("YY",function(b,c){c[ae]=a.parseTwoDigitYear(b)}),ba("Y",function(a,b){b[ae]=parseInt(a,10)}), -// HOOKS - a.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)}; -// MOMENTS - var pe=O("FullYear",!0); -// FORMATTING - U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"), -// ALIASES - J("week","w"),J("isoWeek","W"), -// PRIORITIES - M("week",5),M("isoWeek",5), -// PARSING - Z("w",Od),Z("ww",Od,Kd),Z("W",Od),Z("WW",Od,Kd),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var qe={dow:0,// Sunday is the first day of the week. - doy:6}; -// FORMATTING - U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"), -// ALIASES - J("day","d"),J("weekday","e"),J("isoWeekday","E"), -// PRIORITY - M("day",11),M("weekday",11),M("isoWeekday",11), -// PARSING - Z("d",Od),Z("e",Od),Z("E",Od),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict); -// if we didn't get a weekday name, mark the date as invalid - null!=e?b.d=e:m(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)}); -// LOCALES - var re="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),se="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),te="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ue=Zd,ve=Zd,we=Zd;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1), -// ALIASES - J("hour","h"), -// PRIORITY - M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Od),Z("h",Od),Z("HH",Od,Kd),Z("hh",Od,Kd),Z("hmm",Pd),Z("hmmss",Qd),Z("Hmm",Pd),Z("Hmmss",Qd),ba(["H","HH"],de),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[de]=u(a),m(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d)),m(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e)),m(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[de]=u(a.substr(0,d)),b[ee]=u(a.substr(d,2)),b[fe]=u(a.substr(e))});var xe,ye=/[ap]\.?m?\.?/i,ze=O("Hours",!0),Ae={calendar:xd,longDateFormat:yd,invalidDate:zd,ordinal:Ad,ordinalParse:Bd,relativeTime:Cd,months:le,monthsShort:me,week:qe,weekdays:re,weekdaysMin:te,weekdaysShort:se,meridiemParse:ye},Be={},Ce={},De=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ee=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Fe=/Z|[+-]\d\d(?::?\d\d)?/,Ge=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/], -// YYYYMM is NOT allowed by the standard - ["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],He=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ie=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=x("value provided is not in a recognized ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}), -// constant that refers to the ISO standard - a.ISO_8601=function(){};var Je=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?a<this?this:a:o()}),Ke=x("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=sb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:o()}),Le=function(){return Date.now?Date.now():+new Date};zb("Z",":"),zb("ZZ",""), -// PARSING - Z("Z",Xd),Z("ZZ",Xd),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ab(Xd,a)}); -// HELPERS -// timezone chunker -// '+10:00' > ['10', '00'] -// '-1530' > ['-15', '30'] - var Me=/([\+\-]|\d\d)/gi; -// HOOKS -// This function will be called whenever a moment is mutated. -// It is intended to keep the offset in sync with the timezone. - a.updateOffset=function(){}; -// ASP.NET json date format regex - var Ne=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Oe=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Ob.fn=wb.prototype;var Pe=Sb(1,"add"),Qe=Sb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Re=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)}); -// FORMATTING - U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),zc("gggg","weekYear"),zc("ggggg","weekYear"),zc("GGGG","isoWeekYear"),zc("GGGGG","isoWeekYear"), -// ALIASES - J("weekYear","gg"),J("isoWeekYear","GG"), -// PRIORITY - M("weekYear",1),M("isoWeekYear",1), -// PARSING - Z("G",Vd),Z("g",Vd),Z("GG",Od,Kd),Z("gg",Od,Kd),Z("GGGG",Sd,Md),Z("gggg",Sd,Md),Z("GGGGG",Td,Nd),Z("ggggg",Td,Nd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}), -// FORMATTING - U("Q",0,"Qo","quarter"), -// ALIASES - J("quarter","Q"), -// PRIORITY - M("quarter",7), -// PARSING - Z("Q",Jd),ba("Q",function(a,b){b[be]=3*(u(a)-1)}), -// FORMATTING - U("D",["DD",2],"Do","date"), -// ALIASES - J("date","D"), -// PRIOROITY - M("date",9), -// PARSING - Z("D",Od),Z("DD",Od,Kd),Z("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),ba(["D","DD"],ce),ba("Do",function(a,b){b[ce]=u(a.match(Od)[0],10)}); -// MOMENTS - var Se=O("Date",!0); -// FORMATTING - U("DDD",["DDDD",3],"DDDo","dayOfYear"), -// ALIASES - J("dayOfYear","DDD"), -// PRIORITY - M("dayOfYear",4), -// PARSING - Z("DDD",Rd),Z("DDDD",Ld),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}), -// FORMATTING - U("m",["mm",2],0,"minute"), -// ALIASES - J("minute","m"), -// PRIORITY - M("minute",14), -// PARSING - Z("m",Od),Z("mm",Od,Kd),ba(["m","mm"],ee); -// MOMENTS - var Te=O("Minutes",!1); -// FORMATTING - U("s",["ss",2],0,"second"), -// ALIASES - J("second","s"), -// PRIORITY - M("second",15), -// PARSING - Z("s",Od),Z("ss",Od,Kd),ba(["s","ss"],fe); -// MOMENTS - var Ue=O("Seconds",!1); -// FORMATTING - U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}), -// ALIASES - J("millisecond","ms"), -// PRIORITY - M("millisecond",16), -// PARSING - Z("S",Rd,Jd),Z("SS",Rd,Kd),Z("SSS",Rd,Ld);var Ve;for(Ve="SSSS";Ve.length<=9;Ve+="S")Z(Ve,Ud);for(Ve="S";Ve.length<=9;Ve+="S")ba(Ve,Ic); -// MOMENTS - var We=O("Milliseconds",!1); -// FORMATTING - U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var Xe=r.prototype;Xe.add=Pe,Xe.calendar=Vb,Xe.clone=Wb,Xe.diff=bc,Xe.endOf=oc,Xe.format=gc,Xe.from=hc,Xe.fromNow=ic,Xe.to=jc,Xe.toNow=kc,Xe.get=R,Xe.invalidAt=xc,Xe.isAfter=Xb,Xe.isBefore=Yb,Xe.isBetween=Zb,Xe.isSame=$b,Xe.isSameOrAfter=_b,Xe.isSameOrBefore=ac,Xe.isValid=vc,Xe.lang=Re,Xe.locale=lc,Xe.localeData=mc,Xe.max=Ke,Xe.min=Je,Xe.parsingFlags=wc,Xe.set=S,Xe.startOf=nc,Xe.subtract=Qe,Xe.toArray=sc,Xe.toObject=tc,Xe.toDate=rc,Xe.toISOString=ec,Xe.inspect=fc,Xe.toJSON=uc,Xe.toString=dc,Xe.unix=qc,Xe.valueOf=pc,Xe.creationData=yc, -// Year - Xe.year=pe,Xe.isLeapYear=ra, -// Week Year - Xe.weekYear=Ac,Xe.isoWeekYear=Bc, -// Quarter - Xe.quarter=Xe.quarters=Gc, -// Month - Xe.month=ka,Xe.daysInMonth=la, -// Week - Xe.week=Xe.weeks=Ba,Xe.isoWeek=Xe.isoWeeks=Ca,Xe.weeksInYear=Dc,Xe.isoWeeksInYear=Cc, -// Day - Xe.date=Se,Xe.day=Xe.days=Ka,Xe.weekday=La,Xe.isoWeekday=Ma,Xe.dayOfYear=Hc, -// Hour - Xe.hour=Xe.hours=ze, -// Minute - Xe.minute=Xe.minutes=Te, -// Second - Xe.second=Xe.seconds=Ue, -// Millisecond - Xe.millisecond=Xe.milliseconds=We, -// Offset - Xe.utcOffset=Db,Xe.utc=Fb,Xe.local=Gb,Xe.parseZone=Hb,Xe.hasAlignedHourOffset=Ib,Xe.isDST=Jb,Xe.isLocal=Lb,Xe.isUtcOffset=Mb,Xe.isUtc=Nb,Xe.isUTC=Nb, -// Timezone - Xe.zoneAbbr=Jc,Xe.zoneName=Kc, -// Deprecations - Xe.dates=x("dates accessor is deprecated. Use date instead.",Se),Xe.months=x("months accessor is deprecated. Use month instead",ka),Xe.years=x("years accessor is deprecated. Use year instead",pe),Xe.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Eb),Xe.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Kb);var Ye=C.prototype;Ye.calendar=D,Ye.longDateFormat=E,Ye.invalidDate=F,Ye.ordinal=G,Ye.preparse=Nc,Ye.postformat=Nc,Ye.relativeTime=H,Ye.pastFuture=I,Ye.set=A, -// Month - Ye.months=fa,Ye.monthsShort=ga,Ye.monthsParse=ia,Ye.monthsRegex=na,Ye.monthsShortRegex=ma, -// Week - Ye.week=ya,Ye.firstDayOfYear=Aa,Ye.firstDayOfWeek=za, -// Day of Week - Ye.weekdays=Fa,Ye.weekdaysMin=Ha,Ye.weekdaysShort=Ga,Ye.weekdaysParse=Ja,Ye.weekdaysRegex=Na,Ye.weekdaysShortRegex=Oa,Ye.weekdaysMinRegex=Pa, -// Hours - Ye.isPM=Va,Ye.meridiem=Wa,$a("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}), -// Side effect imports - a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var Ze=Math.abs,$e=ed("ms"),_e=ed("s"),af=ed("m"),bf=ed("h"),cf=ed("d"),df=ed("w"),ef=ed("M"),ff=ed("y"),gf=gd("milliseconds"),hf=gd("seconds"),jf=gd("minutes"),kf=gd("hours"),lf=gd("days"),mf=gd("months"),nf=gd("years"),of=Math.round,pf={s:45,// seconds to minute - m:45,// minutes to hour - h:22,// hours to day - d:26,// days to month - M:11},qf=Math.abs,rf=wb.prototype; -// Deprecations -// Side effect imports -// FORMATTING -// PARSING -// Side effect imports - return rf.abs=Wc,rf.add=Yc,rf.subtract=Zc,rf.as=cd,rf.asMilliseconds=$e,rf.asSeconds=_e,rf.asMinutes=af,rf.asHours=bf,rf.asDays=cf,rf.asWeeks=df,rf.asMonths=ef,rf.asYears=ff,rf.valueOf=dd,rf._bubble=_c,rf.get=fd,rf.milliseconds=gf,rf.seconds=hf,rf.minutes=jf,rf.hours=kf,rf.days=lf,rf.weeks=hd,rf.months=mf,rf.years=nf,rf.humanize=md,rf.toISOString=nd,rf.toString=nd,rf.toJSON=nd,rf.locale=lc,rf.localeData=mc,rf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",nd),rf.lang=Re,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Vd),Z("X",Yd),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.17.1",b(sb),a.fn=Xe,a.min=ub,a.max=vb,a.now=Le,a.utc=k,a.unix=Lc,a.months=Rc,a.isDate=g,a.locale=$a,a.invalid=o,a.duration=Ob,a.isMoment=s,a.weekdays=Tc,a.parseZone=Mc,a.localeData=bb,a.isDuration=xb,a.monthsShort=Sc,a.weekdaysMin=Vc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Uc,a.normalizeUnits=K,a.relativeTimeRounding=kd,a.relativeTimeThreshold=ld,a.calendarFormat=Ub,a.prototype=Xe,a}); \ No newline at end of file +/** + * Moment.js + * + * Version: 2.24.0 + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function c(){return e.apply(null,arguments)}function o(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function u(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e){return void 0===e}function h(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,s=[];for(n=0;n<e.length;++n)s.push(t(e[n],n));return s}function m(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function _(e,t){for(var n in t)m(t,n)&&(e[n]=t[n]);return m(t,"toString")&&(e.toString=t.toString),m(t,"valueOf")&&(e.valueOf=t.valueOf),e}function y(e,t,n,s){return Tt(e,t,n,s,!0).utc()}function g(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function v(e){if(null==e._isValid){var t=g(e),n=i.call(t.parsedDateParts,function(e){return null!=e}),s=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return s;e._isValid=s}return e._isValid}function p(e){var t=y(NaN);return null!=e?_(g(t),e):g(t).userInvalidated=!0,t}i=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1};var r=c.momentProperties=[];function w(e,t){var n,s,i;if(l(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),l(t._i)||(e._i=t._i),l(t._f)||(e._f=t._f),l(t._l)||(e._l=t._l),l(t._strict)||(e._strict=t._strict),l(t._tzm)||(e._tzm=t._tzm),l(t._isUTC)||(e._isUTC=t._isUTC),l(t._offset)||(e._offset=t._offset),l(t._pf)||(e._pf=g(t)),l(t._locale)||(e._locale=t._locale),0<r.length)for(n=0;n<r.length;n++)l(i=t[s=r[n]])||(e[s]=i);return e}var t=!1;function M(e){w(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===t&&(t=!0,c.updateOffset(this),t=!1)}function k(e){return e instanceof M||null!=e&&null!=e._isAMomentObject}function S(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function D(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=S(t)),n}function a(e,t,n){var s,i=Math.min(e.length,t.length),r=Math.abs(e.length-t.length),a=0;for(s=0;s<i;s++)(n&&e[s]!==t[s]||!n&&D(e[s])!==D(t[s]))&&a++;return a+r}function Y(e){!1===c.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function n(i,r){var a=!0;return _(function(){if(null!=c.deprecationHandler&&c.deprecationHandler(null,i),a){for(var e,t=[],n=0;n<arguments.length;n++){if(e="","object"==typeof arguments[n]){for(var s in e+="\n["+n+"] ",arguments[0])e+=s+": "+arguments[0][s]+", ";e=e.slice(0,-2)}else e=arguments[n];t.push(e)}Y(i+"\nArguments: "+Array.prototype.slice.call(t).join("")+"\n"+(new Error).stack),a=!1}return r.apply(this,arguments)},r)}var s,O={};function T(e,t){null!=c.deprecationHandler&&c.deprecationHandler(e,t),O[e]||(Y(t),O[e]=!0)}function b(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function x(e,t){var n,s=_({},e);for(n in t)m(t,n)&&(u(e[n])&&u(t[n])?(s[n]={},_(s[n],e[n]),_(s[n],t[n])):null!=t[n]?s[n]=t[n]:delete s[n]);for(n in e)m(e,n)&&!m(t,n)&&u(e[n])&&(s[n]=_({},s[n]));return s}function P(e){null!=e&&this.set(e)}c.suppressDeprecationWarnings=!1,c.deprecationHandler=null,s=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)m(e,t)&&n.push(t);return n};var W={};function C(e,t){var n=e.toLowerCase();W[n]=W[n+"s"]=W[t]=e}function H(e){return"string"==typeof e?W[e]||W[e.toLowerCase()]:void 0}function R(e){var t,n,s={};for(n in e)m(e,n)&&(t=H(n))&&(s[t]=e[n]);return s}var U={};function F(e,t){U[e]=t}function L(e,t,n){var s=""+Math.abs(e),i=t-s.length;return(0<=e?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+s}var N=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,G=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,V={},E={};function I(e,t,n,s){var i=s;"string"==typeof s&&(i=function(){return this[s]()}),e&&(E[e]=i),t&&(E[t[0]]=function(){return L(i.apply(this,arguments),t[1],t[2])}),n&&(E[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function A(e,t){return e.isValid()?(t=j(t,e.localeData()),V[t]=V[t]||function(s){var e,i,t,r=s.match(N);for(e=0,i=r.length;e<i;e++)E[r[e]]?r[e]=E[r[e]]:r[e]=(t=r[e]).match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"");return function(e){var t,n="";for(t=0;t<i;t++)n+=b(r[t])?r[t].call(e,s):r[t];return n}}(t),V[t](e)):e.localeData().invalidDate()}function j(e,t){var n=5;function s(e){return t.longDateFormat(e)||e}for(G.lastIndex=0;0<=n&&G.test(e);)e=e.replace(G,s),G.lastIndex=0,n-=1;return e}var Z=/\d/,z=/\d\d/,$=/\d{3}/,q=/\d{4}/,J=/[+-]?\d{6}/,B=/\d\d?/,Q=/\d\d\d\d?/,X=/\d\d\d\d\d\d?/,K=/\d{1,3}/,ee=/\d{1,4}/,te=/[+-]?\d{1,6}/,ne=/\d+/,se=/[+-]?\d+/,ie=/Z|[+-]\d\d:?\d\d/gi,re=/Z|[+-]\d\d(?::?\d\d)?/gi,ae=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,oe={};function ue(e,n,s){oe[e]=b(n)?n:function(e,t){return e&&s?s:n}}function le(e,t){return m(oe,e)?oe[e](t._strict,t._locale):new RegExp(he(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function he(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var de={};function ce(e,n){var t,s=n;for("string"==typeof e&&(e=[e]),h(n)&&(s=function(e,t){t[n]=D(e)}),t=0;t<e.length;t++)de[e[t]]=s}function fe(e,i){ce(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}var me=0,_e=1,ye=2,ge=3,ve=4,pe=5,we=6,Me=7,ke=8;function Se(e){return De(e)?366:365}function De(e){return e%4==0&&e%100!=0||e%400==0}I("Y",0,0,function(){var e=this.year();return e<=9999?""+e:"+"+e}),I(0,["YY",2],0,function(){return this.year()%100}),I(0,["YYYY",4],0,"year"),I(0,["YYYYY",5],0,"year"),I(0,["YYYYYY",6,!0],0,"year"),C("year","y"),F("year",1),ue("Y",se),ue("YY",B,z),ue("YYYY",ee,q),ue("YYYYY",te,J),ue("YYYYYY",te,J),ce(["YYYYY","YYYYYY"],me),ce("YYYY",function(e,t){t[me]=2===e.length?c.parseTwoDigitYear(e):D(e)}),ce("YY",function(e,t){t[me]=c.parseTwoDigitYear(e)}),ce("Y",function(e,t){t[me]=parseInt(e,10)}),c.parseTwoDigitYear=function(e){return D(e)+(68<D(e)?1900:2e3)};var Ye,Oe=Te("FullYear",!0);function Te(t,n){return function(e){return null!=e?(xe(this,t,e),c.updateOffset(this,n),this):be(this,t)}}function be(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function xe(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&De(e.year())&&1===e.month()&&29===e.date()?e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Pe(n,e.month())):e._d["set"+(e._isUTC?"UTC":"")+t](n))}function Pe(e,t){if(isNaN(e)||isNaN(t))return NaN;var n,s=(t%(n=12)+n)%n;return e+=(t-s)/12,1===s?De(e)?29:28:31-s%7%2}Ye=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},I("M",["MM",2],"Mo",function(){return this.month()+1}),I("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),I("MMMM",0,0,function(e){return this.localeData().months(this,e)}),C("month","M"),F("month",8),ue("M",B),ue("MM",B,z),ue("MMM",function(e,t){return t.monthsShortRegex(e)}),ue("MMMM",function(e,t){return t.monthsRegex(e)}),ce(["M","MM"],function(e,t){t[_e]=D(e)-1}),ce(["MMM","MMMM"],function(e,t,n,s){var i=n._locale.monthsParse(e,s,n._strict);null!=i?t[_e]=i:g(n).invalidMonth=e});var We=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Ce="January_February_March_April_May_June_July_August_September_October_November_December".split("_");var He="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Re(e,t){var n;if(!e.isValid())return e;if("string"==typeof t)if(/^\d+$/.test(t))t=D(t);else if(!h(t=e.localeData().monthsParse(t)))return e;return n=Math.min(e.date(),Pe(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e}function Ue(e){return null!=e?(Re(this,e),c.updateOffset(this,!0),this):be(this,"Month")}var Fe=ae;var Le=ae;function Ne(){function e(e,t){return t.length-e.length}var t,n,s=[],i=[],r=[];for(t=0;t<12;t++)n=y([2e3,t]),s.push(this.monthsShort(n,"")),i.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(s.sort(e),i.sort(e),r.sort(e),t=0;t<12;t++)s[t]=he(s[t]),i[t]=he(i[t]);for(t=0;t<24;t++)r[t]=he(r[t]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Ge(e){var t;if(e<100&&0<=e){var n=Array.prototype.slice.call(arguments);n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)}else t=new Date(Date.UTC.apply(null,arguments));return t}function Ve(e,t,n){var s=7+t-n;return-((7+Ge(e,0,s).getUTCDay()-t)%7)+s-1}function Ee(e,t,n,s,i){var r,a,o=1+7*(t-1)+(7+n-s)%7+Ve(e,s,i);return a=o<=0?Se(r=e-1)+o:o>Se(e)?(r=e+1,o-Se(e)):(r=e,o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(Se(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),C("week","w"),C("isoWeek","W"),F("week",5),F("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=D(e)});function je(e,t){return e.slice(t,7).concat(e.slice(0,t))}I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),C("day","d"),C("weekday","e"),C("isoWeekday","E"),F("day",11),F("weekday",11),F("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=D(e)});var Ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var $e="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var qe=ae;var Je=ae;var Be=ae;function Qe(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=he(o[t]),u[t]=he(u[t]),l[t]=he(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Xe(){return this.hours()%12||12}function Ke(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function et(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Xe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Xe.apply(this)+L(this.minutes(),2)+L(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+L(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+L(this.minutes(),2)+L(this.seconds(),2)}),Ke("a",!0),Ke("A",!1),C("hour","h"),F("hour",13),ue("a",et),ue("A",et),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=D(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=D(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=D(e.substr(0,s)),t[ve]=D(e.substr(s,2)),t[pe]=D(e.substr(i))});var tt,nt=Te("Hours",!0),st={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:He,week:{dow:0,doy:6},weekdays:Ze,weekdaysMin:$e,weekdaysShort:ze,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=tt._abbr,require("./locale/"+e),ut(t)}catch(e){}return it[e]}function ut(e,t){var n;return e&&((n=l(t)?ht(e):lt(e,t))?tt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tt._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,s=st;if(t.abbr=e,null!=it[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])s=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return it[e]=new P(x(s,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),ut(e),it[e]}function ht(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tt;if(!o(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=at(e[r]).split("-")).length,n=(n=at(e[r+1]))?n.split("-"):null;0<t;){if(s=ot(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&a(i,n,!0)>=t-1)break;t--}r++}return tt}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11<n[_e]?_e:n[ye]<1||n[ye]>Pe(n[me],n[_e])?ye:n[ge]<0||24<n[ge]||24===n[ge]&&(0!==n[ve]||0!==n[pe]||0!==n[we])?ge:n[ve]<0||59<n[ve]?ve:n[pe]<0||59<n[pe]?pe:n[we]<0||999<n[we]?we:-1,g(e)._overflowDayOfYear&&(t<me||ye<t)&&(t=ye),g(e)._overflowWeeks&&-1===t&&(t=Me),g(e)._overflowWeekday&&-1===t&&(t=ke),g(e).overflow=t),e}function ct(e,t,n){return null!=e?e:null!=t?t:n}function ft(e){var t,n,s,i,r,a=[];if(!e._d){var o,u;for(o=e,u=new Date(c.now()),s=o._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],e._w&&null==e._a[ye]&&null==e._a[_e]&&function(e){var t,n,s,i,r,a,o,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)r=1,a=4,n=ct(t.GG,e._a[me],Ie(bt(),1,4).year),s=ct(t.W,1),((i=ct(t.E,1))<1||7<i)&&(u=!0);else{r=e._locale._week.dow,a=e._locale._week.doy;var l=Ie(bt(),r,a);n=ct(t.gg,e._a[me],l.year),s=ct(t.w,l.week),null!=t.d?((i=t.d)<0||6<i)&&(u=!0):null!=t.e?(i=t.e+r,(t.e<0||6<t.e)&&(u=!0)):i=r}s<1||s>Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ct(e._a[me],s[me]),(e._dayOfYear>Se(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[pe]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var mt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,yt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function wt(e){var t,n,s,i,r,a,o=e._i,u=mt.exec(o)||_t.exec(o);if(u){for(g(e).iso=!0,t=0,n=gt.length;t<n;t++)if(gt[t][1].exec(u[1])){i=gt[t][0],s=!1!==gt[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=vt.length;t<n;t++)if(vt[t][1].exec(u[3])){r=(u[2]||" ")+vt[t][0];break}if(null==r)return void(e._isValid=!1)}if(!s&&null!=r)return void(e._isValid=!1);if(u[4]){if(!yt.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),Yt(e)}else e._isValid=!1}var Mt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function kt(e,t,n,s,i,r){var a=[function(e){var t=parseInt(e,10);{if(t<=49)return 2e3+t;if(t<=999)return 1900+t}return t}(e),He.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&a.push(parseInt(r,10)),a}var St={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Dt(e){var t,n,s,i=Mt.exec(e._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var r=kt(i[4],i[3],i[2],i[5],i[6],i[7]);if(t=i[1],n=r,s=e,t&&ze.indexOf(t)!==new Date(n[0],n[1],n[2]).getDay()&&(g(s).weekdayMismatch=!0,!(s._isValid=!1)))return;e._a=r,e._tzm=function(e,t,n){if(e)return St[e];if(t)return 0;var s=parseInt(n,10),i=s%100;return(s-i)/100*60+i}(i[8],i[9],i[10]),e._d=Ge.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),g(e).rfc2822=!0}else e._isValid=!1}function Yt(e){if(e._f!==c.ISO_8601)if(e._f!==c.RFC_2822){e._a=[],g(e).empty=!0;var t,n,s,i,r,a,o,u,l=""+e._i,h=l.length,d=0;for(s=j(e._f,e._locale).match(N)||[],t=0;t<s.length;t++)i=s[t],(n=(l.match(le(i,e))||[])[0])&&(0<(r=l.substr(0,l.indexOf(n))).length&&g(e).unusedInput.push(r),l=l.slice(l.indexOf(n)+n.length),d+=n.length),E[i]?(n?g(e).empty=!1:g(e).unusedTokens.push(i),a=i,u=e,null!=(o=n)&&m(de,a)&&de[a](o,u._a,u,a)):e._strict&&!n&&g(e).unusedTokens.push(i);g(e).charsLeftOver=h-d,0<l.length&&g(e).unusedInput.push(l),e._a[ge]<=12&&!0===g(e).bigHour&&0<e._a[ge]&&(g(e).bigHour=void 0),g(e).parsedDateParts=e._a.slice(0),g(e).meridiem=e._meridiem,e._a[ge]=function(e,t,n){var s;if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((s=e.isPM(n))&&t<12&&(t+=12),s||12!==t||(t=0)),t)}(e._locale,e._a[ge],e._meridiem),ft(e),dt(e)}else Dt(e);else wt(e)}function Ot(e){var t,n,s,i,r=e._i,a=e._f;return e._locale=e._locale||ht(e._l),null===r||void 0===a&&""===r?p({nullInput:!0}):("string"==typeof r&&(e._i=r=e._locale.preparse(r)),k(r)?new M(dt(r)):(d(r)?e._d=r:o(a)?function(e){var t,n,s,i,r;if(0===e._f.length)return g(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<e._f.length;i++)r=0,t=w({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],Yt(t),v(t)&&(r+=g(t).charsLeftOver,r+=10*g(t).unusedTokens.length,g(t).score=r,(null==s||r<s)&&(s=r,n=t));_(e,n||t)}(e):a?Yt(e):l(n=(t=e)._i)?t._d=new Date(c.now()):d(n)?t._d=new Date(n.valueOf()):"string"==typeof n?(s=t,null===(i=pt.exec(s._i))?(wt(s),!1===s._isValid&&(delete s._isValid,Dt(s),!1===s._isValid&&(delete s._isValid,c.createFromInputFallback(s)))):s._d=new Date(+i[1])):o(n)?(t._a=f(n.slice(0),function(e){return parseInt(e,10)}),ft(t)):u(n)?function(e){if(!e._d){var t=R(e._i);e._a=f([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),ft(e)}}(t):h(n)?t._d=new Date(n):c.createFromInputFallback(t),v(e)||(e._d=null),e))}function Tt(e,t,n,s,i){var r,a={};return!0!==n&&!1!==n||(s=n,n=void 0),(u(e)&&function(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}(e)||o(e)&&0===e.length)&&(e=void 0),a._isAMomentObject=!0,a._useUTC=a._isUTC=i,a._l=n,a._i=e,a._f=t,a._strict=s,(r=new M(dt(Ot(a))))._nextDay&&(r.add(1,"d"),r._nextDay=void 0),r}function bt(e,t,n,s){return Tt(e,t,n,s,!1)}c.createFromInputFallback=n("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),c.ISO_8601=function(){},c.RFC_2822=function(){};var xt=n("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=bt.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:p()}),Pt=n("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=bt.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:p()});function Wt(e,t){var n,s;if(1===t.length&&o(t[0])&&(t=t[0]),!t.length)return bt();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Ct=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ht(e){var t=R(e),n=t.year||0,s=t.quarter||0,i=t.month||0,r=t.week||t.isoWeek||0,a=t.day||0,o=t.hour||0,u=t.minute||0,l=t.second||0,h=t.millisecond||0;this._isValid=function(e){for(var t in e)if(-1===Ye.call(Ct,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,s=0;s<Ct.length;++s)if(e[Ct[s]]){if(n)return!1;parseFloat(e[Ct[s]])!==D(e[Ct[s]])&&(n=!0)}return!0}(t),this._milliseconds=+h+1e3*l+6e4*u+1e3*o*60*60,this._days=+a+7*r,this._months=+i+3*s+12*n,this._data={},this._locale=ht(),this._bubble()}function Rt(e){return e instanceof Ht}function Ut(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Ft(e,n){I(e,0,0,function(){var e=this.utcOffset(),t="+";return e<0&&(e=-e,t="-"),t+L(~~(e/60),2)+n+L(~~e%60,2)})}Ft("Z",":"),Ft("ZZ",""),ue("Z",re),ue("ZZ",re),ce(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Nt(re,e)});var Lt=/([\+\-]|\d\d)/gi;function Nt(e,t){var n=(t||"").match(e);if(null===n)return null;var s=((n[n.length-1]||[])+"").match(Lt)||["-",0,0],i=60*s[1]+D(s[2]);return 0===i?0:"+"===s[0]?i:-i}function Gt(e,t){var n,s;return t._isUTC?(n=t.clone(),s=(k(e)||d(e)?e.valueOf():bt(e).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+s),c.updateOffset(n,!1),n):bt(e).local()}function Vt(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Et(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}c.updateOffset=function(){};var It=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,At=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function jt(e,t){var n,s,i,r=e,a=null;return Rt(e)?r={ms:e._milliseconds,d:e._days,M:e._months}:h(e)?(r={},t?r[t]=e:r.milliseconds=e):(a=It.exec(e))?(n="-"===a[1]?-1:1,r={y:0,d:D(a[ye])*n,h:D(a[ge])*n,m:D(a[ve])*n,s:D(a[pe])*n,ms:D(Ut(1e3*a[we]))*n}):(a=At.exec(e))?(n="-"===a[1]?-1:1,r={y:Zt(a[2],n),M:Zt(a[3],n),w:Zt(a[4],n),d:Zt(a[5],n),h:Zt(a[6],n),m:Zt(a[7],n),s:Zt(a[8],n)}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(i=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=Gt(t,e),e.isBefore(t)?n=zt(e,t):((n=zt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(bt(r.from),bt(r.to)),(r={}).ms=i.milliseconds,r.M=i.months),s=new Ht(r),Rt(e)&&m(e,"_locale")&&(s._locale=e._locale),s}function Zt(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function zt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function $t(s,i){return function(e,t){var n;return null===t||isNaN(+t)||(T(i,"moment()."+i+"(period, number) is deprecated. Please use moment()."+i+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),n=e,e=t,t=n),qt(this,jt(e="string"==typeof e?+e:e,t),s),this}}function qt(e,t,n,s){var i=t._milliseconds,r=Ut(t._days),a=Ut(t._months);e.isValid()&&(s=null==s||s,a&&Re(e,be(e,"Month")+a*n),r&&xe(e,"Date",be(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s&&c.updateOffset(e,r||a))}jt.fn=Ht.prototype,jt.invalid=function(){return jt(NaN)};var Jt=$t(1,"add"),Bt=$t(-1,"subtract");function Qt(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),s=e.clone().add(n,"months");return-(n+(t-s<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(n+1,"months")-s)))||0}function Xt(e){var t;return void 0===e?this._locale._abbr:(null!=(t=ht(e))&&(this._locale=t),this)}c.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",c.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Kt=n("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function en(){return this._locale}var tn=126227808e5;function nn(e,t){return(e%t+t)%t}function sn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-tn:new Date(e,t,n).valueOf()}function rn(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-tn:Date.UTC(e,t,n)}function an(e,t){I(0,[e,e.length],0,t)}function on(e,t,n,s,i){var r;return null==e?Ie(this,s,i).year:((r=Ae(e,s,i))<t&&(t=r),function(e,t,n,s,i){var r=Ee(e,t,n,s,i),a=Ge(r.year,0,r.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}.call(this,e,t,n,s,i))}I(0,["gg",2],0,function(){return this.weekYear()%100}),I(0,["GG",2],0,function(){return this.isoWeekYear()%100}),an("gggg","weekYear"),an("ggggg","weekYear"),an("GGGG","isoWeekYear"),an("GGGGG","isoWeekYear"),C("weekYear","gg"),C("isoWeekYear","GG"),F("weekYear",1),F("isoWeekYear",1),ue("G",se),ue("g",se),ue("GG",B,z),ue("gg",B,z),ue("GGGG",ee,q),ue("gggg",ee,q),ue("GGGGG",te,J),ue("ggggg",te,J),fe(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=D(e)}),fe(["gg","GG"],function(e,t,n,s){t[s]=c.parseTwoDigitYear(e)}),I("Q",0,"Qo","quarter"),C("quarter","Q"),F("quarter",7),ue("Q",Z),ce("Q",function(e,t){t[_e]=3*(D(e)-1)}),I("D",["DD",2],"Do","date"),C("date","D"),F("date",9),ue("D",B),ue("DD",B,z),ue("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),ce(["D","DD"],ye),ce("Do",function(e,t){t[ye]=D(e.match(B)[0])});var un=Te("Date",!0);I("DDD",["DDDD",3],"DDDo","dayOfYear"),C("dayOfYear","DDD"),F("dayOfYear",4),ue("DDD",K),ue("DDDD",$),ce(["DDD","DDDD"],function(e,t,n){n._dayOfYear=D(e)}),I("m",["mm",2],0,"minute"),C("minute","m"),F("minute",14),ue("m",B),ue("mm",B,z),ce(["m","mm"],ve);var ln=Te("Minutes",!1);I("s",["ss",2],0,"second"),C("second","s"),F("second",15),ue("s",B),ue("ss",B,z),ce(["s","ss"],pe);var hn,dn=Te("Seconds",!1);for(I("S",0,0,function(){return~~(this.millisecond()/100)}),I(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),I(0,["SSS",3],0,"millisecond"),I(0,["SSSS",4],0,function(){return 10*this.millisecond()}),I(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),I(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),I(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),I(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),I(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),C("millisecond","ms"),F("millisecond",16),ue("S",K,Z),ue("SS",K,z),ue("SSS",K,$),hn="SSSS";hn.length<=9;hn+="S")ue(hn,ne);function cn(e,t){t[we]=D(1e3*("0."+e))}for(hn="S";hn.length<=9;hn+="S")ce(hn,cn);var fn=Te("Milliseconds",!1);I("z",0,0,"zoneAbbr"),I("zz",0,0,"zoneName");var mn=M.prototype;function _n(e){return e}mn.add=Jt,mn.calendar=function(e,t){var n=e||bt(),s=Gt(n,this).startOf("day"),i=c.calendarFormat(this,s)||"sameElse",r=t&&(b(t[i])?t[i].call(this,n):t[i]);return this.format(r||this.localeData().calendar(i,this,bt(n)))},mn.clone=function(){return new M(this)},mn.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Gt(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=H(t)){case"year":r=Qt(this,s)/12;break;case"month":r=Qt(this,s);break;case"quarter":r=Qt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:S(r)},mn.endOf=function(e){var t;if(void 0===(e=H(e))||"millisecond"===e||!this.isValid())return this;var n=this._isUTC?rn:sn;switch(e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf(),t+=36e5-nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":t=this._d.valueOf(),t+=6e4-nn(t,6e4)-1;break;case"second":t=this._d.valueOf(),t+=1e3-nn(t,1e3)-1;break}return this._d.setTime(t),c.updateOffset(this,!0),this},mn.format=function(e){e||(e=this.isUtc()?c.defaultFormatUtc:c.defaultFormat);var t=A(this,e);return this.localeData().postformat(t)},mn.from=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||bt(e).isValid())?jt({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},mn.fromNow=function(e){return this.from(bt(),e)},mn.to=function(e,t){return this.isValid()&&(k(e)&&e.isValid()||bt(e).isValid())?jt({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},mn.toNow=function(e){return this.to(bt(),e)},mn.get=function(e){return b(this[e=H(e)])?this[e]():this},mn.invalidAt=function(){return g(this).overflow},mn.isAfter=function(e,t){var n=k(e)?e:bt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},mn.isBefore=function(e,t){var n=k(e)?e:bt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},mn.isBetween=function(e,t,n,s){var i=k(e)?e:bt(e),r=k(t)?t:bt(t);return!!(this.isValid()&&i.isValid()&&r.isValid())&&("("===(s=s||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===s[1]?this.isBefore(r,n):!this.isAfter(r,n))},mn.isSame=function(e,t){var n,s=k(e)?e:bt(e);return!(!this.isValid()||!s.isValid())&&("millisecond"===(t=H(t)||"millisecond")?this.valueOf()===s.valueOf():(n=s.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},mn.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},mn.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},mn.isValid=function(){return v(this)},mn.lang=Kt,mn.locale=Xt,mn.localeData=en,mn.max=Pt,mn.min=xt,mn.parsingFlags=function(){return _({},g(this))},mn.set=function(e,t){if("object"==typeof e)for(var n=function(e){var t=[];for(var n in e)t.push({unit:n,priority:U[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}(e=R(e)),s=0;s<n.length;s++)this[n[s].unit](e[n[s].unit]);else if(b(this[e=H(e)]))return this[e](t);return this},mn.startOf=function(e){var t;if(void 0===(e=H(e))||"millisecond"===e||!this.isValid())return this;var n=this._isUTC?rn:sn;switch(e){case"year":t=n(this.year(),0,1);break;case"quarter":t=n(this.year(),this.month()-this.month()%3,1);break;case"month":t=n(this.year(),this.month(),1);break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":t=n(this.year(),this.month(),this.date());break;case"hour":t=this._d.valueOf(),t-=nn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":t=this._d.valueOf(),t-=nn(t,6e4);break;case"second":t=this._d.valueOf(),t-=nn(t,1e3);break}return this._d.setTime(t),c.updateOffset(this,!0),this},mn.subtract=Bt,mn.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},mn.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},mn.toDate=function(){return new Date(this.valueOf())},mn.toISOString=function(e){if(!this.isValid())return null;var t=!0!==e,n=t?this.clone().utc():this;return n.year()<0||9999<n.year()?A(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):b(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",A(n,"Z")):A(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},mn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e="moment",t="";this.isLocal()||(e=0===this.utcOffset()?"moment.utc":"moment.parseZone",t="Z");var n="["+e+'("]',s=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=t+'[")]';return this.format(n+s+"-MM-DD[T]HH:mm:ss.SSS"+i)},mn.toJSON=function(){return this.isValid()?this.toISOString():null},mn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},mn.unix=function(){return Math.floor(this.valueOf()/1e3)},mn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},mn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},mn.year=Oe,mn.isLeapYear=function(){return De(this.year())},mn.weekYear=function(e){return on.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},mn.isoWeekYear=function(e){return on.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},mn.quarter=mn.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},mn.month=Ue,mn.daysInMonth=function(){return Pe(this.year(),this.month())},mn.week=mn.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},mn.isoWeek=mn.isoWeeks=function(e){var t=Ie(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},mn.weeksInYear=function(){var e=this.localeData()._week;return Ae(this.year(),e.dow,e.doy)},mn.isoWeeksInYear=function(){return Ae(this.year(),1,4)},mn.date=un,mn.day=mn.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t,n,s=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(t=e,n=this.localeData(),e="string"!=typeof t?t:isNaN(t)?"number"==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10),this.add(e-s,"d")):s},mn.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},mn.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null==e)return this.day()||7;var t,n,s=(t=e,n=this.localeData(),"string"==typeof t?n.weekdaysParse(t)%7||7:isNaN(t)?null:t);return this.day(this.day()%7?s:s-7)},mn.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},mn.hour=mn.hours=nt,mn.minute=mn.minutes=ln,mn.second=mn.seconds=dn,mn.millisecond=mn.milliseconds=fn,mn.utcOffset=function(e,t,n){var s,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Vt(this);if("string"==typeof e){if(null===(e=Nt(re,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(s=Vt(this)),this._offset=e,this._isUTC=!0,null!=s&&this.add(s,"m"),i!==e&&(!t||this._changeInProgress?qt(this,jt(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,c.updateOffset(this,!0),this._changeInProgress=null)),this},mn.utc=function(e){return this.utcOffset(0,e)},mn.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Vt(this),"m")),this},mn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=Nt(ie,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},mn.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?bt(e).utcOffset():0,(this.utcOffset()-e)%60==0)},mn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},mn.isUtc=Et,mn.isUTC=Et,mn.zoneAbbr=function(){return this._isUTC?"UTC":""},mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},mn.dates=n("dates accessor is deprecated. Use date instead.",un),mn.months=n("months accessor is deprecated. Use month instead",Ue),mn.years=n("years accessor is deprecated. Use year instead",Oe),mn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),mn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Ot(e))._a){var t=e._isUTC?y(e._a):bt(e._a);this._isDSTShifted=this.isValid()&&0<a(e._a,t.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted});var yn=P.prototype;function gn(e,t,n,s){var i=ht(),r=y().set(s,t);return i[n](r,e)}function vn(e,t,n){if(h(e)&&(t=e,e=void 0),e=e||"",null!=t)return gn(e,t,n,"month");var s,i=[];for(s=0;s<12;s++)i[s]=gn(e,s,n,"month");return i}function pn(e,t,n,s){t=("boolean"==typeof e?h(t)&&(n=t,t=void 0):(t=e,e=!1,h(n=t)&&(n=t,t=void 0)),t||"");var i,r=ht(),a=e?r._week.dow:0;if(null!=n)return gn(t,(n+a)%7,s,"day");var o=[];for(i=0;i<7;i++)o[i]=gn(t,(i+a)%7,s,"day");return o}yn.calendar=function(e,t,n){var s=this._calendar[e]||this._calendar.sameElse;return b(s)?s.call(t,n):s},yn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},yn.invalidDate=function(){return this._invalidDate},yn.ordinal=function(e){return this._ordinal.replace("%d",e)},yn.preparse=_n,yn.postformat=_n,yn.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return b(i)?i(e,t,n,s):i.replace(/%d/i,e)},yn.pastFuture=function(e,t){var n=this._relativeTime[0<e?"future":"past"];return b(n)?n(t):n.replace(/%s/i,t)},yn.set=function(e){var t,n;for(n in e)b(t=e[n])?this[n]=t:this["_"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},yn.months=function(e,t){return e?o(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||We).test(t)?"format":"standalone"][e.month()]:o(this._months)?this._months:this._months.standalone},yn.monthsShort=function(e,t){return e?o(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[We.test(t)?"format":"standalone"][e.month()]:o(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},yn.monthsParse=function(e,t,n){var s,i,r;if(this._monthsParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=y([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=y([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(r="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},yn.monthsRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(m(this,"_monthsRegex")||(this._monthsRegex=Le),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},yn.monthsShortRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(m(this,"_monthsShortRegex")||(this._monthsShortRegex=Fe),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},yn.week=function(e){return Ie(e,this._week.dow,this._week.doy).week},yn.firstDayOfYear=function(){return this._week.doy},yn.firstDayOfWeek=function(){return this._week.dow},yn.weekdays=function(e,t){var n=o(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"];return!0===e?je(n,this._week.dow):e?n[e.day()]:n},yn.weekdaysMin=function(e){return!0===e?je(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},yn.weekdaysShort=function(e){return!0===e?je(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},yn.weekdaysParse=function(e,t,n){var s,i,r;if(this._weekdaysParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=y([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=y([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[s]||(r="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},yn.weekdaysRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(m(this,"_weekdaysRegex")||(this._weekdaysRegex=qe),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},yn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(m(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Je),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},yn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Qe.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(m(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Be),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},yn.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},yn.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ut("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===D(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),c.lang=n("moment.lang is deprecated. Use moment.locale instead.",ut),c.langData=n("moment.langData is deprecated. Use moment.localeData instead.",ht);var wn=Math.abs;function Mn(e,t,n,s){var i=jt(t,n);return e._milliseconds+=s*i._milliseconds,e._days+=s*i._days,e._months+=s*i._months,e._bubble()}function kn(e){return e<0?Math.floor(e):Math.ceil(e)}function Sn(e){return 4800*e/146097}function Dn(e){return 146097*e/4800}function Yn(e){return function(){return this.as(e)}}var On=Yn("ms"),Tn=Yn("s"),bn=Yn("m"),xn=Yn("h"),Pn=Yn("d"),Wn=Yn("w"),Cn=Yn("M"),Hn=Yn("Q"),Rn=Yn("y");function Un(e){return function(){return this.isValid()?this._data[e]:NaN}}var Fn=Un("milliseconds"),Ln=Un("seconds"),Nn=Un("minutes"),Gn=Un("hours"),Vn=Un("days"),En=Un("months"),In=Un("years");var An=Math.round,jn={ss:44,s:45,m:45,h:22,d:26,M:11};var Zn=Math.abs;function zn(e){return(0<e)-(e<0)||+e}function $n(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=Zn(this._milliseconds)/1e3,s=Zn(this._days),i=Zn(this._months);t=S((e=S(n/60))/60),n%=60,e%=60;var r=S(i/12),a=i%=12,o=s,u=t,l=e,h=n?n.toFixed(3).replace(/\.?0+$/,""):"",d=this.asSeconds();if(!d)return"P0D";var c=d<0?"-":"",f=zn(this._months)!==zn(d)?"-":"",m=zn(this._days)!==zn(d)?"-":"",_=zn(this._milliseconds)!==zn(d)?"-":"";return c+"P"+(r?f+r+"Y":"")+(a?f+a+"M":"")+(o?m+o+"D":"")+(u||l||h?"T":"")+(u?_+u+"H":"")+(l?_+l+"M":"")+(h?_+h+"S":"")}var qn=Ht.prototype;return qn.isValid=function(){return this._isValid},qn.abs=function(){var e=this._data;return this._milliseconds=wn(this._milliseconds),this._days=wn(this._days),this._months=wn(this._months),e.milliseconds=wn(e.milliseconds),e.seconds=wn(e.seconds),e.minutes=wn(e.minutes),e.hours=wn(e.hours),e.months=wn(e.months),e.years=wn(e.years),this},qn.add=function(e,t){return Mn(this,e,t,1)},qn.subtract=function(e,t){return Mn(this,e,t,-1)},qn.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=H(e))||"quarter"===e||"year"===e)switch(t=this._days+s/864e5,n=this._months+Sn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(Dn(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},qn.asMilliseconds=On,qn.asSeconds=Tn,qn.asMinutes=bn,qn.asHours=xn,qn.asDays=Pn,qn.asWeeks=Wn,qn.asMonths=Cn,qn.asQuarters=Hn,qn.asYears=Rn,qn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*D(this._months/12):NaN},qn._bubble=function(){var e,t,n,s,i,r=this._milliseconds,a=this._days,o=this._months,u=this._data;return 0<=r&&0<=a&&0<=o||r<=0&&a<=0&&o<=0||(r+=864e5*kn(Dn(o)+a),o=a=0),u.milliseconds=r%1e3,e=S(r/1e3),u.seconds=e%60,t=S(e/60),u.minutes=t%60,n=S(t/60),u.hours=n%24,o+=i=S(Sn(a+=S(n/24))),a-=kn(Dn(i)),s=S(o/12),o%=12,u.days=a,u.months=o,u.years=s,this},qn.clone=function(){return jt(this)},qn.get=function(e){return e=H(e),this.isValid()?this[e+"s"]():NaN},qn.milliseconds=Fn,qn.seconds=Ln,qn.minutes=Nn,qn.hours=Gn,qn.days=Vn,qn.weeks=function(){return S(this.days()/7)},qn.months=En,qn.years=In,qn.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t,n,s,i,r,a,o,u,l,h,d,c=this.localeData(),f=(n=!e,s=c,i=jt(t=this).abs(),r=An(i.as("s")),a=An(i.as("m")),o=An(i.as("h")),u=An(i.as("d")),l=An(i.as("M")),h=An(i.as("y")),(d=r<=jn.ss&&["s",r]||r<jn.s&&["ss",r]||a<=1&&["m"]||a<jn.m&&["mm",a]||o<=1&&["h"]||o<jn.h&&["hh",o]||u<=1&&["d"]||u<jn.d&&["dd",u]||l<=1&&["M"]||l<jn.M&&["MM",l]||h<=1&&["y"]||["yy",h])[2]=n,d[3]=0<+t,d[4]=s,function(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}.apply(null,d));return e&&(f=c.pastFuture(+this,f)),c.postformat(f)},qn.toISOString=$n,qn.toString=$n,qn.toJSON=$n,qn.locale=Xt,qn.localeData=en,qn.toIsoString=n("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",$n),qn.lang=Kt,I("X",0,0,"unix"),I("x",0,0,"valueOf"),ue("x",se),ue("X",/[+-]?\d+(\.\d{1,3})?/),ce("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),ce("x",function(e,t,n){n._d=new Date(D(e))}),c.version="2.24.0",e=bt,c.fn=mn,c.min=function(){return Wt("isBefore",[].slice.call(arguments,0))},c.max=function(){return Wt("isAfter",[].slice.call(arguments,0))},c.now=function(){return Date.now?Date.now():+new Date},c.utc=y,c.unix=function(e){return bt(1e3*e)},c.months=function(e,t){return vn(e,t,"months")},c.isDate=d,c.locale=ut,c.invalid=p,c.duration=jt,c.isMoment=k,c.weekdays=function(e,t,n){return pn(e,t,n,"weekdays")},c.parseZone=function(){return bt.apply(null,arguments).parseZone()},c.localeData=ht,c.isDuration=Rt,c.monthsShort=function(e,t){return vn(e,t,"monthsShort")},c.weekdaysMin=function(e,t,n){return pn(e,t,n,"weekdaysMin")},c.defineLocale=lt,c.updateLocale=function(e,t){if(null!=t){var n,s,i=st;null!=(s=ot(e))&&(i=s._config),(n=new P(t=x(i,t))).parentLocale=it[e],it[e]=n,ut(e)}else null!=it[e]&&(null!=it[e].parentLocale?it[e]=it[e].parentLocale:null!=it[e]&&delete it[e]);return it[e]},c.locales=function(){return s(it)},c.weekdaysShort=function(e,t,n){return pn(e,t,n,"weekdaysShort")},c.normalizeUnits=H,c.relativeTimeRounding=function(e){return void 0===e?An:"function"==typeof e&&(An=e,!0)},c.relativeTimeThreshold=function(e,t){return void 0!==jn[e]&&(void 0===t?jn[e]:(jn[e]=t,"s"===e&&(jn.ss=t-1),!0))},c.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},c.prototype=mn,c.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},c}); \ No newline at end of file From e4598ebffd58f0318708a4432ea364b934ab2e04 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Tue, 3 Dec 2019 00:00:06 +0200 Subject: [PATCH 2279/2437] Minor code style fix --- app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php index fd6b59a6ad388..42ab3146f1321 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace Magento\Captcha\Test\Unit\Model\Config; + use PHPUnit\Framework\TestCase; use Magento\Captcha\Helper\Data as HelperData; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; From e2cecfbfe5ae9ed86212af1fdc8afb791320645a Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Tue, 3 Dec 2019 00:00:28 +0200 Subject: [PATCH 2280/2437] Minor code style fix --- .../Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php index 25681ac975cef..d3f40f5872a7d 100644 --- a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace Magento\Captcha\Test\Unit\Model\Config\Form; + use Magento\Captcha\Model\Config\Form\Frontend; use PHPUnit\Framework\TestCase; use Magento\Framework\App\Config\ScopeConfigInterface; From ff92deb93ecd30669ba17e0ad58f3fc64a68e2fe Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 3 Dec 2019 10:26:00 +0200 Subject: [PATCH 2281/2437] Fixing static tests --- .../Config/Model/Config/Structure/Element/Dependency/Field.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 8f4d82eed51c5..28e1ba505a2ce 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -6,6 +6,8 @@ namespace Magento\Config\Model\Config\Structure\Element\Dependency; /** + * Field + * * @api * @since 100.0.2 */ From f64cd491cd0a4574b572de10e609870a746e3e48 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 3 Dec 2019 10:27:55 +0200 Subject: [PATCH 2282/2437] Revert "Fixing static tests" This reverts commit ff92deb93ecd30669ba17e0ad58f3fc64a68e2fe. --- .../Config/Model/Config/Structure/Element/Dependency/Field.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 28e1ba505a2ce..8f4d82eed51c5 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -6,8 +6,6 @@ namespace Magento\Config\Model\Config\Structure\Element\Dependency; /** - * Field - * * @api * @since 100.0.2 */ From 37fc4e501eda2536fa244765fb44fc1204e16cf4 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Tue, 3 Dec 2019 10:28:56 +0200 Subject: [PATCH 2283/2437] Fixing static tests --- .../Config/Model/Config/Structure/Element/Dependency/Field.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 6ed9be0d10e11..6171bdfca5584 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -6,6 +6,8 @@ namespace Magento\Config\Model\Config\Structure\Element\Dependency; /** + * Field + * * @api * @since 100.0.2 */ From f42641c9280d0415895ad6571da582f1e8d66704 Mon Sep 17 00:00:00 2001 From: Ajith <ajithkumar.maragathavel@ziffity.com> Date: Tue, 3 Dec 2019 20:04:50 +0530 Subject: [PATCH 2284/2437] Unit test case added --- .../Controller/Adminhtml/Block/SaveTest.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php index 0d44f66048ba3..41e74416d3b1d 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php @@ -373,6 +373,58 @@ public function testSaveAndClose() $this->assertSame($this->resultRedirect, $this->saveController->execute()); } + public function testSaveActionWithMarginalSpace() + { + $postData = [ + 'title' => '"><img src=y onerror=prompt(document.domain)>;', + 'identifier' => ' unique_title_123', + 'stores' => ['0'], + 'is_active' => true, + 'content' => '"><script>alert("cookie: "+document.cookie)</script>', + 'back' => 'continue' + ]; + + $this->requestMock->expects($this->any())->method('getPostValue')->willReturn($postData); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') + ->willReturnMap( + [ + ['block_id', null, 1], + ['back', null, true], + ] + ); + + $this->blockFactory->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($this->blockMock); + + $this->blockRepository->expects($this->once()) + ->method('getById') + ->with($this->blockId) + ->willReturn($this->blockMock); + + $this->blockMock->expects($this->once())->method('setData'); + $this->blockRepository->expects($this->once())->method('save') + ->with($this->blockMock) + ->willThrowException(new \Exception('No marginal white space please.')); + + $this->messageManagerMock->expects($this->never()) + ->method('addSuccessMessage'); + $this->messageManagerMock->expects($this->once()) + ->method('addExceptionMessage'); + + $this->dataPersistorMock->expects($this->any()) + ->method('set') + ->with('cms_block', array_merge($postData, ['block_id' => null])); + + $this->resultRedirect->expects($this->atLeastOnce()) + ->method('setPath') + ->with('*/*/edit', ['block_id' => $this->blockId]) + ->willReturnSelf(); + + $this->assertSame($this->resultRedirect, $this->saveController->execute()); + } + public function testSaveActionThrowsException() { $postData = [ From f3f2b02c1b9bf2edf278260a334d023c97e9c170 Mon Sep 17 00:00:00 2001 From: Ajith <ajithkumar.maragathavel@ziffity.com> Date: Tue, 3 Dec 2019 21:45:33 +0530 Subject: [PATCH 2285/2437] Unit test case postData modified --- .../Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php index 41e74416d3b1d..c5bb26d04c734 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php @@ -376,11 +376,11 @@ public function testSaveAndClose() public function testSaveActionWithMarginalSpace() { $postData = [ - 'title' => '"><img src=y onerror=prompt(document.domain)>;', + 'title' => 'unique_title_123', 'identifier' => ' unique_title_123', 'stores' => ['0'], 'is_active' => true, - 'content' => '"><script>alert("cookie: "+document.cookie)</script>', + 'content' => '', 'back' => 'continue' ]; From a4d57b99f4163ca929433f20d56630e99f234a54 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 4 Dec 2019 22:50:34 +0700 Subject: [PATCH 2286/2437] Resolve issue 25896: Cannot create folder using only letters --- .../Magento/Cms/Model/Wysiwyg/Images/Storage.php | 2 +- .../Unit/Model/Wysiwyg/Images/StorageTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index e02d2b461a94e..21f3b232e783c 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -416,7 +416,7 @@ public function createDirectory($name, $path) { if (!preg_match(self::DIRECTORY_NAME_REGEXP, $name)) { throw new \Magento\Framework\Exception\LocalizedException( - __('Please rename the folder using only letters, numbers, underscores and dashes.') + __('Please rename the folder using only Latin letters, numbers, underscores and dashes.') ); } diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index 662aee671dd24..9a9c63195051c 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -7,6 +7,7 @@ use Magento\Cms\Model\Wysiwyg\Images\Storage\Collection as StorageCollection; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; /** * @SuppressWarnings(PHPMD.LongVariable) @@ -539,4 +540,18 @@ public function testUploadFile() $this->assertEquals($result, $this->imagesStorage->uploadFile($targetPath, $type)); } + + /** + * Test create directory with invalid name + */ + public function testCreateDirectoryWithInvalidName() + { + $name = 'папка'; + $path = '/tmp/path'; + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage( + (string)__('Please rename the folder using only Latin letters, numbers, underscores and dashes.') + ); + $this->imagesStorage->createDirectory($name, $path); + } } From 8a2ce159b3b0c980cc556f788aaee9ad0243edd8 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 4 Dec 2019 23:22:08 +0700 Subject: [PATCH 2287/2437] [Checkout] Cover DirectoryData by Unit Test --- .../Unit/CustomerData/DirectoryDataTest.php | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php new file mode 100644 index 0000000000000..3c0bae31c9c0d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\CustomerData; + +use Magento\Checkout\CustomerData\DirectoryData; +use Magento\Directory\Helper\Data as HelperData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Directory\Model\Country; +use PHPUnit\Framework\TestCase; + +class DirectoryDataTest extends TestCase +{ + /** + * @var DirectoryData + */ + private $model; + + /** + * @var HelperData|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryHelperMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->objectManager = new ObjectManagerHelper($this); + $this->directoryHelperMock = $this->createMock(HelperData::class); + + $this->model = $this->objectManager->getObject( + DirectoryData::class, + [ + 'directoryHelper' => $this->directoryHelperMock + ] + ); + } + + /** + * Test getSectionData() function + */ + public function testGetSectionData() + { + $regions = [ + 'US' => [ + 'TX' => [ + 'code' => 'TX', + 'name' => 'Texas' + ] + ] + ]; + + $testCountryInfo = $this->objectManager->getObject(Country::class); + $testCountryInfo->setData('country_id', 'US'); + $testCountryInfo->setData('iso2_code', 'US'); + $testCountryInfo->setData('iso3_code', 'USA'); + $testCountryInfo->setData('name_default', 'United States of America'); + $testCountryInfo->setData('name_en_US', 'United States of America'); + $countries = ['US' => $testCountryInfo]; + + $this->directoryHelperMock->expects($this->any()) + ->method('getRegionData') + ->willReturn($regions); + + $this->directoryHelperMock->expects($this->any()) + ->method('getCountryCollection') + ->willReturn($countries); + + /* Assert result */ + $this->assertEquals( + [ + 'US' => [ + 'name' => 'United States of America', + 'regions' => [ + 'TX' => [ + 'code' => 'TX', + 'name' => 'Texas' + ] + ] + ] + ], + $this->model->getSectionData() + ); + } +} From 9f14f895ac616d8a9c4aa5ed1cf2a657eff24814 Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@magento.com> Date: Wed, 4 Dec 2019 13:06:04 -0600 Subject: [PATCH 2288/2437] Merge branch '2.3-develop' of github.com:magento-engcom/magento2ee into coding-standard-update # Conflicts: # composer.lock --- .../Magento/BundleGraphQl/etc/schema.graphqls | 2 - composer.lock | 160 +++++++++--------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index 914e405ba2695..0eff0e086180e 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -86,5 +86,3 @@ enum ShipBundleItemsEnum @doc(description: "This enumeration defines whether bun TOGETHER SEPARATELY } - -type invalidCamelCaseType {} #test static check diff --git a/composer.lock b/composer.lock index d6cd5368fd72a..ba126b3eabefc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0e6d1fc607befd753c33181c6bcfd1b4", + "content-hash": "e3ad90186a7742707e4c12cda2580b35", "packages": [ { "name": "braintree/braintree_php", @@ -1112,16 +1112,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.1", + "version": "1.25.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", "shasum": "" }, "require": { @@ -1186,7 +1186,7 @@ "logging", "psr-3" ], - "time": "2019-09-06T13:49:17+00:00" + "time": "2019-11-13T10:00:05+00:00" }, { "name": "paragonie/random_compat", @@ -1391,39 +1391,36 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v2.7.3", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f" + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", - "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/6e2b2501e021e994fb64429e5a78118f83b5c200", + "reference": "6e2b2501e021e994fb64429e5a78118f83b5c200", "shasum": "" }, "require": { "ext-bcmath": "*", - "ext-mbstring": "*", - "php": ">=5.3.0" + "ext-sockets": "*", + "php": ">=5.6" }, "replace": { "videlalvaro/php-amqplib": "self.version" }, "require-dev": { - "phpdocumentor/phpdocumentor": "^2.9", - "phpunit/phpunit": "^4.8", - "scrutinizer/ocular": "^1.1", + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", "squizlabs/php_codesniffer": "^2.5" }, - "suggest": { - "ext-sockets": "Use AMQPSocketConnection" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.10-dev" } }, "autoload": { @@ -1449,6 +1446,11 @@ "name": "Raúl Araya", "email": "nubeiro@gmail.com", "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" } ], "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", @@ -1458,7 +1460,7 @@ "queue", "rabbitmq" ], - "time": "2018-04-30T03:54:54+00:00" + "time": "2019-10-10T13:23:40+00:00" }, { "name": "phpseclib/mcrypt_compat", @@ -2081,7 +2083,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2134,7 +2136,7 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -2262,7 +2264,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2312,7 +2314,7 @@ }, { "name": "symfony/finder", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -2478,7 +2480,7 @@ }, { "name": "symfony/process", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2724,23 +2726,23 @@ }, { "name": "wikimedia/less.php", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/wikimedia/less.php.git", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b" + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=7.2.9" }, "require-dev": { - "phpunit/phpunit": "~4.8.24" + "phpunit/phpunit": "7.5.14" }, "bin": [ "bin/lessc" @@ -2759,6 +2761,10 @@ "Apache-2.0" ], "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, { "name": "Matt Agar", "homepage": "https://github.com/agar" @@ -2766,10 +2772,6 @@ { "name": "Martin Jantošovič", "homepage": "https://github.com/Mordred" - }, - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" } ], "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", @@ -2781,7 +2783,7 @@ "php", "stylesheet" ], - "time": "2019-01-19T01:01:33+00:00" + "time": "2019-11-06T18:30:11+00:00" }, { "name": "zendframework/zend-captcha", @@ -6160,16 +6162,16 @@ }, { "name": "doctrine/cache", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a" + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c15dcd24b756f9e52ea7c3ae8227354f3628f11a", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a", + "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", "shasum": "" }, "require": { @@ -6239,7 +6241,7 @@ "riak", "xcache" ], - "time": "2019-11-11T10:31:52+00:00" + "time": "2019-11-15T14:31:57+00:00" }, { "name": "doctrine/inflector", @@ -6366,28 +6368,30 @@ }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -6400,14 +6404,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -6422,7 +6426,7 @@ "parser", "php" ], - "time": "2019-06-08T11:03:04+00:00" + "time": "2019-10-30T14:39:59+00:00" }, { "name": "facebook/webdriver", @@ -6616,16 +6620,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", "shasum": "" }, "require": { @@ -6634,13 +6638,11 @@ "require-dev": { "ext-intl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" + "squizlabs/php_codesniffer": "^2.9.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -6662,7 +6664,7 @@ "faker", "fixtures" ], - "time": "2018-07-12T10:23:15+00:00" + "time": "2019-11-14T13:13:06+00:00" }, { "name": "grasmash/expander", @@ -7677,16 +7679,16 @@ }, { "name": "phpcompatibility/php-compatibility", - "version": "9.3.3", + "version": "9.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696" + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1af08ca3861048a8bfb39d0405d0ac3e50ba2696", - "reference": "1af08ca3861048a8bfb39d0405d0ac3e50ba2696", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", "shasum": "" }, "require": { @@ -7731,7 +7733,7 @@ "phpcs", "standards" ], - "time": "2019-11-11T03:25:23+00:00" + "time": "2019-11-15T04:12:02+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -9252,7 +9254,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -9311,7 +9313,7 @@ }, { "name": "symfony/config", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -9375,7 +9377,7 @@ }, { "name": "symfony/dependency-injection", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", @@ -9448,7 +9450,7 @@ }, { "name": "symfony/dom-crawler", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -9509,16 +9511,16 @@ }, { "name": "symfony/http-foundation", - "version": "v2.8.50", + "version": "v2.8.52", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a" + "reference": "3929d9fe8148d17819ad0178c748b8d339420709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/746f8d3638bf46ee8b202e62f2b214c3d61fb06a", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3929d9fe8148d17819ad0178c748b8d339420709", + "reference": "3929d9fe8148d17819ad0178c748b8d339420709", "shasum": "" }, "require": { @@ -9560,11 +9562,11 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-16T10:00:53+00:00" + "time": "2019-11-12T12:34:41+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -9904,7 +9906,7 @@ }, { "name": "symfony/stopwatch", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -9954,7 +9956,7 @@ }, { "name": "symfony/yaml", - "version": "v4.3.7", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", From 706826899aa6c9452938d8d241b51106caa51633 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Thu, 5 Dec 2019 13:49:33 +0200 Subject: [PATCH 2289/2437] magento/magento2#25911 Fix notice on incorrect price param --- .../Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php | 2 +- .../Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php index d1aee8c4c5ba6..35c5a1cb29c84 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php @@ -310,7 +310,7 @@ public function validateFilter($filter) return false; } foreach ($filter as $v) { - if ($v !== '' && $v !== '0' && (double)$v <= 0 || is_infinite((double)$v)) { + if ($v !== '' && $v !== '0' && (!is_numeric($v) || (double)$v <= 0 || is_infinite((double)$v))) { return false; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php index 8ca23df31cdee..c59aa1988be55 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php @@ -178,6 +178,7 @@ public function validateFilterDataProvider() ['filter' => '0', 'result' => false], ['filter' => 0, 'result' => false], ['filter' => '100500INF', 'result' => false], + ['filter' => '-10\'[0]', 'result' => false], ]; } From 9cd0d395e3fece75fa5cb849feea13758832b133 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 5 Dec 2019 14:43:38 +0200 Subject: [PATCH 2290/2437] Covering the Backend Decoding Helper by UnitTest --- .../Backend/Test/Unit/Helper/JsTest.php | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 app/code/Magento/Backend/Test/Unit/Helper/JsTest.php diff --git a/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php new file mode 100644 index 0000000000000..d2afdbbaf49a6 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Helper; + +use Magento\Backend\Helper\Js; +use PHPUnit\Framework\TestCase; + +/** + * Class JsTest + */ +class JsTest extends TestCase +{ + /** + * @var Js + */ + private $helper; + + /** + * Set Up + */ + protected function setUp() + { + $this->helper = new Js(); + } + + /** + * Test decoding the serialized input + * + * @dataProvider getEncodedDataProvider + * + * @param string $encoded + * @param array $expected + */ + public function testDecodeGridSerializedInput(string $encoded, array $expected) + { + $this->assertEquals($expected, $this->helper->decodeGridSerializedInput($encoded)); + } + + /** + * Get serialized grid input + * + * @return array + */ + public function getEncodedDataProvider(): array + { + return [ + 'Decoding empty serialized string' => [ + '', + [] + ], + 'Decoding a simplified serialized string' => [ + '1&2&3&4', + [1, 2, 3, 4] + ], + 'Decoding encoded serialized string' => [ + '2=dGVzdC1zdHJpbmc=', + [ + 2 => [ + 'test-string' => '' + ] + ] + ], + 'Decoding multiple encoded serialized strings' => [ + '2=dGVzdC1zdHJpbmc=&3=bmV3LXN0cmluZw==', + [ + 2 => [ + 'test-string' => '' + ], + 3 => [ + 'new-string' => '' + ] + ] + ] + ]; + } +} From 7c53b1efae82ceb0d925e274ccf4faaf01adf8b2 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 5 Dec 2019 16:20:13 +0200 Subject: [PATCH 2291/2437] Adding class description --- app/code/Magento/Backend/Test/Unit/Helper/JsTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php index d2afdbbaf49a6..ff10158a11943 100644 --- a/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php +++ b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php @@ -12,6 +12,8 @@ /** * Class JsTest + * + * Testing decoding serialized grid data */ class JsTest extends TestCase { From 67c55ec5b8bd00ad78355df34362e1a754980bf7 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Thu, 5 Dec 2019 17:39:16 +0200 Subject: [PATCH 2292/2437] Adding admin class for password input type. Removing duplcated classes. --- .../Magento/Backend/Block/System/Account/Edit/Form.php | 6 +++--- .../Block/Adminhtml/Integration/Edit/Tab/Info.php | 2 +- app/code/Magento/User/Block/Role/Tab/Info.php | 2 +- app/code/Magento/User/Block/User/Edit/Tab/Main.php | 2 +- .../Magento/Framework/Data/Form/Element/Password.php | 7 ++++++- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php index 7c5246143b2c6..49ec305ddd761 100644 --- a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php +++ b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php @@ -114,7 +114,7 @@ protected function _prepareForm() 'name' => 'password', 'label' => __('New Password'), 'title' => __('New Password'), - 'class' => 'validate-admin-password admin__control-text' + 'class' => 'validate-admin-password' ] ); @@ -124,7 +124,7 @@ protected function _prepareForm() [ 'name' => 'password_confirmation', 'label' => __('Password Confirmation'), - 'class' => 'validate-cpassword admin__control-text' + 'class' => 'validate-cpassword' ] ); @@ -152,7 +152,7 @@ protected function _prepareForm() 'label' => __('Your Password'), 'id' => self::IDENTITY_VERIFICATION_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'validate-current-password required-entry admin__control-text', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php index 4042c2ebde87d..89cad471933e6 100644 --- a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php +++ b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php @@ -179,7 +179,7 @@ protected function _addGeneralFieldset($form, $integrationData) 'label' => __('Your Password'), 'id' => self::DATA_CONSUMER_PASSWORD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/User/Block/Role/Tab/Info.php b/app/code/Magento/User/Block/Role/Tab/Info.php index 8a656efa97443..f71b7eebd3c32 100644 --- a/app/code/Magento/User/Block/Role/Tab/Info.php +++ b/app/code/Magento/User/Block/Role/Tab/Info.php @@ -99,7 +99,7 @@ protected function _initForm() 'label' => __('Your Password'), 'id' => self::IDENTITY_VERIFICATION_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index 27e00483733d0..3182393db8eaf 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -184,7 +184,7 @@ protected function _prepareForm() 'label' => __('Your Password'), 'id' => self::CURRENT_USER_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Password.php b/lib/internal/Magento/Framework/Data/Form/Element/Password.php index 8d330e1a810ab..c71048dabd1df 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Password.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Password.php @@ -13,6 +13,11 @@ use Magento\Framework\Escaper; +/** + * Class Password + * + * Password input type + */ class Password extends AbstractElement { /** @@ -37,7 +42,7 @@ public function __construct( */ public function getHtml() { - $this->addClass('input-text'); + $this->addClass('input-text admin__control-text'); return parent::getHtml(); } } From 2cdd48f259729c6eafba0612772c5a8aec32e2ef Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Thu, 5 Dec 2019 13:26:33 -0600 Subject: [PATCH 2293/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- .../AdminCatalogPriceRuleStagingSection.xml | 15 --------------- .../Test/AdminReorderWithCatalogPriceTest.xml | 3 --- 2 files changed, 18 deletions(-) delete mode 100644 app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml deleted file mode 100644 index 7a92829e2371e..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminCatalogPriceRuleStagingSection"> - <element name="status" type="select" selector=".modal-component [data-index='is_active'] select"/> - <element name="isActive" type="select" selector=".modals-wrapper input[name='is_active']+label"/> - </section> -</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml index 587b23e857c0c..bddc114f5dd5e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml @@ -18,9 +18,6 @@ <useCaseId value="MAGETWO-99691"/> <group value="sales"/> <group value="catalogRule"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <!--Create the catalog price rule --> From 839fed39f56cc8582f3195ef754718250411451c Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Thu, 5 Dec 2019 15:04:56 -0600 Subject: [PATCH 2294/2437] MQE-1907: stabilize mtf to mftf migration pr #744 --- .../AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml | 2 +- .../Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml index ca292c384979f..5a05dab7aa707 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml @@ -30,7 +30,7 @@ <!--Create the Category Url Rewrite--> <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewrite"> <argument name="category" value="$$category.name$$"/> - <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="customUrlRewriteValue" value="For Category"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="-"/> <argument name="redirectTypeValue" value="No"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml index 3c0dc8dc2be10..e94e10767c632 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml @@ -30,7 +30,7 @@ <!--Create the Category Url Rewrite--> <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewriteSecondTime"> <argument name="category" value="$$category.name$$"/> - <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="customUrlRewriteValue" value="For Category"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="newrequestpath.html"/> <argument name="redirectTypeValue" value="No"/> From c070d8e4248d1dca30773c20055b869a1cac3df4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Thu, 5 Dec 2019 16:43:42 -0600 Subject: [PATCH 2295/2437] MC-22998: Asynchronous image resizing --- .../MediaStorage/Console/Command/ImagesResizeCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index ea8cdef8598f4..4ed84829c2ad0 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -22,8 +22,6 @@ /** * Resizes product images according to theme view definitions. - * - * @package Magento\MediaStorage\Console\Command */ class ImagesResizeCommand extends Command { From 37a9929ad036fd8e8309111d87f8ce7f1e0e35cc Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 08:19:27 +0700 Subject: [PATCH 2296/2437] Resolve A "500 (Internal Server Error)" appears in Developer Console if Delete the image that is added to Page Content issue25893 --- .../Adminhtml/Wysiwyg/Directive.php | 17 ++++++++--- .../Adminhtml/Wysiwyg/DirectiveTest.php | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php index b21ea9fd7ef7b..db53c6a415ddd 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php @@ -4,6 +4,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Cms\Controller\Adminhtml\Wysiwyg; use Magento\Backend\App\Action; @@ -13,6 +16,8 @@ /** * Process template text for wysiwyg editor. + * + * Class Directive */ class Directive extends Action implements HttpGetActionInterface { @@ -73,10 +78,14 @@ public function execute() /** @var Config $config */ $config = $this->_objectManager->get(Config::class); $imagePath = $config->getSkinImagePlaceholderPath(); - $image->open($imagePath); - $resultRaw->setHeader('Content-Type', $image->getMimeType()); - $resultRaw->setContents($image->getImage()); - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + try { + $image->open($imagePath); + $resultRaw->setHeader('Content-Type', $image->getMimeType()); + $resultRaw->setContents($image->getImage()); + $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + } catch (\Exception $e) { + $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + } } return $resultRaw; } diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php index 16b218ebf6493..85c48e3fa3b7d 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php @@ -274,4 +274,33 @@ protected function prepareExecuteTest() ->method('create') ->willReturn($this->imageAdapterMock); } + + /** + * Test Execute With Deleted Image + * + * @covers \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive::execute + */ + public function testExecuteWithDeletedImage() + { + $exception = new \Exception('epic fail'); + $placeholderPath = 'pub/static/adminhtml/Magento/backend/en_US/Magento_Cms/images/wysiwyg_skin_image.png'; + $this->prepareExecuteTest(); + + $this->imageAdapterMock->expects($this->at(0)) + ->method('open') + ->with(self::IMAGE_PATH) + ->willThrowException($exception); + $this->wysiwygConfigMock->expects($this->once()) + ->method('getSkinImagePlaceholderPath') + ->willReturn($placeholderPath); + $this->imageAdapterMock->expects($this->at(1)) + ->method('open') + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->with($exception); + + $this->wysiwygDirective->execute(); + } } From 66bf8d18bf6d5f88ffd5dc674106c52a8fd066a2 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 08:21:24 +0700 Subject: [PATCH 2297/2437] Unit Test for Directive --- .../Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php index 85c48e3fa3b7d..cede3a80cb98b 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php @@ -286,15 +286,18 @@ public function testExecuteWithDeletedImage() $placeholderPath = 'pub/static/adminhtml/Magento/backend/en_US/Magento_Cms/images/wysiwyg_skin_image.png'; $this->prepareExecuteTest(); - $this->imageAdapterMock->expects($this->at(0)) + $this->imageAdapterMock->expects($this->any()) ->method('open') ->with(self::IMAGE_PATH) ->willThrowException($exception); + $this->wysiwygConfigMock->expects($this->once()) ->method('getSkinImagePlaceholderPath') ->willReturn($placeholderPath); - $this->imageAdapterMock->expects($this->at(1)) + + $this->imageAdapterMock->expects($this->any()) ->method('open') + ->with($placeholderPath) ->willThrowException($exception); $this->loggerMock->expects($this->once()) From fa2ed5347963585c3f8c8f639511fdf25a28df24 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 13:53:37 +0700 Subject: [PATCH 2298/2437] Resolve Duplicate Records when sorting column in Content->Themes Grid issue25925 --- .../Theme/view/adminhtml/ui_component/design_theme_listing.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml index 14aea72d87357..d2e5fa7ae1ca9 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml @@ -20,6 +20,9 @@ <dataSource name="design_theme_listing_data_source" component="Magento_Ui/js/grid/provider"> <settings> <updateUrl path="mui/index/render"/> + <storageConfig> + <param name="indexField" xsi:type="string">theme_id</param> + </storageConfig> </settings> <aclResource>Magento_Theme::theme</aclResource> <dataProvider class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider" name="design_theme_listing_data_source"> From febe51cde0b75356b5041b3d6952fe2b8d047d48 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 15:49:11 +0700 Subject: [PATCH 2299/2437] [Variable] Cover Variable Data Model by Unit Test --- .../Test/Unit/Model/Variable/DataTest.php | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php diff --git a/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php new file mode 100644 index 0000000000000..36e6dbcceb507 --- /dev/null +++ b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php @@ -0,0 +1,128 @@ +<?php +/*** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Variable\Test\Unit\Model\Variable; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; +use Magento\Variable\Model\Variable\Data as VariableDataModel; +use Magento\Variable\Model\ResourceModel\Variable\CollectionFactory as VariableCollectionFactory; +use Magento\Variable\Model\ResourceModel\Variable\Collection as VariableCollection; +use Magento\Variable\Model\Source\Variables as StoreVariables; + +class DataTest extends TestCase +{ + /** + * @var VariableDataModel + */ + private $model; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var StoreVariables|PHPUnit_Framework_MockObject_MockObject + */ + private $storesVariablesMock; + + /** + * @var VariableCollectionFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $variableCollectionFactoryMock; + + /** + * Set up before tests + */ + protected function setUp() + { + $this->storesVariablesMock = $this->createMock(StoreVariables::class); + $this->variableCollectionFactoryMock = $this->getMockBuilder( + VariableCollectionFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + VariableDataModel::class, + [ + 'collectionFactory' => $this->variableCollectionFactoryMock, + 'storesVariables' => $this->storesVariablesMock + ] + ); + } + + /** + * @covers VariableDataModel::getDefaultVariables + */ + public function testGetDefaultVariables() + { + $storesVariablesData = [ + [ + 'value' => 'test 1', + 'label' => 'Test Label 1', + 'group_label' => 'Group Label 1' + ], + [ + 'value' => 'test 2', + 'label' => 'Test Label 2', + 'group_label' => 'Group Label 2' + ] + ]; + $expectedResult = [ + [ + 'code' => 'test 1', + 'variable_name' => 'Group Label 1 / Test Label 1', + 'variable_type' => StoreVariables::DEFAULT_VARIABLE_TYPE + ], + [ + 'code' => 'test 2', + 'variable_name' => 'Group Label 2 / Test Label 2', + 'variable_type' => StoreVariables::DEFAULT_VARIABLE_TYPE + ] + ]; + $this->storesVariablesMock->expects($this->any())->method('getData')->willReturn($storesVariablesData); + + $this->assertEquals($expectedResult, $this->model->getDefaultVariables()); + } + + /** + * @covers VariableDataModel::getCustomVariables + */ + public function testGetCustomVariables() + { + $customVariables = [ + [ + 'code' => 'test 1', + 'name' => 'Test 1' + ], + [ + 'code' => 'test 2', + 'name' => 'Test 2' + ] + ]; + $expectedResult = [ + [ + 'code' => 'test 1', + 'variable_name' => 'Custom Variable / Test 1', + 'variable_type' => StoreVariables::CUSTOM_VARIABLE_TYPE + ], + [ + 'code' => 'test 2', + 'variable_name' => 'Custom Variable / Test 2', + 'variable_type' => StoreVariables::CUSTOM_VARIABLE_TYPE + ] + ]; + $variableCollectionMock = $this->createMock(VariableCollection::class); + $this->variableCollectionFactoryMock->expects($this->once())->method('create') + ->willReturn($variableCollectionMock); + $variableCollectionMock->expects($this->any())->method('getData')->willReturn($customVariables); + + $this->assertEquals($expectedResult, $this->model->getCustomVariables()); + } +} From 25e1c1e917fc0001d483d5a63168c71b43efe3b4 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 16:28:18 +0700 Subject: [PATCH 2300/2437] Fix static test --- .../Magento/Variable/Test/Unit/Model/Variable/DataTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php index 36e6dbcceb507..50191de66efbf 100644 --- a/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php +++ b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php @@ -58,7 +58,7 @@ protected function setUp() } /** - * @covers VariableDataModel::getDefaultVariables + * Test getDefaultVariables() function */ public function testGetDefaultVariables() { @@ -92,7 +92,7 @@ public function testGetDefaultVariables() } /** - * @covers VariableDataModel::getCustomVariables + * Test getCustomVariables() function */ public function testGetCustomVariables() { From b92aef02958c7820d2d4e9456eb22f3cebba107d Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 16:56:06 +0700 Subject: [PATCH 2301/2437] Resolve Refresh Statistics: Updated At = Null should be display as "Never" instead of "undefined". issue25931 --- .../Reports/Controller/Adminhtml/Report/AbstractReport.php | 2 +- app/code/Magento/Reports/i18n/en_US.csv | 2 ++ .../view/adminhtml/layout/reports_report_statistics_index.xml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php index 2fbff13a5b644..c6d79459a2b52 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php @@ -140,7 +140,7 @@ protected function _showLastExecutionTime($flagCode, $refreshCode) $flag = $this->_objectManager->create(\Magento\Reports\Model\Flag::class) ->setReportFlagCode($flagCode) ->loadSelf(); - $updatedAt = 'undefined'; + $updatedAt = __('Never'); if ($flag->hasData()) { $updatedAt = $this->timezone->formatDate( $flag->getLastUpdate(), diff --git a/app/code/Magento/Reports/i18n/en_US.csv b/app/code/Magento/Reports/i18n/en_US.csv index 3225f2fc41409..169d3cc2b74b4 100644 --- a/app/code/Magento/Reports/i18n/en_US.csv +++ b/app/code/Magento/Reports/i18n/en_US.csv @@ -224,3 +224,5 @@ Action,Action Report,Report Description,Description undefined,undefined +Never,Never + diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml index 649dc7ceeb065..5b841e3523649 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml @@ -71,7 +71,7 @@ <argument name="sortable" xsi:type="string">0</argument> <argument name="id" xsi:type="string">updated_at</argument> <argument name="index" xsi:type="string">updated_at</argument> - <argument name="default" xsi:type="string" translate="true">undefined</argument> + <argument name="default" xsi:type="string" translate="true">Never</argument> <argument name="column_css_class" xsi:type="string">col-period</argument> <argument name="header_css_class" xsi:type="string">col-period</argument> </arguments> From 414105e20221688f94e45b0563f1b38f9e9ea258 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <svizev.igor@gmail.com> Date: Fri, 6 Dec 2019 14:29:33 +0200 Subject: [PATCH 2302/2437] magento/magento2#25911 Fix notice on incorrect price param Fix static tests --- .../Model/Layer/Filter/DataProvider/Price.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php index 35c5a1cb29c84..229844fbe84b5 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php @@ -10,6 +10,9 @@ use Magento\Framework\Registry; use Magento\Store\Model\ScopeInterface; +/** + * Data provider for price filter in layered navigation + */ class Price { /** @@ -103,6 +106,8 @@ public function __construct( } /** + * Getter for interval + * * @return array */ public function getInterval() @@ -111,6 +116,8 @@ public function getInterval() } /** + * Setter for interval + * * @param array $interval * @return void */ @@ -120,6 +127,10 @@ public function setInterval($interval) } /** + * Retrieves price layered navigation modes + * + * @see RANGE_CALCULATION_AUTO + * * @return mixed */ public function getRangeCalculationValue() @@ -131,6 +142,8 @@ public function getRangeCalculationValue() } /** + * Retrieves range step + * * @return mixed */ public function getRangeStepValue() @@ -142,6 +155,8 @@ public function getRangeStepValue() } /** + * Retrieves one price interval + * * @return mixed */ public function getOnePriceIntervalValue() @@ -179,6 +194,8 @@ public function getRangeMaxIntervalsValue() } /** + * Retrieves Catalog Layer object + * * @return Layer */ public function getLayer() @@ -276,6 +293,8 @@ public function getMaxPrice() } /** + * Retrieve list of prior filters + * * @param string $filterParams * @return array */ @@ -339,6 +358,8 @@ public function getResetValue() } /** + * Getter for prior intervals + * * @return array */ public function getPriorIntervals() @@ -347,6 +368,8 @@ public function getPriorIntervals() } /** + * Setter for prior intervals + * * @param array $priorInterval * @return void */ @@ -356,6 +379,8 @@ public function setPriorIntervals($priorInterval) } /** + * Get Resource model for price filter + * * @return \Magento\Catalog\Model\ResourceModel\Layer\Filter\Price */ public function getResource() @@ -364,6 +389,8 @@ public function getResource() } /** + * Retrieves additional request data + * * @return string */ public function getAdditionalRequestData() From f9a510a10ed9cf8a3c8a1f0f4ffd374b59333f82 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 6 Dec 2019 15:04:15 +0200 Subject: [PATCH 2303/2437] MC-23900: Authorize Net issue on the admin order creation page --- .../Magento/Sales/view/adminhtml/web/order/create/scripts.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 4e07414510748..8803e2ea48f02 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -482,11 +482,6 @@ define([ }, switchPaymentMethod: function(method){ - jQuery('#edit_form') - .off('submitOrder') - .on('submitOrder', function(){ - jQuery(this).trigger('realOrder'); - }); jQuery('#edit_form').trigger('changePaymentMethod', [method]); this.setPaymentMethod(method); var data = {}; From f33199f77fe16beb22478dd9773035185b576a0d Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 22:03:30 +0700 Subject: [PATCH 2304/2437] Fix static test --- .../Reports/Controller/Adminhtml/Report/AbstractReport.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php index c6d79459a2b52..d5d8d32744e49 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php @@ -18,6 +18,7 @@ /** * Reports api controller * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 * @SuppressWarnings(PHPMD.AllPurposeAction) From a6badea946e710b4e9c306f86ec4707d1cfaa528 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Fri, 6 Dec 2019 23:35:04 +0700 Subject: [PATCH 2305/2437] [ProductAlert] Cover Helper Data by Unit Test --- .../Test/Unit/Helper/DataTest.php | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..1818da2289b58 --- /dev/null +++ b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ProductAlert\Test\Unit\Helper; + +use PHPUnit\Framework\TestCase; +use Magento\ProductAlert\Helper\Data as HelperData; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\UrlInterface; +use Magento\Framework\Url\EncoderInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\ProductAlert\Model\Observer; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\ProductAlert\Block\Email\Price; +use Magento\Framework\Exception\LocalizedException; + +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + + /** + * @var EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $encoderMock; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutMock; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->encoderMock = $this->createMock(EncoderInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->layoutMock = $this->createMock(LayoutInterface::class); + $this->contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($this->urlBuilderMock); + $this->contextMock->expects($this->once())->method('getUrlEncoder')->willReturn($this->encoderMock); + $this->contextMock->expects($this->once())->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $productMock = $this->createMock(Product::class); + $productMock->expects($this->any())->method('getId')->willReturn(1); + + $this->helper = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock, + 'layout' => $this->layoutMock + ] + ); + $this->helper->setProduct($productMock); + } + + /** + * Test getSaveUrl() function + */ + public function testGetSaveUrl() + { + $currentUrl = 'http://www.example.com/'; + $type = 'stock'; + $uenc = strtr(base64_encode($currentUrl), '+/=', '-_,'); + $expected = 'http://www.example.com/roductalert/add/stock/product_id/1/uenc/' . $uenc; + + $this->urlBuilderMock->expects($this->any())->method('getCurrentUrl')->willReturn($currentUrl); + $this->encoderMock->expects($this->any())->method('encode') + ->with($currentUrl) + ->willReturn($uenc); + $this->urlBuilderMock->expects($this->any())->method('getUrl') + ->with( + 'productalert/add/' . $type, + [ + 'product_id' => 1, + 'uenc' => $uenc + ] + ) + ->willReturn($expected); + + $this->assertEquals($expected, $this->helper->getSaveUrl($type)); + } + + /** + * Test createBlock() with no exception + */ + public function testCreateBlockWithNoException() + { + $priceBlockMock = $this->createMock(Price::class); + $this->layoutMock->expects($this->once())->method('createBlock')->willReturn($priceBlockMock); + + $this->assertEquals($priceBlockMock, $this->helper->createBlock(Price::class)); + } + + /** + * Test createBlock() with exception + */ + public function testCreateBlockWithException() + { + $invalidBlock = $this->createMock(Product::class); + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage((string)__('Invalid block type: %1')); + + $this->helper->createBlock($invalidBlock); + } + + /** + * Test isStockAlertAllowed() function with Yes settings + */ + public function testIsStockAlertAllowedWithYesSettings() + { + $this->scopeConfigMock->expects($this->any())->method('isSetFlag') + ->with(Observer::XML_PATH_STOCK_ALLOW, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $this->assertEquals('1', $this->helper->isStockAlertAllowed()); + } + + /** + * Test isPriceAlertAllowed() function with Yes settings + */ + public function testIsPriceAlertAllowedWithYesSetting() + { + $this->scopeConfigMock->expects($this->any())->method('isSetFlag') + ->with(Observer::XML_PATH_PRICE_ALLOW, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $this->assertEquals('1', $this->helper->isPriceAlertAllowed()); + } +} From dc8821dc2e297c1b73064ea49c1c4e7253b7e84f Mon Sep 17 00:00:00 2001 From: sdovbenko <sdovbenko@magecom.us> Date: Fri, 6 Dec 2019 19:44:52 +0200 Subject: [PATCH 2306/2437] Add OperationStatusPool and OperationStatusValidator --- .../Model/Operation.php | 21 +++++++++ .../Model/OperationStatusPool.php | 37 +++++++++++++++ .../Model/OperationStatusValidator.php | 47 +++++++++++++++++++ .../Magento/AsynchronousOperations/etc/di.xml | 11 +++++ 4 files changed, 116 insertions(+) create mode 100644 app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php create mode 100644 app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation.php b/app/code/Magento/AsynchronousOperations/Model/Operation.php index dbe6ecc1b6b1f..9e0039d2ee151 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation.php @@ -6,6 +6,7 @@ namespace Magento\AsynchronousOperations\Model; use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Model\OperationStatusValidator; use Magento\Framework\DataObject; /** @@ -13,6 +14,25 @@ */ class Operation extends DataObject implements OperationInterface { + /** + * @var OperationStatusValidator + */ + protected $operationStatusValidator; + + /** + * Operation constructor. + * + * @param array $data + * @param OperationStatusValidator $operationStatusValidator + */ + public function __construct( + array $data = [], + OperationStatusValidator $operationStatusValidator + ) { + $this->operationStatusValidator = $operationStatusValidator; + parent::__construct($data); + } + /** * @inheritDoc */ @@ -106,6 +126,7 @@ public function getStatus() */ public function setStatus($status) { + $this->operationStatusValidator->validate($status); return $this->setData(self::STATUS, $status); } diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php new file mode 100644 index 0000000000000..73eeedbdb7a20 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AsynchronousOperations\Model; + +/** + * Class OperationStatusPool + * + * Pool of statuses that require validate + */ +class OperationStatusPool +{ + /** + * @var array + */ + protected $statuses; + + /** + * @param array $statuses + */ + public function __construct(array $statuses = []) + { + $this->statuses = $statuses; + } + + /** + * Retrieve statuses that require validate + * + * @return array + */ + public function getStatuses() + { + return $this->statuses; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php new file mode 100644 index 0000000000000..3da94f1c4d7fb --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AsynchronousOperations\Model; + +use Magento\AsynchronousOperations\Model\OperationStatusPool; +use Magento\Framework\Exception\NoSuchEntityException; +use Doctrine\Instantiator\Exception\InvalidArgumentException; + +/** + * Class OperationStatusValidator to validate operation status + */ +class OperationStatusValidator +{ + /** + * @var OperationStatusPool + */ + protected $operationStatusPool; + + /** + * OperationStatusValidator constructor. + * + * @param OperationStatusPool $operationStatusPool + */ + public function __construct(OperationStatusPool $operationStatusPool) + { + $this->operationStatusPool = $operationStatusPool; + } + + /** + * Validate method + * + * @param $status + */ + public function validate($status) + { + $statuses = $this->operationStatusPool->getStatuses(); + + if (!in_array($status, $statuses)) { + throw new \InvalidArgumentException('Invalid Operation Status.'); + } + + return; + } +} diff --git a/app/code/Magento/AsynchronousOperations/etc/di.xml b/app/code/Magento/AsynchronousOperations/etc/di.xml index 94a4c56c19cea..171a01cedf289 100644 --- a/app/code/Magento/AsynchronousOperations/etc/di.xml +++ b/app/code/Magento/AsynchronousOperations/etc/di.xml @@ -80,6 +80,17 @@ </argument> </arguments> </type> + <type name="Magento\AsynchronousOperations\Model\OperationStatusPool"> + <arguments> + <argument name="statuses" xsi:type="array"> + <item name="complete" xsi:type="string">1</item> + <item name="retriablyFailed" xsi:type="string">2</item> + <item name="notRetriablyFailed" xsi:type="string">3</item> + <item name="open" xsi:type="string">4</item> + <item name="rejected" xsi:type="string">5</item> + </argument> + </arguments> + </type> <virtualType name="Magento\AsynchronousOperations\Ui\Component\DataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider"/> From f305a84472fc0222147844ffa83542c01cd1c023 Mon Sep 17 00:00:00 2001 From: sdovbenko <sdovbenko@magecom.us> Date: Fri, 6 Dec 2019 20:12:55 +0200 Subject: [PATCH 2307/2437] Changed protected to private modifier --- app/code/Magento/AsynchronousOperations/Model/Operation.php | 2 +- .../AsynchronousOperations/Model/OperationStatusPool.php | 2 +- .../AsynchronousOperations/Model/OperationStatusValidator.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation.php b/app/code/Magento/AsynchronousOperations/Model/Operation.php index 9e0039d2ee151..de2b56d0a0dd7 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation.php @@ -17,7 +17,7 @@ class Operation extends DataObject implements OperationInterface /** * @var OperationStatusValidator */ - protected $operationStatusValidator; + private $operationStatusValidator; /** * Operation constructor. diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php index 73eeedbdb7a20..890eb8c1c8c75 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php @@ -15,7 +15,7 @@ class OperationStatusPool /** * @var array */ - protected $statuses; + private $statuses; /** * @param array $statuses diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php index 3da94f1c4d7fb..ae154edfb50f7 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php @@ -17,7 +17,7 @@ class OperationStatusValidator /** * @var OperationStatusPool */ - protected $operationStatusPool; + private $operationStatusPool; /** * OperationStatusValidator constructor. From 345ece3f5277c1add6a1fd31cf8c830b3615c96f Mon Sep 17 00:00:00 2001 From: sdovbenko <sdovbenko@magecom.us> Date: Fri, 6 Dec 2019 20:13:59 +0200 Subject: [PATCH 2308/2437] Added OperationStatusValidatorTest class to Unit --- .../Model/OperationStatusValidatorTest.php | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php new file mode 100644 index 0000000000000..2209564d03aee --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php @@ -0,0 +1,155 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\AsynchronousOperations\Test\Unit\Model; + +use Magento\AsynchronousOperations\Model\OperationStatusValidator; +use Magento\AsynchronousOperations\Model\Operation; +use Magento\AsynchronousOperations\Model\OperationStatusPool; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Class OperationStatusValidatorTest + */ +class OperationStatusValidatorTest extends TestCase +{ + /** + * @var OperationStatusPool + */ + private $operationStatusPool; + + /** + * @var OperationStatusValidator + */ + private $operationStatusValidator; + + /** + * @var Operation + */ + private $operation; + + protected function setUp() + { + $this->operationStatusPool = $this->getMockBuilder(OperationStatusPool::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new ObjectManager($this); + + $this->operationStatusValidator = $objectManager->getObject( + OperationStatusValidator::class, + [ + 'operationStatusPool' => $this->operationStatusPool + ] + ); + + $this->operation = $objectManager->getObject( + Operation::class, + [ + 'operationStatusValidator' => $this->operationStatusValidator + ] + ); + } + + /** + * @param string $status + * @param array $statusPool + * @param string $expectedResult + * @dataProvider dataProviderForTestSetStatus + */ + public function testSetStatus ( + string $status, + array $statusPool, + string $expectedResult + ) { + $this->operationStatusPool + ->expects($this->any()) + ->method('getStatuses') + ->willReturn($statusPool); + + try { + $this->operation->setStatus($status); + $this->assertEquals($expectedResult, $this->operation->getStatus()); + } catch (\Exception $exception) { + $this->assertEquals($expectedResult, $exception->getMessage()); + } + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function dataProviderForTestSetStatus() + { + return [ + [ + 'status' => 0, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 'Invalid Operation Status.' + ], + [ + 'status' => 1, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 1 + ], + [ + 'status' => 2, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 2 + ], + [ + 'status' => 3, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 3 + ], + [ + 'status' => 4, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 4 + ], + [ + 'status' => 5, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 5 + ] + ]; + } +} From b1e518ca649a862abca2856db0c99d385ba964a3 Mon Sep 17 00:00:00 2001 From: Sergey Dovbenko <sdovbenko@magecom.us> Date: Fri, 6 Dec 2019 18:16:25 +0000 Subject: [PATCH 2309/2437] Corrected Code Styles --- .../Magento/AsynchronousOperations/Model/Operation.php | 8 ++++---- .../Model/OperationStatusValidator.php | 6 +++--- .../Test/Unit/Model/OperationStatusValidatorTest.php | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation.php b/app/code/Magento/AsynchronousOperations/Model/Operation.php index de2b56d0a0dd7..00f33d10a1e1b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation.php @@ -10,7 +10,7 @@ use Magento\Framework\DataObject; /** - * Class Operation + * Class Operation encapsulates methods for Operation Model Object */ class Operation extends DataObject implements OperationInterface { @@ -22,12 +22,12 @@ class Operation extends DataObject implements OperationInterface /** * Operation constructor. * - * @param array $data * @param OperationStatusValidator $operationStatusValidator + * @param array $data */ public function __construct( - array $data = [], - OperationStatusValidator $operationStatusValidator + OperationStatusValidator $operationStatusValidator, + array $data = [] ) { $this->operationStatusValidator = $operationStatusValidator; parent::__construct($data); diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php index ae154edfb50f7..f2ae135e4303c 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php @@ -32,7 +32,9 @@ public function __construct(OperationStatusPool $operationStatusPool) /** * Validate method * - * @param $status + * @param int $status + * @throws \InvalidArgumentException + * @return void */ public function validate($status) { @@ -41,7 +43,5 @@ public function validate($status) if (!in_array($status, $statuses)) { throw new \InvalidArgumentException('Invalid Operation Status.'); } - - return; } } diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php index 2209564d03aee..b93d9701a7a69 100644 --- a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\TestCase; /** - * Class OperationStatusValidatorTest + * Class OperationStatusValidatorTest implements logic for testing Operation::setStatus() method */ class OperationStatusValidatorTest extends TestCase { @@ -60,7 +60,7 @@ protected function setUp() * @param string $expectedResult * @dataProvider dataProviderForTestSetStatus */ - public function testSetStatus ( + public function testSetStatus( string $status, array $statusPool, string $expectedResult From 501f20d685d2a407e5c8f07b26977662b03fecdf Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 6 Dec 2019 16:07:25 -0600 Subject: [PATCH 2310/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- ...ithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index a2a4f65860254..8e5b965b460b5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,8 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <deleteData stepKey="deleteDefaultVirtualProduct" createDataKey="initialVirtualProduct"/> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> </after> From fd723af73f30f684176a9aec28738fe129475dff Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 16:07:58 -0600 Subject: [PATCH 2311/2437] Fix static --- .../Model/Config/Structure/Element/Dependency/Field.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 6171bdfca5584..f1434c70351af 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -6,11 +6,14 @@ namespace Magento\Config\Model\Config\Structure\Element\Dependency; /** - * Field - * * @api * @since 100.0.2 */ + +/** + * Class Field + * @package Magento\Config\Model\Config\Structure\Element\Dependency + */ class Field { /** From ade9147721e1182b7b166c0dd22dd85d634bc5ad Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 16:09:46 -0600 Subject: [PATCH 2312/2437] Fix static --- app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index 68cd0eb8cc1e9..a9054c7077d8b 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -7,6 +7,7 @@ /** * Class LayoutPlugin + * @package Magento\PageCache\Model\Layout */ class LayoutPlugin { From 9543ab82b20fbc68f425f712010bb0b6b73d23ae Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 16:11:28 -0600 Subject: [PATCH 2313/2437] Fix static --- .../Observer/SwitchPageCacheOnMaintenance/PageCacheState.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php index 5c52aa055ef16..531722a42eae6 100644 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -13,8 +13,8 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** - * Page Cache state. - * + * Class PageCacheState + * @package Magento\PageCache\Observer\SwitchPageCacheOnMaintenance * @deprecated Originally used by now removed observer SwitchPageCacheOnMaintenance */ class PageCacheState From 4f7fcbf9ac78463a0e144fd1b701ba624da12a44 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 6 Dec 2019 18:05:15 -0600 Subject: [PATCH 2314/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- ...WithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 8e5b965b460b5..67237785258bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,9 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <deleteData stepKey="deleteDefaultVirtualProduct" createDataKey="initialVirtualProduct"/> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> </after> From 183dd645b733a65d57b3bf6ad42daef6da75923a Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 7 Dec 2019 09:57:34 +0700 Subject: [PATCH 2315/2437] Resolve Email address mismatch with text in iPad(768) view issue25935 --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 1 + .../Magento/luma/Magento_Customer/web/css/source/_module.less | 1 + 2 files changed, 2 insertions(+) diff --git a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less index 213b8131815b3..0c2b1b4db83e6 100644 --- a/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Customer/web/css/source/_module.less @@ -85,6 +85,7 @@ .box-information, .box-newsletter { .box-content { + .lib-wrap-words(); line-height: 26px; } } diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index 6adf4b5b2f86b..6354cc35d32ed 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -126,6 +126,7 @@ .box-information, .box-newsletter { .box-content { + .lib-wrap-words(); &:extend(.abs-account-block-line-height all); } } From 93f070cbb0efbc8dbab6fea0b2d3dcc2211758b6 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 22:38:59 -0600 Subject: [PATCH 2316/2437] Fix static --- .../Config/Model/Config/Structure/Element/Dependency/Field.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index f1434c70351af..15f23c7737294 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -12,7 +12,8 @@ /** * Class Field - * @package Magento\Config\Model\Config\Structure\Element\Dependency + * + * Fields are used to describe possible values for a type/interface. */ class Field { From 24a45a24bdf2668875d61f568bf75a2a0e325b9c Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 22:40:15 -0600 Subject: [PATCH 2317/2437] Update LayoutPlugin.php --- app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index a9054c7077d8b..88619673ad425 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -7,7 +7,8 @@ /** * Class LayoutPlugin - * @package Magento\PageCache\Model\Layout + * + * Plugin for Magento\Framework\View\Layout */ class LayoutPlugin { From 3e25ec441aaa287500164cdceb1bc5889209fac4 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Fri, 6 Dec 2019 22:43:13 -0600 Subject: [PATCH 2318/2437] Fix static --- .../Observer/SwitchPageCacheOnMaintenance/PageCacheState.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php index 531722a42eae6..da6a71a0c2655 100644 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -14,7 +14,9 @@ /** * Class PageCacheState - * @package Magento\PageCache\Observer\SwitchPageCacheOnMaintenance + * + * Page Cache State Observer + * * @deprecated Originally used by now removed observer SwitchPageCacheOnMaintenance */ class PageCacheState From 2c93f7f49b3c12ad71ac69cd9f3ff7a38f76e547 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Fri, 6 Dec 2019 23:16:14 -0600 Subject: [PATCH 2319/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- ...thRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 67237785258bd..fbe1e7b85a0ed 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,7 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <deleteData stepKey="deleteDefaultVirtualProduct" createDataKey="initialVirtualProduct"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="$$initialVirtualProduct.sku$$"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> From 18281bbb12f323c20764a5128f962912782b852a Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 7 Dec 2019 08:16:26 +0200 Subject: [PATCH 2320/2437] Fixing static tests --- .../Backend/Block/System/Account/Edit/Form.php | 2 +- app/code/Magento/User/Block/Role/Tab/Info.php | 14 +++++++++++++- .../Framework/Data/Form/Element/Password.php | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php index 49ec305ddd761..c075585a6e4eb 100644 --- a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php +++ b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php @@ -68,7 +68,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function _prepareForm() { diff --git a/app/code/Magento/User/Block/Role/Tab/Info.php b/app/code/Magento/User/Block/Role/Tab/Info.php index f71b7eebd3c32..721271c87c67d 100644 --- a/app/code/Magento/User/Block/Role/Tab/Info.php +++ b/app/code/Magento/User/Block/Role/Tab/Info.php @@ -6,7 +6,7 @@ namespace Magento\User\Block\Role\Tab; /** - * implementing now + * Info * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ @@ -18,6 +18,8 @@ class Info extends \Magento\Backend\Block\Widget\Form\Generic implements \Magent const IDENTITY_VERIFICATION_PASSWORD_FIELD = 'current_password'; /** + * Get tab label + * * @return \Magento\Framework\Phrase */ public function getTabLabel() @@ -26,6 +28,8 @@ public function getTabLabel() } /** + * Get tab title + * * @return string */ public function getTabTitle() @@ -34,6 +38,8 @@ public function getTabTitle() } /** + * Can show tab + * * @return bool */ public function canShowTab() @@ -42,6 +48,8 @@ public function canShowTab() } /** + * Is tab hidden + * * @return bool */ public function isHidden() @@ -50,6 +58,8 @@ public function isHidden() } /** + * Before html rendering + * * @return $this */ public function _beforeToHtml() @@ -60,6 +70,8 @@ public function _beforeToHtml() } /** + * Form initializatiion + * * @return void */ protected function _initForm() diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Password.php b/lib/internal/Magento/Framework/Data/Form/Element/Password.php index c71048dabd1df..df95133b4abf2 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Password.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Password.php @@ -38,11 +38,14 @@ public function __construct( } /** + * Get field html + * * @return mixed */ public function getHtml() { $this->addClass('input-text admin__control-text'); + return parent::getHtml(); } } From 8ea3d264ae8be701faa30bc597fd046b697d1582 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Sat, 7 Dec 2019 09:01:37 +0200 Subject: [PATCH 2321/2437] Fixing static tests --- app/code/Magento/User/Block/Role/Tab/Info.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/User/Block/Role/Tab/Info.php b/app/code/Magento/User/Block/Role/Tab/Info.php index 721271c87c67d..1b8b528aff774 100644 --- a/app/code/Magento/User/Block/Role/Tab/Info.php +++ b/app/code/Magento/User/Block/Role/Tab/Info.php @@ -8,6 +8,8 @@ /** * Info * + * User role tab info + * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ class Info extends \Magento\Backend\Block\Widget\Form\Generic implements \Magento\Backend\Block\Widget\Tab\TabInterface From 41437b1de9a3e32c93b55256f30b4e0226a7eb17 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Sat, 7 Dec 2019 08:28:39 -0600 Subject: [PATCH 2322/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- ...WithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index fbe1e7b85a0ed..bbde98f8fc043 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -29,7 +29,7 @@ </before> <after> <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> - <argument name="sku" value="$$initialVirtualProduct.sku$$"/> + <argument name="sku" value="{{updateVirtualProductRegularPrice5OutOfStock.sku}}"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> From fe935a668044bf02852b5b2ddcb01834090132a7 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 8 Dec 2019 22:32:41 +0700 Subject: [PATCH 2323/2437] Resolve queue_consumer.xml doesn't allow numbers in handler class issue25731 --- .../Test/Unit/Consumer/Config/XsdTest.php | 12 ++++++------ .../Magento/Framework/MessageQueue/etc/consumer.xsd | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/MessageQueue/Test/Unit/Consumer/Config/XsdTest.php b/lib/internal/Magento/Framework/MessageQueue/Test/Unit/Consumer/Config/XsdTest.php index 448a8a3db7407..1eecf94558960 100644 --- a/lib/internal/Magento/Framework/MessageQueue/Test/Unit/Consumer/Config/XsdTest.php +++ b/lib/internal/Magento/Framework/MessageQueue/Test/Unit/Consumer/Config/XsdTest.php @@ -84,17 +84,17 @@ public function exemplarXmlDataProvider() ], 'invalid handler format' => [ '<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd"> - <consumer name="consumer1" queue="queue1" handler="handlerClass1::handlerMethodOne" consumerInstance="consumerClass1" connection="amqp" maxMessages="100"/> - <consumer name="consumer2" queue="queue2" handler="handlerClassTwo::handlerMethod2" consumerInstance="consumerClass2" connection="db"/> + <consumer name="consumer1" queue="queue1" handler="handlerClass_One1::handlerMethod1" consumerInstance="consumerClass1" connection="amqp" maxMessages="100"/> + <consumer name="consumer2" queue="queue2" handler="handlerClassOne2::handler_Method2" consumerInstance="consumerClass2" connection="db"/> <consumer name="consumer3" queue="queue3" handler="handlerClassThree::handlerMethodThree" consumerInstance="consumerClass3"/> <consumer name="consumer4" queue="queue4" handler="handlerClassFour::handlerMethodFour"/> <consumer name="consumer5" queue="queue4"/> </config>', [ - "Element 'consumer', attribute 'handler': [facet 'pattern'] The value 'handlerClass1::handlerMethodOne' is not accepted by the pattern '[a-zA-Z\\\\]+::[a-zA-Z]+'.", - "Element 'consumer', attribute 'handler': 'handlerClass1::handlerMethodOne' is not a valid value of the atomic type 'handlerType'.", - "Element 'consumer', attribute 'handler': [facet 'pattern'] The value 'handlerClassTwo::handlerMethod2' is not accepted by the pattern '[a-zA-Z\\\\]+::[a-zA-Z]+'.", - "Element 'consumer', attribute 'handler': 'handlerClassTwo::handlerMethod2' is not a valid value of the atomic type 'handlerType'.", + "Element 'consumer', attribute 'handler': [facet 'pattern'] The value 'handlerClass_One1::handlerMethod1' is not accepted by the pattern '[a-zA-Z0-9\\\\]+::[a-zA-Z0-9]+'.", + "Element 'consumer', attribute 'handler': 'handlerClass_One1::handlerMethod1' is not a valid value of the atomic type 'handlerType'.", + "Element 'consumer', attribute 'handler': [facet 'pattern'] The value 'handlerClassOne2::handler_Method2' is not accepted by the pattern '[a-zA-Z0-9\\\\]+::[a-zA-Z0-9]+'.", + "Element 'consumer', attribute 'handler': 'handlerClassOne2::handler_Method2' is not a valid value of the atomic type 'handlerType'.", ], ], 'invalid maxMessages format' => [ diff --git a/lib/internal/Magento/Framework/MessageQueue/etc/consumer.xsd b/lib/internal/Magento/Framework/MessageQueue/etc/consumer.xsd index 52bfc77bbb511..7e3d501aaa46e 100644 --- a/lib/internal/Magento/Framework/MessageQueue/etc/consumer.xsd +++ b/lib/internal/Magento/Framework/MessageQueue/etc/consumer.xsd @@ -32,7 +32,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z\\]+::[a-zA-Z]+" /> + <xs:pattern value="[a-zA-Z0-9\\]+::[a-zA-Z0-9]+" /> <xs:minLength value="5" /> </xs:restriction> </xs:simpleType> From 850db28b947c495c9a320a449568319f478ed784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Sun, 8 Dec 2019 22:24:05 +0100 Subject: [PATCH 2324/2437] Refactor: Add method hints to Tracking Status --- .../Shipping/Model/Tracking/Result/Status.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php index cb24bd8ebb0e8..f3e4dcfe986f0 100644 --- a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php +++ b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php @@ -1,21 +1,25 @@ <?php +declare(strict_types=1); + /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Shipping\Model\Tracking\Result; /** - * Fields: - * - carrier: carrier code - * - carrierTitle: carrier title + * @method string getCarrier() + * @method Status setCarrier(string $carrierCode) + * @method string getCarrierTitle() + * @method Status setCarrierTitle(string $carrierTitle) */ -class Status extends \Magento\Shipping\Model\Tracking\Result\AbstractResult +class Status extends AbstractResult { /** * @return array */ - public function getAllData() + public function getAllData(): array { return $this->_data; } From ec9380ee1cbbf115534dba9a88ac1c1e87414cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Sun, 8 Dec 2019 23:30:41 +0100 Subject: [PATCH 2325/2437] Refactor: Add method hints to Tracking Status --- app/code/Magento/Shipping/Model/Tracking/Result/Status.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php index f3e4dcfe986f0..04ff29139d926 100644 --- a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php +++ b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php @@ -9,6 +9,8 @@ namespace Magento\Shipping\Model\Tracking\Result; /** + * Tracking Status DataObject + * * @method string getCarrier() * @method Status setCarrier(string $carrierCode) * @method string getCarrierTitle() @@ -17,6 +19,8 @@ class Status extends AbstractResult { /** + * Returns all Status data + * * @return array */ public function getAllData(): array From 86c17d6a8fa0ff9a2637b81a985d4145414a6d09 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Sun, 8 Dec 2019 17:00:16 -0600 Subject: [PATCH 2326/2437] Fix static --- app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php index 1818da2289b58..5df1da7a194d0 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php @@ -132,7 +132,7 @@ public function testCreateBlockWithException() { $invalidBlock = $this->createMock(Product::class); $this->expectException(LocalizedException::class); - $this->expectExceptionMessage((string)__('Invalid block type: %1')); + $this->expectExceptionMessage((string)__('Invalid block type: %1')); // @codingStandardsIgnoreLine $this->helper->createBlock($invalidBlock); } From 9044b1fa828343530793edd9b72f2938a97a8b24 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Sun, 8 Dec 2019 21:57:47 -0600 Subject: [PATCH 2327/2437] B2B-299: Fix Skipped MFTF Tests From MC-17140: MC-15884, MC-16695 --- ...ductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml | 3 +++ ...roductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml | 3 +++ ...ctWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml | 3 +++ ...ithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml index ffbad0752b73c..15e5be210b73a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductRegularPrice5OutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml index aa3184994daff..8b4518007ea29 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml @@ -27,6 +27,9 @@ </createData> </before> <after> + <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductRegularPrice99OutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml index 3101c1e460322..984c296845113 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductSpecialPrice.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 58978c31b5b40..1c590563d4cfc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="deleteProductBySku" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductSpecialPriceOutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> From 53323fa950c04bbfe5e1d168ae8bf612f1451d47 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Mon, 9 Dec 2019 00:35:41 -0600 Subject: [PATCH 2328/2437] Fix static --- .../frontend/templates/product/view/opengraph/general.phtml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml index 4d4a34c6239d4..4bfdbb7bc24bc 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml @@ -15,7 +15,10 @@ <meta property="og:description" content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getShortDescription())) ?>" /> <meta property="og:url" content="<?= $block->escapeUrl($block->getProduct()->getProductUrl()) ?>" /> -<?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()) :?> +<?php if ($priceAmount = $block->getProduct() + ->getPriceInfo() + ->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->getAmount()):?> <meta property="product:price:amount" content="<?= $block->escapeHtmlAttr($priceAmount) ?>"/> <?= $block->getChildHtml('meta.currency') ?> <?php endif;?> From db316fd45927ce2dd3747c04e824e0a4165f58e4 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Mon, 9 Dec 2019 00:42:12 -0600 Subject: [PATCH 2329/2437] Fix static --- app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php index 5df1da7a194d0..122d785e79599 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php @@ -132,7 +132,6 @@ public function testCreateBlockWithException() { $invalidBlock = $this->createMock(Product::class); $this->expectException(LocalizedException::class); - $this->expectExceptionMessage((string)__('Invalid block type: %1')); // @codingStandardsIgnoreLine $this->helper->createBlock($invalidBlock); } From 3e65b3732aeee0e87fc66b2b66eb06cfed2acd52 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 9 Dec 2019 10:18:54 +0200 Subject: [PATCH 2330/2437] Cover case with unit test --- .../Framework/Data/Test/Unit/Form/Element/PasswordTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/PasswordTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/PasswordTest.php index d8de658d6813a..8ceac46c6404b 100644 --- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/PasswordTest.php +++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/Element/PasswordTest.php @@ -53,6 +53,6 @@ public function testGetHtml() { $html = $this->_model->getHtml(); $this->assertContains('type="password"', $html); - $this->assertTrue(preg_match('/class=\".*input-text.*\"/i', $html) > 0); + $this->assertTrue(preg_match('/class=\"* input-text admin__control-text.*\"/i', $html) > 0); } } From 2d0c917f48e5aa0d4b9e461834f9d4d147e53000 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Mon, 9 Dec 2019 15:46:34 +0700 Subject: [PATCH 2331/2437] Integration Test for issue 25931 --- .../Adminhtml/Report/Statistics/IndexTest.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Statistics/IndexTest.php diff --git a/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Statistics/IndexTest.php b/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Statistics/IndexTest.php new file mode 100644 index 0000000000000..e7d74159e48fc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Statistics/IndexTest.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Reports\Controller\Adminhtml\Report\Statistics; + +/** + * @magentoAppArea adminhtml + */ +class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + /** + * Test load page + */ + public function testExecute() + { + $this->dispatch('backend/reports/report_statistics'); + $actual = $this->getResponse()->getBody(); + $this->assertContains('Never', $actual); + } +} From 206f2dd8928ef5f2198c2c199f80bc568e59debf Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 9 Dec 2019 11:00:18 +0100 Subject: [PATCH 2332/2437] Update app/code/Magento/Shipping/Model/Tracking/Result/Status.php Co-Authored-By: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> --- app/code/Magento/Shipping/Model/Tracking/Result/Status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php index 04ff29139d926..5f80c31120ec1 100644 --- a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php +++ b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php @@ -13,7 +13,7 @@ * * @method string getCarrier() * @method Status setCarrier(string $carrierCode) - * @method string getCarrierTitle() + * @method string|null getCarrierTitle() * @method Status setCarrierTitle(string $carrierTitle) */ class Status extends AbstractResult From 9a40b1656a6568963c540bd234889d736dbbf492 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Mon, 9 Dec 2019 11:00:29 +0100 Subject: [PATCH 2333/2437] Update app/code/Magento/Shipping/Model/Tracking/Result/Status.php Co-Authored-By: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> --- app/code/Magento/Shipping/Model/Tracking/Result/Status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php index 5f80c31120ec1..784b806fa3f86 100644 --- a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php +++ b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php @@ -11,7 +11,7 @@ /** * Tracking Status DataObject * - * @method string getCarrier() + * @method string|null getCarrier() * @method Status setCarrier(string $carrierCode) * @method string|null getCarrierTitle() * @method Status setCarrierTitle(string $carrierTitle) From 1a285eac193762b3c11123ac9600d22ec1ac300a Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 9 Dec 2019 12:07:23 +0200 Subject: [PATCH 2334/2437] Cover changes with mftf test --- .../Test/Mftf/Section/AdminThemeSection.xml | 5 ++- .../Mftf/Test/AdminContentThemeSortTest.xml | 40 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml index 219ca7420361c..1a0bb738bc9ca 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml @@ -15,7 +15,8 @@ <element name="rowsInThemeTitleColumn" type="text" selector="//tbody/tr/td[contains(@class, 'parent_theme')]/preceding-sibling::td"/> <element name="rowsInColumn" type="text" selector="//tbody/tr/td[contains(@class, '{{column}}')]" parameterized="true"/> <!--Specific cell e.g. {{Section.gridCell('Name')}}--> - <element name="gridCell" type="text" selector="//table[@id='theme_grid_table']//td[contains(text(), '{{gridCellText}}')]" parameterized="true"/> + <element name="gridCell" type="text" selector="//table//div[contains(text(), '{{gridCellText}}')]" parameterized="true"/> + <element name="gridCellUpdated" type="text" selector="//tbody//tr//div[contains(text(), '{{gridCellText}}')]" parameterized="true"/> <element name="columnHeader" type="text" selector="//thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml new file mode 100644 index 0000000000000..5e251e452e263 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminContentThemesSortTest"> + <annotations> + <features value="Theme"/> + <stories value="Menu Navigation"/> + <title value="Admin content themes sort themes test"/> + <description value="Admin should be able to sort Themes"/> + <severity value="CRITICAL"/> + <testCaseId value="https://github.com/magento/magento2/pull/25926"/> + <group value="menu"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentThemesPage"> + <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuContentDesignThemes.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> + <argument name="title" value="{{AdminMenuContentDesignThemes.pageTitle}}"/> + </actionGroup> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitle"/> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitleSecondTime"/> + <seeNumberOfElements selector="{{AdminThemeSection.rowsInColumn('theme_path')}}" userInput="2" stepKey="see2RowsOnTheGrid"/> + <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Luma')}}" stepKey="seeLumaThemeInTitleColumn"/> + <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Blank')}}" stepKey="seeBlankThemeInTitleColumn"/> + </test> +</tests> From 7cd10625d1208c4599e711bbbba4b50edca4f953 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 9 Dec 2019 12:09:42 +0200 Subject: [PATCH 2335/2437] fix typo --- .../Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml index 5e251e452e263..d27ed90f80520 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -31,8 +31,8 @@ <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> <argument name="title" value="{{AdminMenuContentDesignThemes.pageTitle}}"/> </actionGroup> - <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitle"/> - <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitleSecondTime"/> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitle"/> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitleSecondTime"/> <seeNumberOfElements selector="{{AdminThemeSection.rowsInColumn('theme_path')}}" userInput="2" stepKey="see2RowsOnTheGrid"/> <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Luma')}}" stepKey="seeLumaThemeInTitleColumn"/> <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Blank')}}" stepKey="seeBlankThemeInTitleColumn"/> From 05c84c29df8a2054a7aa04471696a884861acc21 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 9 Dec 2019 12:24:18 +0200 Subject: [PATCH 2336/2437] refactorign --- .../Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml index d27ed90f80520..9facab57e9a09 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -34,7 +34,6 @@ <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitle"/> <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitleSecondTime"/> <seeNumberOfElements selector="{{AdminThemeSection.rowsInColumn('theme_path')}}" userInput="2" stepKey="see2RowsOnTheGrid"/> - <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Luma')}}" stepKey="seeLumaThemeInTitleColumn"/> - <seeElement selector="{{AdminThemeSection.gridCellUpdated('Magento Blank')}}" stepKey="seeBlankThemeInTitleColumn"/> + <seeNumberOfElements selector="{{AdminThemeSection.gridCellUpdated('Magento Luma')}}" userInput="1" stepKey="seeLumaThemeInTitleColumn"/> </test> </tests> From 46bfa6a6d301569d1f75160bea8655f5cdb1d16e Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Mon, 9 Dec 2019 14:25:11 -0600 Subject: [PATCH 2337/2437] MQE-1878: Stabilize community PR 742 --- .../StorefrontCustomerWishlistActionGroup.xml | 43 +++++++++++++++++++ ...eProductFromShoppingCartToWishlistTest.xml | 25 ++++++----- ...eProductFromShoppingCartToWishlistTest.xml | 25 ++++++----- ...eProductFromShoppingCartToWishlistTest.xml | 25 ++++++----- ...lProductFromShoppingCartToWishlistTest.xml | 16 +++---- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml index 4c1c088c102cd..5568f255304a7 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml @@ -135,4 +135,47 @@ <dontSeeElement selector="{{StorefrontCustomerWishlistProductSection.pager}}" stepKey="checkThatPagerIsAbsent"/> <see selector="{{StorefrontCustomerWishlistProductSection.wishlistEmpty}}" userInput="You have no items in your wish list." stepKey="checkNoItemsMessage"/> </actionGroup> + + <actionGroup name="AssertMoveProductToWishListSuccessMessage"> + <annotations> + <description>Moves a product from the cart to the wishlist.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName(productName)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForMove"/> + <see userInput="{{productName}} has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertSuccess"/> + </actionGroup> + + <actionGroup name="AssertProductIsPresentInWishList"> + <annotations> + <description>Go to storefront customer wishlist page and assert product name and price is present.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productPrice" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishList"/> + <waitForPageLoad stepKey="waitForWishList"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productName)}}" time="30" stepKey="assertProductName"/> + <see userInput="{{productPrice}}" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productName)}}" stepKey="assertProductPrice"/> + </actionGroup> + + <actionGroup name="AssertProductDetailsInWishlist"> + <annotations> + <description>Assert product name and price in wishlist on hover.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="label" type="string"/> + <argument name="labelValue" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productName)}}" stepKey="moveMouseOverProductInfo"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productName)}}" stepKey="seeAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productName)}}" stepKey="seeImage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName(productName)}}" stepKey="moveMouseOverProductDetails"/> + <see userInput="{{label}}" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName(productName)}}" stepKey="seeLabel"/> + <see userInput="{{labelValue}}" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName(productName)}}" stepKey="seeLabelValue"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml index 317f937def3f1..49cd78ec1884f 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -141,23 +141,22 @@ <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> <!-- Assert move product to wishlist success message --> - <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createConfigProduct.name$$)}}" stepKey="moveToWishlist"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="$$createConfigProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + <actionGroup ref="AssertMoveProductToWishListSuccessMessage" stepKey="moveToWishlist"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> <!-- Assert product is present in wishlist --> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> - <waitForPageLoad stepKey="waitForWishlistPage"/> - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="$20.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + <actionGroup ref="AssertProductIsPresentInWishList" stepKey="assertProductPresent"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="productPrice" value="$20.00"/> + </actionGroup> <!-- Assert product details in Wishlist --> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createConfigProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createConfigProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> - <see userInput="$$createConfigProductAttribute.default_value$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createConfigProduct.name$$)}}" stepKey="seeAttribute"/> - <see userInput="$$getConfigAttributeOption2.label$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createConfigProduct.name$$)}}" stepKey="seeOption"/> + <actionGroup ref="AssertProductDetailsInWishlist" stepKey="assertProductDetails"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="label" value="$$createConfigProductAttribute.default_value$$"/> + <argument name="labelValue" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> </test> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml index dcd69a61e596f..856ebdb7bb73a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -87,23 +87,22 @@ <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> <!-- Assert move product to wishlist success message --> - <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createBundleProduct.name$$)}}" stepKey="moveToWishlist"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="$$createBundleProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + <actionGroup ref="AssertMoveProductToWishListSuccessMessage" stepKey="moveToWishlist"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> <!-- Assert product is present in wishlist --> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> - <waitForPageLoad stepKey="waitForWishlistPage"/> - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createBundleProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="$100.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + <actionGroup ref="AssertProductIsPresentInWishList" stepKey="assertProductPresent"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="productPrice" value="$100.00"/> + </actionGroup> <!-- Assert product details in Wishlist --> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createBundleProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> - <see userInput="$$createBundleOption1_1.title$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createBundleProduct.name$$)}}" stepKey="seeBundleOption"/> - <see userInput="$$simpleProduct1.sku$$ $100.00" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createBundleProduct.name$$)}}" stepKey="seeProduct"/> + <actionGroup ref="AssertProductDetailsInWishlist" stepKey="assertProductDetails"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="label" value="$$createBundleOption1_1.title$$"/> + <argument name="labelValue" value="$$simpleProduct1.sku$$ $100.00"/> + </actionGroup> <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> </test> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml index 4d99b05e9aa6a..9c3984d0287ec 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml @@ -78,23 +78,22 @@ <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> <!-- Assert move product to wishlist success message --> - <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createBundleProduct.name$$)}}" stepKey="moveToWishlist"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="$$createBundleProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> + <actionGroup ref="AssertMoveProductToWishListSuccessMessage" stepKey="moveToWishlist"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> <!-- Assert product is present in wishlist --> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> - <waitForPageLoad stepKey="waitForWishlistPage"/> - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName($$createBundleProduct.name$$)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="$101.23" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductPrice"/> + <actionGroup ref="AssertProductIsPresentInWishList" stepKey="assertProductPresent"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="productPrice" value="$101.23"/> + </actionGroup> <!-- Assert product details in Wishlist --> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName($$createBundleProduct.name$$)}}" stepKey="AssertWishlistProductImage"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName($$createBundleProduct.name$$)}}" stepKey="seeDetailsMoveMouseOverProduct"/> - <see userInput="$$createBundleOption1_1.title$$" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName($$createBundleProduct.name$$)}}" stepKey="seeBundleOption"/> - <see userInput="$$simpleProduct1.sku$$ $100.00" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName($$createBundleProduct.name$$)}}" stepKey="seeProduct"/> + <actionGroup ref="AssertProductDetailsInWishlist" stepKey="assertProductDetails"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="label" value="$$createBundleOption1_1.title$$"/> + <argument name="labelValue" value="$$simpleProduct1.sku$$ $100.00"/> + </actionGroup> <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> </test> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml index baaae80f7d081..20ac78dfbf731 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml @@ -53,16 +53,14 @@ <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> <!-- Assert move product to wishlist success message --> - <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName($$createProduct.name$$)}}" stepKey="moveToWishlist"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="$$createProduct.name$$ has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertMoveProductToWishlistSuccessMessage"/> - - <!-- Assert product is present in wishlist --> - <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishlistPage"/> - <waitForPageLoad stepKey="waitForWishlistPage"/> + <actionGroup ref="AssertMoveProductToWishListSuccessMessage" stepKey="moveToWishlist"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlist" stepKey="assertProductIsPresentInWishlist"> - <argument name="productVar" value="$$createProduct$$"/> + <!-- Assert product is present in wishlist --> + <actionGroup ref="AssertProductIsPresentInWishList" stepKey="assertProductPresent"> + <argument name="productName" value="$$createProduct.name$$"/> + <argument name="productPrice" value="$$createProduct.price$$"/> </actionGroup> <!-- Assert cart is empty --> From 41aad64a1aedc6be608ab624069e15860bb8c734 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Mon, 9 Dec 2019 22:19:40 +0000 Subject: [PATCH 2338/2437] Update values to make linter happy --- .../adminhtml/Magento/backend/web/css/source/_actions.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index 28912d873ae00..c86e9cdbf0866 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -446,12 +446,12 @@ button { } &::after { - border-color: transparent transparent transparent #000; + border-color: transparent transparent transparent @color-black; border-style: solid; - border-width: 0.4rem 0 0.4rem 0.5rem; + border-width: .4rem 0 .4rem .5rem; content: ''; height: 0; - margin-top: -0.2rem; + margin-top: -.2rem; position: absolute; right: 1rem; top: 50%; From 8039f7498e4e1edca652fab745a52f1816d69584 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Mon, 9 Dec 2019 19:04:18 -0600 Subject: [PATCH 2339/2437] MC-23411: Random failure of PAT builds --- setup/src/Magento/Setup/Fixtures/_files/dictionary.csv | 2 +- .../Setup/Model/Address/AddressDataGenerator.php | 2 +- setup/src/Magento/Setup/Model/DataGenerator.php | 10 +++++----- .../Setup/Model/Description/DescriptionGenerator.php | 2 +- .../Description/DescriptionParagraphGenerator.php | 2 +- .../Model/Description/DescriptionSentenceGenerator.php | 2 +- .../Setup/Model/Description/Mixin/BoldMixin.php | 2 +- .../Description/Mixin/Helper/RandomWordSelector.php | 2 +- .../Setup/Model/Description/Mixin/ItalicMixin.php | 2 +- .../Setup/Model/Description/Mixin/SpanMixin.php | 2 +- setup/src/Magento/Setup/Model/Dictionary.php | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv b/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv index c839b7c46f51c..ba188534a3e24 100644 --- a/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv +++ b/setup/src/Magento/Setup/Fixtures/_files/dictionary.csv @@ -7370,4 +7370,4 @@ Gregory pine borrowed bow -disturbing \ No newline at end of file +disturbing diff --git a/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php b/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php index 1bbd152f2ba4e..2d450a4374c5e 100644 --- a/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php +++ b/setup/src/Magento/Setup/Model/Address/AddressDataGenerator.php @@ -18,7 +18,7 @@ class AddressDataGenerator public function generateAddress() { return [ - 'postcode' => random_int(10000, 99999) + 'postcode' => mt_rand(10000, 99999) ]; } } diff --git a/setup/src/Magento/Setup/Model/DataGenerator.php b/setup/src/Magento/Setup/Model/DataGenerator.php index 540433e5aa3ec..c7c975f2d993d 100644 --- a/setup/src/Magento/Setup/Model/DataGenerator.php +++ b/setup/src/Magento/Setup/Model/DataGenerator.php @@ -4,11 +4,11 @@ * See COPYING.txt for license details. */ -/** - * A custom adapter that allows generating arbitrary descriptions - */ namespace Magento\Setup\Model; +/** + * A custom adapter that allows generating arbitrary descriptions. + */ class DataGenerator { /** @@ -67,12 +67,12 @@ protected function readData() */ public function generate($minAmountOfWords, $maxAmountOfWords, $key = null) { - $numberOfWords = random_int($minAmountOfWords, $maxAmountOfWords); + $numberOfWords = mt_rand($minAmountOfWords, $maxAmountOfWords); $result = ''; if ($key === null || !array_key_exists($key, $this->generatedValues)) { for ($i = 0; $i < $numberOfWords; $i++) { - $result .= ' ' . $this->dictionaryData[random_int(0, count($this->dictionaryData) - 1)]; + $result .= ' ' . $this->dictionaryData[mt_rand(0, count($this->dictionaryData) - 1)]; } $result = trim($result); diff --git a/setup/src/Magento/Setup/Model/Description/DescriptionGenerator.php b/setup/src/Magento/Setup/Model/Description/DescriptionGenerator.php index a790bfdbe608d..807e1fde7d90d 100644 --- a/setup/src/Magento/Setup/Model/Description/DescriptionGenerator.php +++ b/setup/src/Magento/Setup/Model/Description/DescriptionGenerator.php @@ -63,7 +63,7 @@ public function generate() */ private function generateRawDescription() { - $paragraphsCount = random_int( + $paragraphsCount = mt_rand( $this->descriptionConfig['paragraphs']['count-min'], $this->descriptionConfig['paragraphs']['count-max'] ); diff --git a/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php b/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php index 57ece1f9558c3..50544e6ea9726 100644 --- a/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php +++ b/setup/src/Magento/Setup/Model/Description/DescriptionParagraphGenerator.php @@ -39,7 +39,7 @@ public function __construct( */ public function generate() { - $sentencesCount = random_int( + $sentencesCount = mt_rand( $this->paragraphConfig['sentences']['count-min'], $this->paragraphConfig['sentences']['count-max'] ); diff --git a/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php b/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php index 10b07e7e1c7a2..299b4b50bed0f 100644 --- a/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php +++ b/setup/src/Magento/Setup/Model/Description/DescriptionSentenceGenerator.php @@ -39,7 +39,7 @@ public function __construct( */ public function generate() { - $sentenceWordsCount = random_int( + $sentenceWordsCount = mt_rand( $this->sentenceConfig['words']['count-min'], $this->sentenceConfig['words']['count-max'] ); diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php index 927759c4bfa7c..db208adc67de8 100644 --- a/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php +++ b/setup/src/Magento/Setup/Model/Description/Mixin/BoldMixin.php @@ -48,7 +48,7 @@ public function apply($text) return $this->wordWrapper->wrapWords( $text, - $this->randomWordSelector->getRandomWords($rawText, random_int(5, 8)), + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), '<b>%s</b>' ); } diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php b/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php index c7efcc7f12e0f..0598db218728f 100644 --- a/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php +++ b/setup/src/Magento/Setup/Model/Description/Mixin/Helper/RandomWordSelector.php @@ -27,7 +27,7 @@ public function getRandomWords($source, $count) $randWords = []; $wordsSize = count($words); while ($count) { - $randWords[] = $words[random_int(0, $wordsSize - 1)]; + $randWords[] = $words[mt_rand(0, $wordsSize - 1)]; $count--; } diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/ItalicMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/ItalicMixin.php index 7621bccf08773..87e033be330cf 100644 --- a/setup/src/Magento/Setup/Model/Description/Mixin/ItalicMixin.php +++ b/setup/src/Magento/Setup/Model/Description/Mixin/ItalicMixin.php @@ -48,7 +48,7 @@ public function apply($text) return $this->wordWrapper->wrapWords( $text, - $this->randomWordSelector->getRandomWords($rawText, random_int(5, 8)), + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), '<i>%s</i>' ); } diff --git a/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php b/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php index fe53ebef535a8..ed5a836129460 100644 --- a/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php +++ b/setup/src/Magento/Setup/Model/Description/Mixin/SpanMixin.php @@ -48,7 +48,7 @@ public function apply($text) return $this->wordWrapper->wrapWords( $text, - $this->randomWordSelector->getRandomWords($rawText, random_int(5, 8)), + $this->randomWordSelector->getRandomWords($rawText, mt_rand(5, 8)), '<span>%s</span>' ); } diff --git a/setup/src/Magento/Setup/Model/Dictionary.php b/setup/src/Magento/Setup/Model/Dictionary.php index 52f7cc1bb5148..630d35092d0fc 100644 --- a/setup/src/Magento/Setup/Model/Dictionary.php +++ b/setup/src/Magento/Setup/Model/Dictionary.php @@ -40,7 +40,7 @@ public function getRandWord() $this->readDictionary(); } - $randIndex = random_int(0, count($this->dictionary) - 1); + $randIndex = mt_rand(0, count($this->dictionary) - 1); return trim($this->dictionary[$randIndex]); } From 393ef91131c4649784a887abe42cb242921d94e8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 10 Dec 2019 10:11:19 +0200 Subject: [PATCH 2340/2437] MC-5233: DateTime product attributes support --- .../Unit/Ui/Component/ColumnFactoryTest.php | 3 ++- .../Product/Form/Modifier/EavTest.php | 3 ++- .../Catalog/Ui/Component/ColumnFactory.php | 2 +- .../Product/Form/Modifier/Eav.php | 2 +- .../catalog/product/attribute/js.phtml | 24 ++++++++++++------- .../Test/Unit/Model/Entity/AttributeTest.php | 23 +++++++++++------- .../Component/Form/Element/DataType/Date.php | 2 +- .../Unit/Component/Filters/Type/DateTest.php | 2 +- 8 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 78e241100dc3b..b3acaa4b8bbed 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase; /** - * ColumnFactory test. + * Test to Create columns factory on product grid page */ class ColumnFactoryTest extends TestCase { @@ -206,6 +206,7 @@ public function testCreateDateColumn( 'component' => 'Magento_Ui/js/grid/columns/date', 'timezone' => $expectedTimezone, 'dateFormat' => $expectedDateFormat, + '__disableTmpl' => ['label' => true], 'options' => [ 'showsTime' => $showsTime ] diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 6221704d51112..91e22407acc43 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -41,7 +41,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class EavTest + * Class to test Data provider for eav attributes on product page * * @method Eav getModel * @SuppressWarnings(PHPMD.TooManyFields) @@ -692,6 +692,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => ['label' => true, 'code' => true] ], 'locked' => false, 'frontendInput' => 'datetime', diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 61bc518756ba6..b902e741c006c 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -15,7 +15,7 @@ use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** - * Column Factory + * Create columns factory on product grid page * * @api * @since 100.0.2 diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index bc5bee64912c7..25e816f79639a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -40,7 +40,7 @@ use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory as AttributeCollectionFactory; /** - * Class Eav + * Data provider for eav attributes on product page * * @api * diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index 212a345f4bcbc..64384ac391a8d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Catalog\Helper\Data; // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> @@ -63,7 +64,12 @@ function bindAttributeInputType() { checkOptionsPanelVisibility(); switchDefaultValueField(); - if($('frontend_input') && ($('frontend_input').value=='boolean' || $('frontend_input').value=='select' || $('frontend_input').value=='multiselect' || $('frontend_input').value=='price')){ + if ($('frontend_input') + && ($('frontend_input').value=='boolean' + || $('frontend_input').value=='select' + || $('frontend_input').value=='multiselect' + || $('frontend_input').value=='price') + ){ if($('is_filterable') && !$('is_filterable').getAttribute('readonly')){ $('is_filterable').disabled = false; } @@ -75,8 +81,7 @@ function bindAttributeInputType() if($('backend_type').options[i].value=='int') $('backend_type').selectedIndex = i; } } - } - else { + } else { if($('is_filterable')){ $('is_filterable').selectedIndex=0; $('is_filterable').disabled = true; @@ -203,21 +208,22 @@ function switchDefaultValueField() setRowVisibility('frontend_class', false); break; - <?php foreach ($this->helper(Magento\Catalog\Helper\Data::class)->getAttributeHiddenFields() as $type => $fields) :?> + <?php // phpcs:ignore Magento2.Templates.ThisInTemplate ?> + <?php foreach ($this->helper(Data::class)->getAttributeHiddenFields() as $type => $fields): ?> case '<?= $block->escapeJs($type) ?>': var isFrontTabHidden = false; - <?php foreach ($fields as $one) :?> - <?php if ($one == '_front_fieldset') :?> + <?php foreach ($fields as $one): ?> + <?php if ($one == '_front_fieldset'): ?> getFrontTab().hide(); isFrontTabHidden = true; - <?php elseif ($one == '_default_value') :?> + <?php elseif ($one == '_default_value'): ?> defaultValueTextVisibility = defaultValueTextareaVisibility = defaultValueDateVisibility = defaultValueYesnoVisibility = false; - <?php elseif ($one == '_scope') :?> + <?php elseif ($one == '_scope'): ?> scopeVisibility = false; - <?php else :?> + <?php else: ?> setRowVisibility('<?= $block->escapeJs($one) ?>', false); <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 7aa5bca00f0b6..ae4ae7ee707e3 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -5,14 +5,21 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\Entity\Attribute\FrontendLabel; +use Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class AttributeTest. + * Test for EAV Entity attribute model */ -class AttributeTest extends \PHPUnit\Framework\TestCase +class AttributeTest extends TestCase { /** * Attribute model to be tested - * @var \Magento\Eav\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ protected $_model; @@ -21,7 +28,7 @@ class AttributeTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->_model = $this->createPartialMock(\Magento\Eav\Model\Entity\Attribute::class, ['__wakeup']); + $this->_model = $this->createPartialMock(Attribute::class, ['__wakeup']); } /** @@ -132,7 +139,7 @@ public function testGetFrontendLabels() { $attributeId = 1; $storeLabels = ['test_attribute_store1']; - $frontendLabelFactory = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory::class) + $frontendLabelFactory = $this->getMockBuilder(FrontendLabelFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -144,15 +151,15 @@ public function testGetFrontendLabels() '_resource' => $resource, 'frontendLabelFactory' => $frontendLabelFactory, ]; - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $objectManager->getObject(\Magento\Eav\Model\Entity\Attribute::class, $arguments); + $objectManager = new ObjectManager($this); + $this->_model = $objectManager->getObject(Attribute::class, $arguments); $this->_model->setAttributeId($attributeId); $resource->expects($this->once()) ->method('getStoreLabelsByAttributeId') ->with($attributeId) ->willReturn($storeLabels); - $frontendLabel = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabel::class) + $frontendLabel = $this->getMockBuilder(FrontendLabel::class) ->setMethods(['setStoreId', 'setLabel']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 6bf1eacd161cc..ef2df77e7daff 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -11,7 +11,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** - * Class Date + * UI component date type */ class Date extends AbstractDataType { diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 31d7ca92c5985..20af2627fbb04 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class DateTest + * Test for Date grid filter functionality */ class DateTest extends \PHPUnit\Framework\TestCase { From 271a2862d39da787ed82175e15334deb00688ba4 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 10 Dec 2019 11:40:45 +0200 Subject: [PATCH 2341/2437] MC-29273: [Magento Cloud] - When admin URL is different to front-base URL admin redirects to storefront --- .../Adminhtml/System/Currencysymbol/Save.php | 51 ++++++-- .../System/Currencysymbol/SaveTest.php | 116 +++++++++--------- 2 files changed, 101 insertions(+), 66 deletions(-) diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php index f77976cc9e2f2..07c7c553ac792 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php @@ -5,37 +5,68 @@ */ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Backend\App\Action; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol as CurrencysymbolController; +use Magento\CurrencySymbol\Model\System\CurrencysymbolFactory; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Filter\FilterManager; /** - * Class Save + * Controller to save currency symbol */ -class Save extends \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol implements HttpPostActionInterface +class Save extends CurrencysymbolController implements HttpPostActionInterface { + /** + * @var FilterManager + */ + private $filterManager; + + /** + * @var CurrencysymbolFactory + */ + private $currencySymbolFactory; + + /** + * @param Action\Context $context + * @param FilterManager $filterManager + * @param CurrencysymbolFactory $currencySymbolFactory + */ + public function __construct( + Action\Context $context, + FilterManager $filterManager, + CurrencysymbolFactory $currencySymbolFactory + ) { + parent::__construct($context); + $this->filterManager = $filterManager; + $this->currencySymbolFactory = $currencySymbolFactory; + } + /** * Save custom Currency symbol * - * @return void + * @return ResultInterface */ public function execute() { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); $symbolsDataArray = $this->getRequest()->getParam('custom_currency_symbol', null); if (is_array($symbolsDataArray)) { foreach ($symbolsDataArray as &$symbolsData) { - /** @var $filterManager \Magento\Framework\Filter\FilterManager */ - $filterManager = $this->_objectManager->get(\Magento\Framework\Filter\FilterManager::class); - $symbolsData = $filterManager->stripTags($symbolsData); + $symbolsData = $this->filterManager->stripTags($symbolsData); } } try { - $this->_objectManager->create(\Magento\CurrencySymbol\Model\System\Currencysymbol::class) - ->setCurrencySymbolsData($symbolsDataArray); + $currencySymbol = $this->currencySymbolFactory->create(); + $currencySymbol->setCurrencySymbolsData($symbolsDataArray); $this->messageManager->addSuccessMessage(__('You applied the custom currency symbols.')); } catch (\Exception $e) { $this->messageManager->addErrorMessage($e->getMessage()); } - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); + return $resultRedirect->setPath('*'); } } diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index 06f4294ce6397..b3c69c352ac7d 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -5,132 +5,136 @@ */ namespace Magento\CurrencySymbol\Test\Unit\Controller\Adminhtml\System\Currencysymbol; +use Magento\Backend\Helper\Data; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save; +use Magento\CurrencySymbol\Model\System\Currencysymbol; +use Magento\CurrencySymbol\Model\System\CurrencysymbolFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class SaveTest + * Test ot to save currency symbol controller */ -class SaveTest extends \PHPUnit\Framework\TestCase +class SaveTest extends TestCase { /** - * @var \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save + * @var Save */ protected $action; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactory; + + /** + * @var RequestInterface|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ - protected $objectManagerMock; + protected $messageManagerMock; /** - * @var \Magento\CurrencySymbol\Model\System\Currencysymbol|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectInterface|MockObject */ - protected $currencySymbolMock; + protected $redirectMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $messageManagerMock; + protected $helperMock; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var FilterManager|MockObject */ - protected $redirectMock; + private $filterManager; /** - * @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencysymbolFactory|MockObject */ - protected $helperMock; + private $currencySymbolFactory; /** - * @var \Magento\Framework\Filter\FilterManager|\PHPUnit_Framework_MockObject_MockObject + * @inheritdoc */ - protected $filterManagerMock; - protected function setUp() { $objectManager = new ObjectManager($this); - - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - - $this->helperMock = $this->createMock(\Magento\Backend\Helper\Data::class); - - $this->redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - + $this->requestMock = $this->createMock(RequestInterface::class); + $this->helperMock = $this->createMock(Data::class); + $this->redirectMock = $this->createMock(RedirectInterface::class); $this->responseMock = $this->createPartialMock( - \Magento\Framework\App\ResponseInterface::class, + ResponseInterface::class, ['setRedirect', 'sendResponse'] ); - - $this->currencySymbolMock = $this->createMock(\Magento\CurrencySymbol\Model\System\Currencysymbol::class); - - $this->filterManagerMock = $this->createPartialMock( - \Magento\Framework\Filter\FilterManager::class, + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->filterManager = $this->createPartialMock( + FilterManager::class, ['stripTags'] ); + $this->currencySymbolFactory = $this->createMock(CurrencysymbolFactory::class); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); $this->action = $objectManager->getObject( - \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save::class, + Save::class, [ 'request' => $this->requestMock, 'response' => $this->responseMock, - 'objectManager' => $this->objectManagerMock, 'redirect' => $this->redirectMock, 'helper' => $this->helperMock, - 'messageManager' => $this->messageManagerMock + 'messageManager' => $this->messageManagerMock, + 'resultRedirectFactory' => $this->resultRedirectFactory, + 'filterManager' => $this->filterManager, + 'currencySymbolFactory' => $this->currencySymbolFactory, ] ); } + /** + * Test to Save custom Currency symbol + */ public function testExecute() { $firstElement = 'firstElement'; $symbolsDataArray = [$firstElement]; - $redirectUrl = 'redirectUrl'; $this->requestMock->expects($this->once()) ->method('getParam') ->with('custom_currency_symbol') ->willReturn($symbolsDataArray); - $this->helperMock->expects($this->once())->method('getUrl')->with('*'); - $this->redirectMock->expects($this->once())->method('getRedirectUrl')->willReturn($redirectUrl); - - $this->currencySymbolMock->expects($this->once())->method('setCurrencySymbolsData')->with($symbolsDataArray); - $this->responseMock->expects($this->once())->method('setRedirect'); - - $this->filterManagerMock->expects($this->once()) + $currencySymbol = $this->createMock(Currencysymbol::class); + $currencySymbol->expects($this->once())->method('setCurrencySymbolsData')->with($symbolsDataArray); + $this->currencySymbolFactory->method('create')->willReturn($currencySymbol); + $this->filterManager->expects($this->once()) ->method('stripTags') ->with($firstElement) ->willReturn($firstElement); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\CurrencySymbol\Model\System\Currencysymbol::class) - ->willReturn($this->currencySymbolMock); - - $this->objectManagerMock->expects($this->once()) - ->method('get') - ->with(\Magento\Framework\Filter\FilterManager::class) - ->willReturn($this->filterManagerMock); - $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') ->with(__('You applied the custom currency symbols.')); - $this->action->execute(); + $redirect = $this->createMock(Redirect::class); + $redirect->expects($this->once())->method('setPath')->with('*')->willReturnSelf(); + $this->resultRedirectFactory->method('create')->willReturn($redirect); + + $this->assertEquals($redirect, $this->action->execute()); } } From 5ae32934cda53256c35eabcce27d92cb516a7411 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Tue, 10 Dec 2019 13:30:03 +0200 Subject: [PATCH 2342/2437] MC-29362: Sitemap grid does not use base URL --- app/code/Magento/Sitemap/Model/Sitemap.php | 43 ++++++++- .../Sitemap/Test/Unit/Model/SitemapTest.php | 95 +++++++++++++++---- 2 files changed, 116 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index 2baa6ff2c71a7..ea5659cf909ff 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -739,8 +739,11 @@ protected function _getFormattedLastmodDate($date) */ protected function _getDocumentRoot() { - return $this->filesystem->getDirectoryRead($this->documentRoot->getPath()) - ->getAbsolutePath(); + if (PHP_SAPI === 'cli') { + return $this->getDocumentRootFromBaseDir() ?? ''; + } + // phpcs:ignore Magento2.Functions.DiscouragedFunction + return realpath($this->_request->getServer('DOCUMENT_ROOT')); } /** @@ -754,10 +757,14 @@ protected function _getStoreBaseDomain() $storeParsedUrl = parse_url($this->_getStoreBaseUrl()); $url = $storeParsedUrl['scheme'] . '://' . $storeParsedUrl['host']; - $documentRoot = trim(str_replace('\\', '/', $this->_getDocumentRoot()), '/'); - $baseDir = trim(str_replace('\\', '/', $this->_getBaseDir()), '/'); + // Set document root to false if we were unable to get it + $documentRoot = $this->_getDocumentRoot() ?: false; + if ($documentRoot) { + $documentRoot = trim(str_replace(DIRECTORY_SEPARATOR, '/', $documentRoot), '/'); + } + $baseDir = trim(str_replace(DIRECTORY_SEPARATOR, '/', $this->_getBaseDir()), '/'); - if (strpos($baseDir, (string) $documentRoot) === 0) { + if ($documentRoot !== false && strpos($baseDir, (string) $documentRoot) === 0) { //case when basedir is in document root $installationFolder = trim(str_replace($documentRoot, '', $baseDir), '/'); $storeDomain = rtrim($url . '/' . $installationFolder, '/'); @@ -878,4 +885,30 @@ public function getIdentities() Value::CACHE_TAG . '_' . $this->getStoreId(), ]; } + + /** + * Get document root using base directory (root directory) and base path (base url path) + * + * Document root is determined using formula: BaseDir = DocumentRoot + BasePath. + * Returns <b>NULL</b> if BaseDir does not end with BasePath (e.g document root contains a symlink to BaseDir). + * + * @return string|null + */ + private function getDocumentRootFromBaseDir(): ?string + { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $basePath = rtrim(parse_url($this->_getStoreBaseUrl(UrlInterface::URL_TYPE_WEB), PHP_URL_PATH) ?: '', '/'); + $basePath = str_replace('/', DIRECTORY_SEPARATOR, $basePath); + $basePath = rtrim($basePath, DIRECTORY_SEPARATOR); + $baseDir = rtrim($this->_getBaseDir(), DIRECTORY_SEPARATOR); + $length = strlen($basePath); + if (!$length) { + $documentRoot = $baseDir; + } elseif (substr($baseDir, -$length) === $basePath) { + $documentRoot = rtrim(substr($baseDir, 0, strlen($baseDir) - $length), DIRECTORY_SEPARATOR); + } else { + $documentRoot = null; + } + return $documentRoot; + } } diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php index a5e40bf3fcff7..16d506c1cdfa3 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Sitemap\Test\Unit\Model; +use Magento\Framework\App\Request\Http; use Magento\Framework\DataObject; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Write as DirectoryWrite; @@ -86,6 +87,15 @@ class SitemapTest extends \PHPUnit\Framework\TestCase */ private $configReaderMock; + /** + * @var Http|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + /** + * @var Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $store; + /** * @inheritdoc */ @@ -143,6 +153,12 @@ protected function setUp() $this->configReaderMock = $this->getMockForAbstractClass(SitemapConfigReaderInterface::class); $this->itemProviderMock = $this->getMockForAbstractClass(ItemProviderInterface::class); + $this->request = $this->createMock(Http::class); + $this->store = $this->createPartialMock(Store::class, ['isFrontUrlSecure', 'getBaseUrl']); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); } /** @@ -476,25 +492,15 @@ function ($from, $to) { $model = $this->getModelMock(true); - $storeMock = $this->getMockBuilder(Store::class) - ->setMethods(['isFrontUrlSecure', 'getBaseUrl']) - ->disableOriginalConstructor() - ->getMock(); - - $storeMock->expects($this->atLeastOnce()) + $this->store->expects($this->atLeastOnce()) ->method('isFrontUrlSecure') ->willReturn(false); - $storeMock->expects($this->atLeastOnce()) + $this->store->expects($this->atLeastOnce()) ->method('getBaseUrl') ->with($this->isType('string'), false) ->willReturn('http://store.com/'); - $this->storeManagerMock->expects($this->atLeastOnce()) - ->method('getStore') - ->with(1) - ->willReturn($storeMock); - return $model; } @@ -599,10 +605,6 @@ private function getModelConstructorArgs() ->disableOriginalConstructor() ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) - ->setMethods(['getStore']) - ->getMockForAbstractClass(); - $objectManager = new ObjectManager($this); $escaper = $objectManager->getObject(\Magento\Framework\Escaper::class); @@ -617,7 +619,8 @@ private function getModelConstructorArgs() 'filesystem' => $this->filesystemMock, 'itemProvider' => $this->itemProviderMock, 'configReader' => $this->configReaderMock, - 'escaper' => $escaper + 'escaper' => $escaper, + 'request' => $this->request, ] ); $constructArguments['resource'] = null; @@ -732,4 +735,62 @@ public static function siteUrlDataProvider() ] ]; } + + /** + * Check site URL getter + * + * @param string $storeBaseUrl + * @param string $baseDir + * @param string $documentRoot + * @dataProvider getDocumentRootFromBaseDirUrlDataProvider + */ + public function testGetDocumentRootFromBaseDir( + string $storeBaseUrl, + string $baseDir, + ?string $documentRoot + ) { + $this->store->setCode('store'); + $this->store->method('getBaseUrl')->willReturn($storeBaseUrl); + $this->directoryMock->method('getAbsolutePath')->willReturn($baseDir); + /** @var $model Sitemap */ + $model = $this->getMockBuilder(Sitemap::class) + ->setMethods(['_construct']) + ->setConstructorArgs($this->getModelConstructorArgs()) + ->getMock(); + + $method = new \ReflectionMethod($model, 'getDocumentRootFromBaseDir'); + $method->setAccessible(true); + $this->assertSame($documentRoot, $method->invoke($model)); + } + + /** + * Provides test cases for document root testing + * + * @return array + */ + public function getDocumentRootFromBaseDirUrlDataProvider(): array + { + return [ + [ + 'http://magento.com/', + '/var/www', + '/var/www', + ], + [ + 'http://magento.com/usa', + '/var/www/usa', + '/var/www', + ], + [ + 'http://magento.com/usa/tx', + '/var/www/usa/tx', + '/var/www', + ], + 'symlink <document root>/usa/txt -> /var/www/html' => [ + 'http://magento.com/usa/tx', + '/var/www/html', + null, + ], + ]; + } } From 200f51853bf8b5b10090d6ecd9abc46269ca364b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chitesh=40wagento=2Ecom=E2=80=9D?= <hitesh@wagento.com> Date: Tue, 10 Dec 2019 17:05:01 +0530 Subject: [PATCH 2343/2437] [Removed spacing in submenu on hover desktop] --- lib/web/css/source/lib/_navigation.less | 2 +- lib/web/css/source/lib/variables/_navigation.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/web/css/source/lib/_navigation.less b/lib/web/css/source/lib/_navigation.less index 9b13c28227eb9..38cd042591722 100644 --- a/lib/web/css/source/lib/_navigation.less +++ b/lib/web/css/source/lib/_navigation.less @@ -459,7 +459,7 @@ } .submenu { - top: 0 !important; + top: -1px !important; left: 100% !important; } diff --git a/lib/web/css/source/lib/variables/_navigation.less b/lib/web/css/source/lib/variables/_navigation.less index 3ef1742547426..4801b8874abcc 100644 --- a/lib/web/css/source/lib/variables/_navigation.less +++ b/lib/web/css/source/lib/variables/_navigation.less @@ -91,7 +91,7 @@ @submenu-desktop__font-size: ''; @submenu-desktop__font-weight: @font-weight__bold; @submenu-desktop__min-width: 230px; -@submenu-desktop__padding: 15px 0; +@submenu-desktop__padding: 0; @submenu-desktop-arrow: true; // [true|false] @submenu-desktop-arrow__size: 10px; From 04f6ac08242db072a832350faf17cc2f90ad7aee Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Tue, 10 Dec 2019 13:42:52 +0200 Subject: [PATCH 2344/2437] MC-29388: Performance issue. The same item from change log table can be processed several times --- .../Framework/Mview/Test/Unit/ViewTest.php | 256 ++++++++++++++---- lib/internal/Magento/Framework/Mview/View.php | 35 ++- 2 files changed, 235 insertions(+), 56 deletions(-) diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php index 54f6351e9651c..ebd9896e62558 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php @@ -5,44 +5,60 @@ */ namespace Magento\Framework\Mview\Test\Unit; +use Magento\Framework\Mview\ActionFactory; +use Magento\Framework\Mview\ActionInterface; +use Magento\Framework\Mview\ConfigInterface; use \Magento\Framework\Mview\View; +use Magento\Framework\Mview\View\Changelog; +use Magento\Framework\Mview\View\StateInterface; +use Magento\Framework\Mview\View\Subscription; +use Magento\Framework\Mview\View\SubscriptionFactory; +use Magento\Indexer\Model\Mview\View\State; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; -class ViewTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Mview functionality + */ +class ViewTest extends TestCase { /** - * @var \Magento\Framework\Mview\View + * @var View */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Mview\ConfigInterface + * @var MockObject|ConfigInterface */ protected $configMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Mview\ActionFactory + * @var MockObject|ActionFactory */ protected $actionFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Indexer\Model\Mview\View\State + * @var MockObject|State */ protected $stateMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Mview\View\Changelog + * @var MockObject|Changelog */ protected $changelogMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Mview\View\SubscriptionFactory + * @var MockObject|SubscriptionFactory */ protected $subscriptionFactoryMock; + /** + * @inheritdoc + */ protected function setUp() { $this->configMock = $this->getMockForAbstractClass( - \Magento\Framework\Mview\ConfigInterface::class, + ConfigInterface::class, [], '', false, @@ -50,9 +66,9 @@ protected function setUp() true, ['getView'] ); - $this->actionFactoryMock = $this->createPartialMock(\Magento\Framework\Mview\ActionFactory::class, ['get']); + $this->actionFactoryMock = $this->createPartialMock(ActionFactory::class, ['get']); $this->stateMock = $this->createPartialMock( - \Magento\Indexer\Model\Mview\View\State::class, + State::class, [ 'getViewId', 'loadByView', @@ -68,11 +84,11 @@ protected function setUp() ] ); $this->changelogMock = $this->createPartialMock( - \Magento\Framework\Mview\View\Changelog::class, + Changelog::class, ['getViewId', 'setViewId', 'create', 'drop', 'getVersion', 'getList', 'clear'] ); $this->subscriptionFactoryMock = $this->createPartialMock( - \Magento\Framework\Mview\View\SubscriptionFactory::class, + SubscriptionFactory::class, ['create'] ); $this->model = new View( @@ -84,24 +100,36 @@ protected function setUp() ); } + /** + * Test to Return view action class + */ public function testGetActionClass() { $this->model->setData('action_class', 'actionClass'); $this->assertEquals('actionClass', $this->model->getActionClass()); } + /** + * Test to Return view group + */ public function testGetGroup() { $this->model->setData('group', 'some_group'); $this->assertEquals('some_group', $this->model->getGroup()); } + /** + * Test to Return view subscriptions + */ public function testGetSubscriptions() { $this->model->setData('subscriptions', ['subscription']); $this->assertEquals(['subscription'], $this->model->getSubscriptions()); } + /** + * Test to Fill view data from config + */ public function testLoad() { $viewId = 'view_test'; @@ -114,10 +142,12 @@ public function testLoad() )->will( $this->returnValue($this->getViewData()) ); - $this->assertInstanceOf(\Magento\Framework\Mview\View::class, $this->model->load($viewId)); + $this->assertInstanceOf(View::class, $this->model->load($viewId)); } /** + * Test to Fill view data from config + * * @expectedException \InvalidArgumentException * @expectedExceptionMessage view_id view does not exist. */ @@ -136,18 +166,21 @@ public function testLoadWithException() $this->model->load($viewId); } + /** + * Test to Create subscriptions + */ public function testSubscribe() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED)); + ->will($this->returnValue(StateInterface::MODE_DISABLED)); $this->stateMock->expects($this->once()) ->method('setMode') - ->with(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED) + ->with(StateInterface::MODE_ENABLED) ->will($this->returnSelf()); $this->changelogMock->expects($this->once()) ->method('create'); - $subscriptionMock = $this->createPartialMock(\Magento\Framework\Mview\View\Subscription::class, ['create']); + $subscriptionMock = $this->createPartialMock(Subscription::class, ['create']); $subscriptionMock->expects($this->exactly(1))->method('create'); $this->subscriptionFactoryMock->expects( $this->exactly(1) @@ -160,11 +193,14 @@ public function testSubscribe() $this->model->subscribe(); } + /** + * Test to Create subscriptions + */ public function testSubscribeEnabled() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED)); + ->will($this->returnValue(StateInterface::MODE_ENABLED)); $this->stateMock->expects($this->never()) ->method('setMode'); $this->changelogMock->expects($this->never()) @@ -182,7 +218,7 @@ public function testSubscribeWithException() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED)); + ->will($this->returnValue(StateInterface::MODE_DISABLED)); $this->changelogMock->expects($this->once()) ->method('create') @@ -196,18 +232,21 @@ function () { $this->model->subscribe(); } + /** + * Test to Remove subscriptions + */ public function testUnsubscribe() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED)); + ->will($this->returnValue(StateInterface::MODE_ENABLED)); $this->stateMock->expects($this->once()) ->method('setMode') - ->with(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED) + ->with(StateInterface::MODE_DISABLED) ->will($this->returnSelf()); $this->changelogMock->expects($this->never()) ->method('drop'); - $subscriptionMock = $this->createPartialMock(\Magento\Framework\Mview\View\Subscription::class, ['remove']); + $subscriptionMock = $this->createPartialMock(Subscription::class, ['remove']); $subscriptionMock->expects($this->exactly(1))->method('remove'); $this->subscriptionFactoryMock->expects( $this->exactly(1) @@ -220,11 +259,14 @@ public function testUnsubscribe() $this->model->unsubscribe(); } + /** + * Test to Remove subscriptions + */ public function testUnsubscribeDisabled() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED)); + ->will($this->returnValue(StateInterface::MODE_DISABLED)); $this->stateMock->expects($this->never()) ->method('setVersionId'); $this->stateMock->expects($this->never()) @@ -244,9 +286,9 @@ public function testUnsubscribeWithException() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED)); + ->will($this->returnValue(StateInterface::MODE_ENABLED)); - $subscriptionMock = $this->createPartialMock(\Magento\Framework\Mview\View\Subscription::class, ['remove']); + $subscriptionMock = $this->createPartialMock(Subscription::class, ['remove']); $subscriptionMock->expects($this->exactly(1)) ->method('remove') ->willReturnCallback( @@ -262,6 +304,9 @@ function () { $this->model->unsubscribe(); } + /** + * Test to Materialize view by IDs in changelog + */ public function testUpdate() { $currentVersionId = 3; @@ -279,10 +324,10 @@ public function testUpdate() ->will($this->returnSelf()); $this->stateMock->expects($this->atLeastOnce()) ->method('getMode') - ->willReturn(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED); + ->willReturn(StateInterface::MODE_ENABLED); $this->stateMock->expects($this->exactly(2)) ->method('getStatus') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE)); + ->will($this->returnValue(StateInterface::STATUS_IDLE)); $this->stateMock->expects($this->exactly(2)) ->method('setStatus') ->will($this->returnSelf()); @@ -308,7 +353,7 @@ public function testUpdate() $this->returnValue($listId) ); - $actionMock = $this->createMock(\Magento\Framework\Mview\ActionInterface::class); + $actionMock = $this->createMock(ActionInterface::class); $actionMock->expects($this->once())->method('execute')->with($listId)->will($this->returnSelf()); $this->actionFactoryMock->expects( $this->once() @@ -325,6 +370,83 @@ public function testUpdate() } /** + * Test to Materialize view by IDs in changelog + */ + public function testUpdateEx(): void + { + $currentVersionId = 200100; + $lastVersionId = 1; + $listIdBatchOne = $this->generateChangeLog(100000, 1, 100); + $listIdBatchTwo = $this->generateChangeLog(100000, 1, 50); + $listIdBatchThree = $this->generateChangeLog(100, 100, 150); + + $this->stateMock->method('getViewId')->willReturn(1); + $this->stateMock->method('getVersionId')->willReturn($lastVersionId); + $this->stateMock->method('setVersionId')->willReturnSelf(); + $this->stateMock->expects($this->atLeastOnce()) + ->method('getMode') + ->willReturn(StateInterface::MODE_ENABLED); + $this->stateMock->expects($this->exactly(2)) + ->method('getStatus') + ->willReturn(StateInterface::STATUS_IDLE); + $this->stateMock->expects($this->exactly(2)) + ->method('setStatus') + ->willReturnSelf(); + $this->stateMock->expects($this->exactly(2)) + ->method('save') + ->willReturnSelf(); + $this->changelogMock + ->expects($this->once()) + ->method('getVersion') + ->willReturn($currentVersionId); + + $this->changelogMock->method('getList') + ->willReturnMap( + [ + [$lastVersionId, 100001, $listIdBatchOne], + [100001, 200001, $listIdBatchTwo], + [200001, $currentVersionId, $listIdBatchThree], + ] + ); + + $actionMock = $this->createMock(ActionInterface::class); + $actionMock->expects($this->once()) + ->method('execute') + ->with($this->generateChangeLog(150, 1, 150)) + ->willReturnSelf(); + $this->actionFactoryMock->method('get')->willReturn($actionMock); + $this->loadView(); + $this->model->update(); + } + + /** + * Generate change log + * + * @param int $count + * @param int $idFrom + * @param int $idTo + * @return array + */ + private function generateChangeLog(int $count, int $idFrom, int $idTo): array + { + $res = []; + $i = 0; + $id = $idFrom; + while ($i < $count) { + if ($id > $idTo) { + $id = $idFrom; + } + $res[] = $id; + $id++; + $i++; + } + + return $res; + } + + /** + * Test to Materialize view by IDs in changelog + * * @expectedException \Exception * @expectedExceptionMessage Test exception */ @@ -344,10 +466,10 @@ public function testUpdateWithException() ->method('setVersionId'); $this->stateMock->expects($this->atLeastOnce()) ->method('getMode') - ->willReturn(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED); + ->willReturn(StateInterface::MODE_ENABLED); $this->stateMock->expects($this->exactly(2)) ->method('getStatus') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE)); + ->will($this->returnValue(StateInterface::STATUS_IDLE)); $this->stateMock->expects($this->exactly(2)) ->method('setStatus') ->will($this->returnSelf()); @@ -373,7 +495,7 @@ public function testUpdateWithException() $this->returnValue($listId) ); - $actionMock = $this->createPartialMock(\Magento\Framework\Mview\ActionInterface::class, ['execute']); + $actionMock = $this->createPartialMock(ActionInterface::class, ['execute']); $actionMock->expects($this->once())->method('execute')->with($listId)->will( $this->returnCallback( function () { @@ -395,18 +517,21 @@ function () { $this->model->update(); } + /** + * Test to Suspend view updates and set version ID to changelog's end + */ public function testSuspend() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED)); + ->will($this->returnValue(StateInterface::MODE_ENABLED)); $this->stateMock->expects($this->once()) ->method('setVersionId') ->with(11) ->will($this->returnSelf()); $this->stateMock->expects($this->once()) ->method('setStatus') - ->with(\Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED) + ->with(StateInterface::STATUS_SUSPENDED) ->will($this->returnSelf()); $this->stateMock->expects($this->once()) ->method('save') @@ -420,11 +545,14 @@ public function testSuspend() $this->model->suspend(); } + /** + * Suspend view updates and set version ID to changelog's end + */ public function testSuspendDisabled() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED)); + ->will($this->returnValue(StateInterface::MODE_DISABLED)); $this->stateMock->expects($this->never()) ->method('setVersionId'); $this->stateMock->expects($this->never()) @@ -439,14 +567,17 @@ public function testSuspendDisabled() $this->model->suspend(); } + /** + * Test to Resume view updates + */ public function testResume() { $this->stateMock->expects($this->once()) ->method('getStatus') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED)); + ->will($this->returnValue(StateInterface::STATUS_SUSPENDED)); $this->stateMock->expects($this->once()) ->method('setStatus') - ->with(\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE) + ->with(StateInterface::STATUS_IDLE) ->will($this->returnSelf()); $this->stateMock->expects($this->once()) ->method('save') @@ -457,6 +588,8 @@ public function testResume() } /** + * Test to Resume view updates + * * @param string $status * @dataProvider dataProviderResumeNotSuspended */ @@ -480,16 +613,19 @@ public function testResumeNotSuspended($status) public function dataProviderResumeNotSuspended() { return [ - [\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE], - [\Magento\Framework\Mview\View\StateInterface::STATUS_WORKING], + [StateInterface::STATUS_IDLE], + [StateInterface::STATUS_WORKING], ]; } + /** + * Test to Clear precessed changelog entries + */ public function testClearChangelog() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED)); + ->will($this->returnValue(StateInterface::MODE_ENABLED)); $this->stateMock->expects($this->once()) ->method('getVersionId') ->will($this->returnValue(11)); @@ -501,11 +637,14 @@ public function testClearChangelog() $this->model->clearChangelog(); } + /** + * Test to Clear precessed changelog entries + */ public function testClearChangelogDisabled() { $this->stateMock->expects($this->once()) ->method('getMode') - ->will($this->returnValue(\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED)); + ->will($this->returnValue(StateInterface::MODE_DISABLED)); $this->stateMock->expects($this->never()) ->method('getVersionId'); $this->changelogMock->expects($this->never()) @@ -514,6 +653,9 @@ public function testClearChangelogDisabled() $this->model->clearChangelog(); } + /** + * Test to Return related state object + */ public function testSetState() { $this->model->setState($this->stateMock); @@ -521,6 +663,8 @@ public function testSetState() } /** + * Test to Check whether view is enabled + * * @param string $mode * @param bool $result * @dataProvider dataProviderIsEnabled @@ -539,12 +683,14 @@ public function testIsEnabled($mode, $result) public function dataProviderIsEnabled() { return [ - [\Magento\Framework\Mview\View\StateInterface::MODE_ENABLED, true], - [\Magento\Framework\Mview\View\StateInterface::MODE_DISABLED, false], + [StateInterface::MODE_ENABLED, true], + [StateInterface::MODE_DISABLED, false], ]; } /** + * Test to Check whether view is idle + * * @param string $status * @param bool $result * @dataProvider dataProviderIsIdle @@ -563,13 +709,15 @@ public function testIsIdle($status, $result) public function dataProviderIsIdle() { return [ - [\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE, true], - [\Magento\Framework\Mview\View\StateInterface::STATUS_WORKING, false], - [\Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED, false], + [StateInterface::STATUS_IDLE, true], + [StateInterface::STATUS_WORKING, false], + [StateInterface::STATUS_SUSPENDED, false], ]; } /** + * Test to Check whether view is working + * * @param string $status * @param bool $result * @dataProvider dataProviderIsWorking @@ -588,13 +736,15 @@ public function testIsWorking($status, $result) public function dataProviderIsWorking() { return [ - [\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE, false], - [\Magento\Framework\Mview\View\StateInterface::STATUS_WORKING, true], - [\Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED, false], + [StateInterface::STATUS_IDLE, false], + [StateInterface::STATUS_WORKING, true], + [StateInterface::STATUS_SUSPENDED, false], ]; } /** + * Test to Check whether view is suspended + * * @param string $status * @param bool $result * @dataProvider dataProviderIsSuspended @@ -613,12 +763,15 @@ public function testIsSuspended($status, $result) public function dataProviderIsSuspended() { return [ - [\Magento\Framework\Mview\View\StateInterface::STATUS_IDLE, false], - [\Magento\Framework\Mview\View\StateInterface::STATUS_WORKING, false], - [\Magento\Framework\Mview\View\StateInterface::STATUS_SUSPENDED, true], + [StateInterface::STATUS_IDLE, false], + [StateInterface::STATUS_WORKING, false], + [StateInterface::STATUS_SUSPENDED, true], ]; } + /** + * Test to Return view updated datetime + */ public function testGetUpdated() { $this->stateMock->expects($this->once()) @@ -627,6 +780,9 @@ public function testGetUpdated() $this->assertEquals('some datetime', $this->model->getUpdated()); } + /** + * Fill view data from config + */ protected function loadView() { $viewId = 'view_test'; diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php index b2372eaaafaad..dade475a20482 100644 --- a/lib/internal/Magento/Framework/Mview/View.php +++ b/lib/internal/Magento/Framework/Mview/View.php @@ -291,16 +291,13 @@ public function update() */ private function executeAction(ActionInterface $action, int $lastVersionId, int $currentVersionId) { - $versionBatchSize = self::$maxVersionQueryBatch; $batchSize = isset($this->changelogBatchSize[$this->getChangelog()->getViewId()]) ? (int) $this->changelogBatchSize[$this->getChangelog()->getViewId()] : self::DEFAULT_BATCH_SIZE; - for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) { - // Don't go past the current version for atomicity. - $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize); - $ids = $this->getChangelog()->getList($vsFrom, $versionTo); - + $vsFrom = $lastVersionId; + while ($vsFrom < $currentVersionId) { + $ids = $this->getBatchOfIds($vsFrom, $currentVersionId); // We run the actual indexer in batches. // Chunked AFTER loading to avoid duplicates in separate chunks. $chunks = array_chunk($ids, $batchSize); @@ -310,6 +307,32 @@ private function executeAction(ActionInterface $action, int $lastVersionId, int } } + /** + * Get batch of entity ids + * + * @param int $lastVersionId + * @param int $currentVersionId + * @return array + */ + private function getBatchOfIds(int &$lastVersionId, int $currentVersionId): array + { + $ids = []; + $versionBatchSize = self::$maxVersionQueryBatch; + $idsBatchSize = self::$maxVersionQueryBatch; + for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) { + // Don't go past the current version for atomicity. + $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize); + /** To avoid duplicate ids need to flip and merge the array */ + $ids += array_flip($this->getChangelog()->getList($vsFrom, $versionTo)); + $lastVersionId = $versionTo; + if (count($ids) >= $idsBatchSize) { + break; + } + } + + return array_keys($ids); + } + /** * Suspend view updates and set version ID to changelog's end * From 96f502e107bdb6f127207060055020b9d0a9c3ad Mon Sep 17 00:00:00 2001 From: maslii <maslii@users.noreply.github.com> Date: Tue, 10 Dec 2019 14:48:43 +0200 Subject: [PATCH 2345/2437] phpdoc fix return type --- app/code/Magento/Customer/Controller/Account/CreatePost.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index a006cfe6725f3..e2a7c085a0b44 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -327,7 +327,7 @@ public function validateForCsrf(RequestInterface $request): ?bool /** * Create customer account action * - * @return void + * @return \Magento\Framework\Controller\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ From ab4fa339c24d0868fa7471d347ac98ad22f4d3fe Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 10 Dec 2019 20:28:28 +0700 Subject: [PATCH 2346/2437] Refactor and unit test for issue 25893 --- .../Adminhtml/Wysiwyg/Directive.php | 76 ++++++++++++++----- .../Adminhtml/Wysiwyg/DirectiveTest.php | 25 +++--- 2 files changed, 67 insertions(+), 34 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php index db53c6a415ddd..97d0b35a2354f 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php @@ -13,6 +13,14 @@ use Magento\Cms\Model\Template\Filter; use Magento\Cms\Model\Wysiwyg\Config; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Image\Adapter\AdapterInterface; +use Magento\Framework\Image\AdapterFactory; +use Psr\Log\LoggerInterface; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\ObjectManager; /** * Process template text for wysiwyg editor. @@ -30,34 +38,68 @@ class Directive extends Action implements HttpGetActionInterface const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** - * @var \Magento\Framework\Url\DecoderInterface + * @var DecoderInterface */ protected $urlDecoder; /** - * @var \Magento\Framework\Controller\Result\RawFactory + * @var RawFactory */ protected $resultRawFactory; /** - * @param Action\Context $context - * @param \Magento\Framework\Url\DecoderInterface $urlDecoder - * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory + * @var LoggerInterface + */ + private $logger; + + /** + * @var AdapterFactory + */ + private $adapterFactory; + + /** + * @var Config + */ + private $config; + + /** + * @var Filter + */ + private $filter; + + /** + * Constructor + * + * @param Context $context + * @param DecoderInterface $urlDecoder + * @param RawFactory $resultRawFactory + * @param AdapterFactory|null $adapterFactory + * @param LoggerInterface|null $logger + * @param Config|null $config + * @param Filter|null $filter */ public function __construct( - Action\Context $context, - \Magento\Framework\Url\DecoderInterface $urlDecoder, - \Magento\Framework\Controller\Result\RawFactory $resultRawFactory + Context $context, + DecoderInterface $urlDecoder, + RawFactory $resultRawFactory, + AdapterFactory $adapterFactory = null, + LoggerInterface $logger = null, + Config $config = null, + Filter $filter = null ) { parent::__construct($context); $this->urlDecoder = $urlDecoder; $this->resultRawFactory = $resultRawFactory; + $this->adapterFactory = $adapterFactory ?: ObjectManager::getInstance()->get(AdapterFactory::class); + $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); + $this->config = $config ?: ObjectManager::getInstance()->get(Config::class); + $this->filter = $filter ?: ObjectManager::getInstance()->get(Filter::class); } /** * Template directives callback * - * @return \Magento\Framework\Controller\Result\Raw + * @return Raw */ public function execute() { @@ -65,26 +107,24 @@ public function execute() $directive = $this->urlDecoder->decode($directive); try { /** @var Filter $filter */ - $filter = $this->_objectManager->create(Filter::class); - $imagePath = $filter->filter($directive); - /** @var \Magento\Framework\Image\Adapter\AdapterInterface $image */ - $image = $this->_objectManager->get(\Magento\Framework\Image\AdapterFactory::class)->create(); - /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ + $imagePath = $this->filter->filter($directive); + /** @var AdapterInterface $image */ + $image = $this->adapterFactory->create(); + /** @var Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); $image->open($imagePath); $resultRaw->setHeader('Content-Type', $image->getMimeType()); $resultRaw->setContents($image->getImage()); } catch (\Exception $e) { /** @var Config $config */ - $config = $this->_objectManager->get(Config::class); - $imagePath = $config->getSkinImagePlaceholderPath(); + $imagePath = $this->config->getSkinImagePlaceholderPath(); try { $image->open($imagePath); $resultRaw->setHeader('Content-Type', $image->getMimeType()); $resultRaw->setContents($image->getImage()); - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + $this->logger->warning($e); } catch (\Exception $e) { - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + $this->logger->warning($e); } } return $resultRaw; diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php index cede3a80cb98b..5fea276225622 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php @@ -151,7 +151,11 @@ protected function setUp() [ 'context' => $this->actionContextMock, 'urlDecoder' => $this->urlDecoderMock, - 'resultRawFactory' => $this->rawFactoryMock + 'resultRawFactory' => $this->rawFactoryMock, + 'adapterFactory' => $this->imageAdapterFactoryMock, + 'logger' => $this->loggerMock, + 'config' => $this->wysiwygConfigMock, + 'filter' => $this->templateFilterMock ] ); } @@ -228,7 +232,7 @@ public function testExecuteException() ->method('getImage') ->willReturn($imageBody); $this->loggerMock->expects($this->once()) - ->method('critical') + ->method('warning') ->with($exception); $this->rawFactoryMock->expects($this->any()) ->method('create') @@ -253,23 +257,12 @@ protected function prepareExecuteTest() ->method('decode') ->with($directiveParam) ->willReturn($directive); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Cms\Model\Template\Filter::class) - ->willReturn($this->templateFilterMock); + $this->templateFilterMock->expects($this->once()) ->method('filter') ->with($directive) ->willReturn(self::IMAGE_PATH); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap( - [ - [\Magento\Framework\Image\AdapterFactory::class, $this->imageAdapterFactoryMock], - [\Magento\Cms\Model\Wysiwyg\Config::class, $this->wysiwygConfigMock], - [\Psr\Log\LoggerInterface::class, $this->loggerMock] - ] - ); + $this->imageAdapterFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->imageAdapterMock); @@ -301,7 +294,7 @@ public function testExecuteWithDeletedImage() ->willThrowException($exception); $this->loggerMock->expects($this->once()) - ->method('critical') + ->method('warning') ->with($exception); $this->wysiwygDirective->execute(); From e8eb43a84cb861cedda424aaf8be83aa1c6c1c29 Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Tue, 10 Dec 2019 16:08:13 +0200 Subject: [PATCH 2347/2437] MC-25036: Marketing Email preview doesn't work with enabled js minification --- app/code/Magento/Email/composer.json | 1 + .../view/adminhtml/layout/adminhtml_email_template_preview.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 548d9a422c132..9070fbac7c653 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -12,6 +12,7 @@ "magento/module-config": "*", "magento/module-store": "*", "magento/module-theme": "*", + "magento/module-require-js": "*", "magento/module-media-storage": "*", "magento/module-variable": "*" }, diff --git a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml index e7cbc675ce386..886a76b3af6f8 100644 --- a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml +++ b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml @@ -17,6 +17,7 @@ <argument name="preview_form_view_model" xsi:type="object">Magento\Email\ViewModel\Template\Preview\Form</argument> </arguments> </block> + <block class="Magento\RequireJs\Block\Html\Head\Config" name="requirejs-config"/> </referenceContainer> </body> </page> From 721f8cedc063e405074c0ceaea200f0c44f905eb Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 10 Dec 2019 21:53:06 +0700 Subject: [PATCH 2348/2437] Update .csv for issue 25896 --- app/code/Magento/Cms/i18n/en_US.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv index 2947c567d75ff..6a623f947cbc2 100644 --- a/app/code/Magento/Cms/i18n/en_US.csv +++ b/app/code/Magento/Cms/i18n/en_US.csv @@ -75,7 +75,7 @@ Pages,Pages "A block identifier with the same properties already exists in the selected store.","A block identifier with the same properties already exists in the selected store." "The page URL key contains capital letters or disallowed symbols.","The page URL key contains capital letters or disallowed symbols." "The page URL key cannot be made of only numbers.","The page URL key cannot be made of only numbers." -"Please rename the folder using only letters, numbers, underscores and dashes.","Please rename the folder using only letters, numbers, underscores and dashes." +"Please rename the folder using only Latin letters, numbers, underscores and dashes.","Please rename the folder using only Latin letters, numbers, underscores and dashes." "We found a directory with the same name. Please try another folder name.","We found a directory with the same name. Please try another folder name." "We cannot create a new directory.","We cannot create a new directory." "We cannot delete directory %1.","We cannot delete directory %1." From 37c85d61103eeecc5b6de303fe717bbd453949da Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 10 Dec 2019 09:55:10 -0600 Subject: [PATCH 2349/2437] MQE-1878: Stabilize community PR 742 --- ...ontMoveConfigurableProductFromShoppingCartToWishlistTest.xml | 2 +- ...ntMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml | 2 +- ...rontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml | 2 +- ...orefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml index 49cd78ec1884f..46ea3497941fd 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -14,7 +14,7 @@ <title value="Move Configurable Product from Shopping Cart to Wishlist"/> <description value="Move Configurable Product from Shopping Cart to Wishlist"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-29545"/> + <testCaseId value="MC-14211"/> <group value="wishlist"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml index 856ebdb7bb73a..6faf4c1002c1a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -14,7 +14,7 @@ <title value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> <description value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-29545"/> + <testCaseId value="MC-14212"/> <group value="wishlist"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml index 9c3984d0287ec..3906767d04cc1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml @@ -14,7 +14,7 @@ <title value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> <description value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-29545"/> + <testCaseId value="MC-14213"/> <group value="wishlist"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml index 20ac78dfbf731..dc1580797e5be 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml @@ -14,7 +14,7 @@ <title value="Move Virtual Product from Shopping Cart to Wishlist"/> <description value="Move Virtual Product from Shopping Cart to Wishlist"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-29545"/> + <testCaseId value="MC-14210"/> <group value="wishlist"/> <group value="mtf_migrated"/> </annotations> From 17a575a1ed64b6c74d969051877f01e25621ef58 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Tue, 10 Dec 2019 18:23:11 +0200 Subject: [PATCH 2350/2437] Restore the translatable phrase for backward compatibility --- app/code/Magento/Cms/i18n/en_US.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv index 6a623f947cbc2..b860a8fb1041e 100644 --- a/app/code/Magento/Cms/i18n/en_US.csv +++ b/app/code/Magento/Cms/i18n/en_US.csv @@ -75,6 +75,7 @@ Pages,Pages "A block identifier with the same properties already exists in the selected store.","A block identifier with the same properties already exists in the selected store." "The page URL key contains capital letters or disallowed symbols.","The page URL key contains capital letters or disallowed symbols." "The page URL key cannot be made of only numbers.","The page URL key cannot be made of only numbers." +"Please rename the folder using only letters, numbers, underscores and dashes.","Please rename the folder using only letters, numbers, underscores and dashes." "Please rename the folder using only Latin letters, numbers, underscores and dashes.","Please rename the folder using only Latin letters, numbers, underscores and dashes." "We found a directory with the same name. Please try another folder name.","We found a directory with the same name. Please try another folder name." "We cannot create a new directory.","We cannot create a new directory." From 1022e6ea7812028f50bb802b9a1e14420d994e0e Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 10 Dec 2019 13:14:36 -0600 Subject: [PATCH 2351/2437] MQE-1878: Stabilize community PR 742 --- ...ntMoveConfigurableProductFromShoppingCartToWishlistTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml index 46ea3497941fd..70268f4e14e8f 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -106,6 +106,9 @@ <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigChildProduct3"/> </createData> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!-- Delete data --> From e853bf692bbff2a4b3811bb14f8d3d09b24e9386 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Tue, 10 Dec 2019 13:23:05 -0600 Subject: [PATCH 2352/2437] MC-17629: Control over search results min terms to match - Fix static tests --- .../Model/Config/Backend/MinimumShouldMatch.php | 5 ++++- .../Test/Unit/Model/Client/ElasticsearchTest.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php b/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php index 434270c62d3d9..ea64ccd772682 100644 --- a/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php +++ b/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php @@ -34,7 +34,10 @@ public function validateValue(): void { if (strlen($this->getValue()) && !preg_match('/^((\d+<)?-?\d+%?\s?)+$/', $this->getValue())) { throw new LocalizedException( - __('Value for the field "%1" was not saved because of the incorrect format.', __('Minimum Terms to Match')) + __( + 'Value for the field "%1" was not saved because of the incorrect format.', + __('Minimum Terms to Match') + ) ); } } diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index 8bb27d366ae26..607624d7b5e8e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -12,7 +12,7 @@ use Magento\Elasticsearch6\Model\Client\Elasticsearch; /** - * Class ElasticsearchTest + * Test elasticsearch client methods */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { From fa095edbfafe4d99248ca7ea9e99bec3be415fcf Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Tue, 10 Dec 2019 14:02:38 -0600 Subject: [PATCH 2353/2437] MC-22572: Shuffle related, up-sale and cross-sale products either by priority and weight - Fix static tests --- .../templates/product/list/items.phtml | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index 8f1fe3fa6874c..fc4d85044a8d6 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -6,6 +6,8 @@ // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis // phpcs:disable Generic.WhiteSpace.ScopeIndent.Incorrect +// phpcs:disable Generic.Files.LineLength +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundHelper /* @var $block \Magento\Catalog\Block\Product\AbstractProduct */ ?> @@ -156,22 +158,22 @@ switch ($type = $block->getType()) { } ?> -<?php if ($exist) :?> +<?php if ($exist):?> -<?php if ($type == 'related' || $type == 'upsell') :?> -<?php if ($type == 'related') :?> +<?php if ($type == 'related' || $type == 'upsell'):?> +<?php if ($type == 'related'):?> <div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"relatedProducts":{"relatedCheckbox":".related.checkbox"}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>" data-shuffle-weighted="<?= /* @noEscape */ $isWeightedRandom ?>"> - <?php else :?> + <?php else:?> <div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"upsellProducts":{}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>" data-shuffle-weighted="<?= /* @noEscape */ $isWeightedRandom ?>"> <?php endif; ?> - <?php else :?> + <?php else:?> <div class="block <?= $block->escapeHtmlAttr($class) ?>"> <?php endif; ?> <div class="block-title title"> <strong id="block-<?= $block->escapeHtmlAttr($class) ?>-heading" role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> </div> <div class="block-content content" aria-labelledby="block-<?= $block->escapeHtmlAttr($class) ?>-heading"> - <?php if ($type == 'related' && $canItemsAddToCart) :?> + <?php if ($type == 'related' && $canItemsAddToCart):?> <div class="block-actions"> <?= $block->escapeHtml(__('Check items to add to the cart or')) ?> <button type="button" class="action select" data-role="select-all"><span><?= $block->escapeHtml(__('select all')) ?></span></button> @@ -179,16 +181,16 @@ switch ($type = $block->getType()) { <?php endif; ?> <div class="products wrapper grid products-grid products-<?= $block->escapeHtmlAttr($type) ?>"> <ol class="products list items product-items"> - <?php foreach ($items as $_item) :?> + <?php foreach ($items as $_item):?> <?php $available = ''; ?> - <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> - <?php if (!$_item->getRequiredOptions()) :?> + <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related'):?> + <?php if (!$_item->getRequiredOptions()):?> <?php $available = 'related-available'; ?> <?php endif; ?> <?php endif; ?> - <?php if ($type == 'related' || $type == 'upsell') :?> + <?php if ($type == 'related' || $type == 'upsell'):?> <li class="item product product-item" style="display: none;" data-shuffle-group="<?= $block->escapeHtmlAttr($_item->getPriority()) ?>" > - <?php else :?> + <?php else:?> <li class="item product product-item"> <?php endif; ?> <div class="product-item-info <?= /* @noEscape */ $available ?>"> @@ -203,12 +205,12 @@ switch ($type = $block->getType()) { <?= /* @noEscape */ $block->getProductPrice($_item) ?> - <?php if ($templateType) :?> + <?php if ($templateType):?> <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> - <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> - <?php if (!$_item->getRequiredOptions()) :?> + <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related'):?> + <?php if (!$_item->getRequiredOptions()):?> <div class="field choice related"> <input type="checkbox" class="checkbox related" id="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>" name="related_products[]" value="<?= $block->escapeHtmlAttr($_item->getId()) ?>" /> <label class="label" for="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>"><span><?= $block->escapeHtml(__('Add to Cart')) ?></span></label> @@ -216,16 +218,16 @@ switch ($type = $block->getType()) { <?php endif; ?> <?php endif; ?> - <?php if ($showAddTo || $showCart) :?> + <?php if ($showAddTo || $showCart):?> <div class="product actions product-item-actions"> - <?php if ($showCart) :?> + <?php if ($showCart):?> <div class="actions-primary"> - <?php if ($_item->isSaleable()) :?> - <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)) :?> + <?php if ($_item->isSaleable()):?> + <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)):?> <button class="action tocart primary" data-mage-init='{"redirectUrl": {"url": "<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php else :?> + <?php else:?> <?php $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); $postData = $postDataHelper->getPostData($block->escapeUrl($block->getAddToCartUrl($_item)), ['product' => $_item->getEntityId()]) ?> @@ -235,19 +237,19 @@ switch ($type = $block->getType()) { <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> <?php endif; ?> - <?php else :?> - <?php if ($_item->getIsSalable()) :?> + <?php else:?> + <?php if ($_item->getIsSalable()):?> <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> - <?php else :?> + <?php else:?> <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> - <?php if ($showAddTo) :?> + <?php if ($showAddTo):?> <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> - <?php if ($addToBlock = $block->getChildBlock('addto')) :?> + <?php if ($addToBlock = $block->getChildBlock('addto')):?> <?= $addToBlock->setProduct($_item)->getChildHtml() ?> <?php endif; ?> </div> From d37620eff93dd5e92381c1cad8c91a6229ffb1e1 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 11 Dec 2019 10:52:51 +0700 Subject: [PATCH 2354/2437] [Catalog] Cover Price Validation Result class by Unit Test --- .../Product/Price/Validation/ResultTest.php | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php new file mode 100644 index 0000000000000..7ed32d564ca19 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Model\Product\Price\Validation; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Catalog\Model\Product\Price\Validation\Result; +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Api\Data\PriceUpdateResultInterface; +use Magento\Catalog\Api\Data\PriceUpdateResultInterfaceFactory; + +class ResultTest extends TestCase +{ + /** + * @var Result + */ + private $model; + + /** + * @var PriceUpdateResultInterfaceFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $priceUpdateResultFactory; + + /** + * @var ObjectManagerHelper|PHPUnit_Framework_MockObject_MockObject + */ + private $objectManager; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->priceUpdateResultFactory = $this->getMockBuilder(PriceUpdateResultInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + Result::class, + [ + 'priceUpdateResultFactory' => $this->priceUpdateResultFactory + ] + ); + + $this->model->addFailedItem(1, 'Invalid attribute color = 1', ['SKU' => 'ABC', 'storeId' => 1]); + $this->model->addFailedItem(2, 'Invalid attribute size = M', ['SKU' => 'DEF', 'storeId' => 1]); + } + + /** + * Test getFailedRowIds() function + */ + public function testGetFailedRowIds() + { + $this->assertEquals([1, 2], $this->model->getFailedRowIds()); + } + + /** + * Test getFailedItems() function + */ + public function testGetFailedItems() + { + $priceUpdateResult1 = $this->createMock(PriceUpdateResultInterface::class); + $priceUpdateResult2 = $this->createMock(PriceUpdateResultInterface::class); + + $this->priceUpdateResultFactory->expects($this->at(0)) + ->method('create') + ->willReturn($priceUpdateResult1); + $this->priceUpdateResultFactory->expects($this->at(1)) + ->method('create') + ->willReturn($priceUpdateResult2); + + $priceUpdateResult1->expects($this->once())->method('setMessage') + ->with('Invalid attribute color = 1'); + $priceUpdateResult1->expects($this->once())->method('setParameters') + ->with(['SKU' => 'ABC', 'storeId' => 1]); + + $priceUpdateResult2->expects($this->once())->method('setMessage') + ->with('Invalid attribute size = M'); + $priceUpdateResult2->expects($this->once())->method('setParameters') + ->with(['SKU' => 'DEF', 'storeId' => 1]); + + $this->assertEquals([$priceUpdateResult1, $priceUpdateResult2], $this->model->getFailedItems()); + } +} From c3a97c015461c39e58f58c41a8c818d2da909571 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 11 Dec 2019 15:06:30 +0700 Subject: [PATCH 2355/2437] [Customer] Cover CustomerData\Customer and CustomerData\JsLayoutDataProviderPool by Unit Test --- .../Test/Unit/CustomerData/CustomerTest.php | 90 +++++++++++++++++++ .../JsLayoutDataProviderPoolTest.php | 84 +++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php new file mode 100644 index 0000000000000..735c526878b6b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\CustomerData; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\CustomerData\Customer as CustomerData; +use Magento\Customer\Helper\Session\CurrentCustomer; +use Magento\Customer\Helper\View; +use Magento\Customer\Api\Data\CustomerInterface; +use PHPUnit\Framework\TestCase; + +class CustomerTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var CustomerData + */ + private $customerData; + + /** + * @var CurrentCustomer + */ + private $currentCustomerMock; + + /** + * @var View + */ + private $customerViewHelperMock; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->currentCustomerMock = $this->createMock(CurrentCustomer::class); + $this->customerViewHelperMock = $this->createMock(View::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->customerData = $this->objectManagerHelper->getObject( + CustomerData::class, + [ + 'currentCustomer' => $this->currentCustomerMock, + 'customerViewHelper' => $this->customerViewHelperMock + ] + ); + } + + /** + * Test getSectionData() without customer Id + */ + public function testGetSectionDataWithoutCustomerId() + { + $this->currentCustomerMock->expects($this->any())->method('getCustomerId')->willReturn(null); + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() with customer + */ + public function testGetSectionDataWithCustomer() + { + $this->currentCustomerMock->expects($this->any())->method('getCustomerId')->willReturn(1); + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->expects($this->any())->method('getFirstname')->willReturn('John'); + $customerMock->expects($this->any())->method('getWebsiteId')->willReturn(1); + $this->currentCustomerMock->expects($this->any())->method('getCustomer')->willReturn($customerMock); + $this->customerViewHelperMock->expects($this->any())->method('getCustomerName') + ->with($customerMock) + ->willReturn('John Adam'); + + $this->assertEquals( + [ + 'fullname' => 'John Adam', + 'firstname' => 'John', + 'websiteId' => 1, + ], + $this->customerData->getSectionData() + ); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php new file mode 100644 index 0000000000000..7b7f6ec9f841b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\CustomerData; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Tax\CustomerData\CheckoutTotalsJsLayoutDataProvider as CheckoutTotalsJs; +use Magento\Customer\CustomerData\JsLayoutDataProviderPool; +use PHPUnit\Framework\TestCase; + +class JsLayoutDataProviderPoolTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var CheckoutTotalsJs + */ + private $checkoutTotalsJsLayoutDataProviderMock; + + /** + * @var JsLayoutDataProviderPool + */ + private $jsLayoutDataProviderPool; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->checkoutTotalsJsLayoutDataProviderMock = $this->createMock(CheckoutTotalsJs::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->jsLayoutDataProviderPool = $this->objectManagerHelper->getObject( + JsLayoutDataProviderPool::class, + [ + 'jsLayoutDataProviders' => [ + 'checkout_totals' => $this->checkoutTotalsJsLayoutDataProviderMock + ] + ] + ); + } + + /** + * Test getData() function + */ + public function testGetData() + { + $checkoutTotalsJsData = [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 1, + 'display_cart_subtotal_excl_tax' => 1 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ]; + $this->checkoutTotalsJsLayoutDataProviderMock->expects($this->any()) + ->method('getData') + ->willReturn($checkoutTotalsJsData); + + $this->assertEquals($checkoutTotalsJsData, $this->jsLayoutDataProviderPool->getData()); + } +} From bad9717027a4b4a54eb427ed5915b9315f045034 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Wed, 11 Dec 2019 13:43:45 +0200 Subject: [PATCH 2356/2437] MC-29417: hide_from_product_page not working for import using store views --- .../Model/Import/Product.php | 51 ++++++++------ .../Import/Product/MediaGalleryProcessor.php | 20 +++--- .../Model/Import/ProductTest.php | 70 +++++++++++++++++-- ...mport_change_image_label_for_storeview.csv | 2 + .../import_hide_image_for_storeview.csv | 2 + 5 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 8f70ea88f4ba7..7ebc397cbe650 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1569,6 +1569,13 @@ protected function _saveProducts() continue; } + $storeId = !empty($rowData[self::COL_STORE]) + ? $this->getStoreIdByCode($rowData[self::COL_STORE]) + : Store::DEFAULT_STORE_ID; + $rowExistingImages = $existingImages[$storeId][$rowSku] ?? []; + $rowStoreMediaGalleryValues = $rowExistingImages; + $rowExistingImages += $existingImages[Store::DEFAULT_STORE_ID][$rowSku] ?? []; + if (self::SCOPE_STORE == $rowScope) { // set necessary data from SCOPE_DEFAULT row $rowData[self::COL_TYPE] = $this->skuProcessor->getNewSku($rowSku)['type_id']; @@ -1672,19 +1679,16 @@ protected function _saveProducts() // 5. Media gallery phase list($rowImages, $rowLabels) = $this->getImagesFromRow($rowData); - $storeId = !empty($rowData[self::COL_STORE]) - ? $this->getStoreIdByCode($rowData[self::COL_STORE]) - : Store::DEFAULT_STORE_ID; $imageHiddenStates = $this->getImagesHiddenStates($rowData); foreach (array_keys($imageHiddenStates) as $image) { - if (array_key_exists($rowSku, $existingImages) - && array_key_exists($image, $existingImages[$rowSku]) - ) { - $rowImages[self::COL_MEDIA_IMAGE][] = $image; + //Mark image as uploaded if it exists + if (array_key_exists($image, $rowExistingImages)) { $uploadedImages[$image] = $image; } - - if (empty($rowImages)) { + //Add image to hide to images list if it does not exist + if (empty($rowImages[self::COL_MEDIA_IMAGE]) + || !in_array($image, $rowImages[self::COL_MEDIA_IMAGE]) + ) { $rowImages[self::COL_MEDIA_IMAGE][] = $image; } } @@ -1725,24 +1729,29 @@ protected function _saveProducts() continue; } - if (isset($existingImages[$rowSku][$uploadedFile])) { - $currentFileData = $existingImages[$rowSku][$uploadedFile]; + if (isset($rowExistingImages[$uploadedFile])) { + $currentFileData = $rowExistingImages[$uploadedFile]; + $currentFileData['store_id'] = $storeId; + $storeMediaGalleryValueExists = isset($rowStoreMediaGalleryValues[$uploadedFile]); + if (array_key_exists($uploadedFile, $imageHiddenStates) + && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] + ) { + $imagesForChangeVisibility[] = [ + 'disabled' => $imageHiddenStates[$uploadedFile], + 'imageData' => $currentFileData, + 'exists' => $storeMediaGalleryValueExists + ]; + $storeMediaGalleryValueExists = true; + } + if (isset($rowLabels[$column][$columnImageKey]) && $rowLabels[$column][$columnImageKey] != $currentFileData['label'] ) { $labelsForUpdate[] = [ 'label' => $rowLabels[$column][$columnImageKey], - 'imageData' => $currentFileData - ]; - } - - if (array_key_exists($uploadedFile, $imageHiddenStates) - && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] - ) { - $imagesForChangeVisibility[] = [ - 'disabled' => $imageHiddenStates[$uploadedFile], - 'imageData' => $currentFileData + 'imageData' => $currentFileData, + 'exists' => $storeMediaGalleryValueExists ]; } } else { diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php index 00e6da0ebe077..bd8523a4e396e 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php @@ -12,7 +12,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; -use Magento\Store\Model\Store; /** * Process and saves images during import. @@ -179,13 +178,15 @@ private function updateMediaGalleryField(array $data, $field) $insertData = []; foreach ($data as $datum) { $imageData = $datum['imageData']; + $exists = $datum['exists'] ?? true; - if ($imageData[$field] === null) { + if (!$exists) { $insertData[] = [ $field => $datum[$field], $this->getProductEntityLinkField() => $imageData[$this->getProductEntityLinkField()], 'value_id' => $imageData['value_id'], - 'store_id' => Store::DEFAULT_STORE_ID, + 'store_id' => $imageData['store_id'], + 'position' => $imageData['position'], ]; } else { $this->connection->update( @@ -196,7 +197,7 @@ private function updateMediaGalleryField(array $data, $field) [ $this->getProductEntityLinkField() . ' = ?' => $imageData[$this->getProductEntityLinkField()], 'value_id = ?' => $imageData['value_id'], - 'store_id = ?' => Store::DEFAULT_STORE_ID, + 'store_id = ?' => $imageData['store_id'], ] ); } @@ -240,14 +241,15 @@ public function getExistingImages(array $bunch) )->joinLeft( ['mgv' => $this->mediaGalleryValueTableName], sprintf( - '(mg.value_id = mgv.value_id AND mgv.%s = mgvte.%s AND mgv.store_id = %d)', + '(mgv.%s = mgvte.%s AND mg.value_id = mgv.value_id)', $this->getProductEntityLinkField(), - $this->getProductEntityLinkField(), - Store::DEFAULT_STORE_ID + $this->getProductEntityLinkField() ), [ + 'store_id' => 'mgv.store_id', 'label' => 'mgv.label', 'disabled' => 'mgv.disabled', + 'position' => 'mgv.position', ] )->joinInner( ['pe' => $this->productEntityTableName], @@ -259,7 +261,9 @@ public function getExistingImages(array $bunch) ); foreach ($this->connection->fetchAll($select) as $image) { - $result[$image['sku']][$image['value']] = $image; + $storeId = $image['store_id']; + unset($image['store_id']); + $result[$storeId][$image['sku']][$image['value']] = $image; } return $result; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 3a039217d61fc..c47a4f340f983 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -29,6 +29,7 @@ use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\Source\Csv; use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; use Psr\Log\LoggerInterface; use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; @@ -386,14 +387,14 @@ public function testSaveCustomOptions(string $importFile, string $sku, int $expe public function testSaveCustomOptionsWithMultipleStoreViews() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ - $storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $objectManager->get(StoreManagerInterface::class); $storeCodes = [ 'admin', 'default', 'secondstore', ]; - /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + /** @var StoreManagerInterface $storeManager */ $importFile = 'product_with_custom_options_and_multiple_store_views.csv'; $sku = 'simple'; $pathToFile = __DIR__ . '/_files/' . $importFile; @@ -1187,7 +1188,7 @@ public function testProductsWithMultipleStores() $product->load($id); $this->assertEquals('1', $product->getHasOptions()); - $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->setCurrentStore('fixturestore'); + $objectManager->get(StoreManagerInterface::class)->setCurrentStore('fixturestore'); /** @var \Magento\Catalog\Model\Product $simpleProduct */ $simpleProduct = $objectManager->create(\Magento\Catalog\Model\Product::class); @@ -2246,13 +2247,20 @@ function ($output, $error) { * Load product by given product sku * * @param string $sku + * @param mixed $store * @return \Magento\Catalog\Model\Product */ - private function getProductBySku($sku) + private function getProductBySku($sku, $store = null) { $resource = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Product::class); $productId = $resource->getIdBySku($sku); $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); + if ($store) { + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $store = $storeManager->getStore($store); + $product->setStoreId($store->getId()); + } $product->load($productId); return $product; @@ -2773,4 +2781,56 @@ public function testProductBaseImageAfterImport() $productAfterImport = $this->getProductBySku('simple_new'); $this->assertNotEquals('/no/exists/image/magento_image.jpg', $productAfterImport->getData('image')); } + + /** + * Tests that images are hidden only for a store view in "store_view_code". + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + */ + public function testHideImageForStoreView() + { + $expectedImageFile = '/m/a/magento_image.jpg'; + $secondStoreCode = 'fixturestore'; + $productSku = 'simple'; + $this->importDataForMediaTest('import_hide_image_for_storeview.csv'); + $product = $this->getProductBySku($productSku); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $product = $this->getProductBySku($productSku, $secondStoreCode); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(0, $imageItems); + } + + /** + * Test that images labels are updated only for a store view in "store_view_code". + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + */ + public function testChangeImageLabelForStoreView() + { + $expectedImageFile = '/m/a/magento_image.jpg'; + $expectedLabelForDefaultStoreView = 'Image Alt Text'; + $expectedLabelForSecondStoreView = 'Magento Logo'; + $secondStoreCode = 'fixturestore'; + $productSku = 'simple'; + $this->importDataForMediaTest('import_change_image_label_for_storeview.csv'); + $product = $this->getProductBySku($productSku); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $this->assertEquals($expectedLabelForDefaultStoreView, $imageItem->getLabel()); + $product = $this->getProductBySku($productSku, $secondStoreCode); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $this->assertEquals($expectedLabelForSecondStoreView, $imageItem->getLabel()); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv new file mode 100644 index 0000000000000..95321df2b6b01 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv @@ -0,0 +1,2 @@ +"sku","store_view_code","base_image", "base_image_label" +"simple","fixturestore","/m/a/magento_image.jpg", "Magento Logo" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv new file mode 100644 index 0000000000000..8400dc17d2a29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv @@ -0,0 +1,2 @@ +"sku","store_view_code","hide_from_product_page" +"simple","fixturestore","/m/a/magento_image.jpg" From d407e0106d0311c064af7cd988396a852a1e152d Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 11 Dec 2019 14:46:00 +0200 Subject: [PATCH 2357/2437] Cover magento/magento2#25556 with jasmine test --- .../js/jasmine/tests/lib/mage/browser.test.js | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 dev/tests/js/jasmine/tests/lib/mage/browser.test.js diff --git a/dev/tests/js/jasmine/tests/lib/mage/browser.test.js b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js new file mode 100644 index 0000000000000..2c60497ce1bcc --- /dev/null +++ b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js @@ -0,0 +1,62 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'mage/adminhtml/browser', + 'jquery' +], function (browser, $) { + 'use strict'; + + var obj; + + beforeEach(function () { + + /** + * Dummy constructor to use for instantiation + * @constructor + */ + var Constr = function () {}; + + Constr.prototype = browser; + + obj = new Constr(); + }); + + describe('"openDialog" method', function () { + it('Opens dialog with provided targetElementId', function () { + var options = { + 'targetElementId': 1 + }; + + spyOn($, 'ajax').and.callFake( + function () { + return { + done: function (data) { + obj.targetElementId = 1; + } + } + }); + obj.openDialog('instance/url', 100, 100, 'title', options); + obj.openDialog('instance/url', 100, 100, 'title', options); + expect($.ajax.calls.count()).toBe(1); + + }); + + it('Opens dialog with provided url param', function () { + spyOn($, 'ajax').and.callFake( + function () { + return { + done: function (data) { + obj.targetElementId = 'instance/url'; + obj.modalLoaded = true; + } + } + }); + obj.openDialog('instance/url', 100, 100, 'title', undefined); + obj.openDialog('instance/url', 100, 100, 'title', undefined); + expect($.ajax.calls.count()).toBe(1); + }); + }); +}); From 47e3ada764fc943fc63d88671ce10b2877fe57d0 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 25 Nov 2019 12:57:40 +0200 Subject: [PATCH 2358/2437] MC-4242: Newsletter subscriptions per website --- .../Mftf/Test/SearchEntityResultsTest.xml | 3 + .../Customer/Block/Account/Dashboard.php | 47 +- .../Customer/Block/Account/Dashboard/Info.php | 51 +- .../Block/Adminhtml/Edit/Tab/Newsletter.php | 371 +++++++--- .../Adminhtml/Edit/Tab/Newsletter/Grid.php | 105 ++- .../Form/Element/Newsletter/Subscriptions.php | 161 +++++ .../Adminhtml/Index/MassSubscribe.php | 18 +- .../Adminhtml/Index/MassUnsubscribe.php | 17 +- .../Controller/Adminhtml/Index/Save.php | 158 +++-- ...CustomerSubscribeNewsletterActionGroup.xml | 34 + ...CustomerSubscribeNewsletterActionGroup.xml | 36 + .../AdminEditCustomerNewsletterSection.xml | 4 +- ...stomerOnStorefrontSignupNewsletterTest.xml | 2 +- ...tomerSubscribeNewsletterPerWebsiteTest.xml | 101 +++ .../Adminhtml/Edit/Tab/NewsletterTest.php | 346 +++++++--- .../Element/Newsletter/SubscriptionsTest.php | 185 +++++ .../Adminhtml/Index/MassSubscribeTest.php | 118 ++-- .../Adminhtml/Index/MassUnsubscribeTest.php | 118 ++-- .../Controller/Adminhtml/Index/SaveTest.php | 348 +++++----- .../adminhtml/templates/tab/newsletter.phtml | 1 + .../Customer/ChangeSubscriptionStatus.php | 48 -- .../Model/Customer/CreateCustomerAccount.php | 25 +- .../Model/Customer/UpdateCustomerAccount.php | 23 +- .../Newsletter/Controller/Manage/Save.php | 29 +- .../Controller/Subscriber/NewAction.php | 54 +- .../Model/Plugin/CustomerPlugin.php | 281 ++++++-- .../Model/ResourceModel/Queue/Collection.php | 25 + .../Model/ResourceModel/Subscriber.php | 144 ++-- .../Magento/Newsletter/Model/Subscriber.php | 634 +++++++---------- .../Newsletter/Model/SubscriptionManager.php | 314 +++++++++ .../Model/SubscriptionManagerInterface.php | 51 ++ .../Unit/Model/Plugin/CustomerPluginTest.php | 389 ++++++----- .../Test/Unit/Model/SubscriberTest.php | 517 +++++++------- .../Unit/Model/SubscriptionManagerTest.php | 651 ++++++++++++++++++ app/code/Magento/Newsletter/etc/di.xml | 7 + app/code/Magento/Store/Model/System/Store.php | 62 +- .../web/css/source/_module.less | 30 + .../Adminhtml/Edit/Tab/NewsletterTest.php | 3 +- .../Controller/Adminhtml/IndexTest.php | 3 +- 39 files changed, 3817 insertions(+), 1697 deletions(-) create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml create mode 100644 app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php create mode 100644 app/code/Magento/Newsletter/Model/SubscriptionManager.php create mode 100644 app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php create mode 100644 app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 3b60e4b09de28..40a02d2ce24f3 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -308,6 +308,9 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> diff --git a/app/code/Magento/Customer/Block/Account/Dashboard.php b/app/code/Magento/Customer/Block/Account/Dashboard.php index 8e0f79d45770a..547074d0bcd81 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard.php @@ -7,6 +7,14 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\Phrase; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; /** * Customer dashboard block @@ -14,20 +22,20 @@ * @api * @since 100.0.2 */ -class Dashboard extends \Magento\Framework\View\Element\Template +class Dashboard extends Template { /** - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ protected $subscription; /** - * @var \Magento\Customer\Model\Session + * @var Session */ protected $customerSession; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $subscriberFactory; @@ -42,19 +50,17 @@ class Dashboard extends \Magento\Framework\View\Element\Template protected $customerAccountManagement; /** - * Constructor - * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @param Context $context + * @param Session $customerSession + * @param SubscriberFactory $subscriberFactory * @param CustomerRepositoryInterface $customerRepository * @param AccountManagementInterface $customerAccountManagement * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Model\Session $customerSession, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, + Context $context, + Session $customerSession, + SubscriberFactory $subscriberFactory, CustomerRepositoryInterface $customerRepository, AccountManagementInterface $customerAccountManagement, array $data = [] @@ -69,7 +75,7 @@ public function __construct( /** * Return the Customer given the customer Id stored in the session. * - * @return \Magento\Customer\Api\Data\CustomerInterface + * @return CustomerInterface */ public function getCustomer() { @@ -99,7 +105,7 @@ public function getAddressesUrl() /** * Retrieve the Url for editing the specified address. * - * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param AddressInterface $address * @return string */ public function getAddressEditUrl($address) @@ -146,13 +152,14 @@ public function getWishlistUrl() /** * Retrieve the subscription object (i.e. the subscriber). * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ public function getSubscriptionObject() { if ($this->subscription === null) { - $this->subscription = - $this->_createSubscriber()->loadByCustomerId($this->customerSession->getCustomerId()); + $websiteId = (int)$this->_storeManager->getWebsite()->getId(); + $this->subscription = $this->_createSubscriber(); + $this->subscription->loadByCustomer((int)$this->getCustomer()->getId(), $websiteId); } return $this->subscription; @@ -171,7 +178,7 @@ public function getManageNewsletterUrl() /** * Retrieve subscription text, either subscribed or not. * - * @return \Magento\Framework\Phrase + * @return Phrase */ public function getSubscriptionText() { @@ -185,7 +192,7 @@ public function getSubscriptionText() /** * Retrieve the customer's primary addresses (i.e. default billing and shipping). * - * @return \Magento\Customer\Api\Data\AddressInterface[]|bool + * @return AddressInterface[]|bool */ public function getPrimaryAddresses() { @@ -230,7 +237,7 @@ public function getBackUrl() /** * Create an instance of a subscriber. * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ protected function _createSubscriber() { diff --git a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php index 87132c3afb8bc..a48c706124c92 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php @@ -5,7 +5,15 @@ */ namespace Magento\Customer\Block\Account\Dashboard; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Form\Register; +use Magento\Customer\Helper\Session\CurrentCustomer; +use Magento\Customer\Helper\View; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; /** * Dashboard Customer Info @@ -13,44 +21,44 @@ * @api * @since 100.0.2 */ -class Info extends \Magento\Framework\View\Element\Template +class Info extends Template { /** * Cached subscription object * - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ protected $_subscription; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $_subscriberFactory; /** - * @var \Magento\Customer\Helper\View + * @var View */ protected $_helperView; /** - * @var \Magento\Customer\Helper\Session\CurrentCustomer + * @var CurrentCustomer */ protected $currentCustomer; /** * Constructor * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory - * @param \Magento\Customer\Helper\View $helperView + * @param Context $context + * @param CurrentCustomer $currentCustomer + * @param SubscriberFactory $subscriberFactory + * @param View $helperView * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - \Magento\Customer\Helper\View $helperView, + Context $context, + CurrentCustomer $currentCustomer, + SubscriberFactory $subscriberFactory, + View $helperView, array $data = [] ) { $this->currentCustomer = $currentCustomer; @@ -62,7 +70,7 @@ public function __construct( /** * Returns the Magento Customer Model for this block * - * @return \Magento\Customer\Api\Data\CustomerInterface|null + * @return CustomerInterface|null */ public function getCustomer() { @@ -84,6 +92,8 @@ public function getName() } /** + * Get the url to change password + * * @return string */ public function getChangePasswordUrl() @@ -94,7 +104,7 @@ public function getChangePasswordUrl() /** * Get Customer Subscription Object Information * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ public function getSubscriptionObject() { @@ -102,7 +112,8 @@ public function getSubscriptionObject() $this->_subscription = $this->_createSubscriber(); $customer = $this->getCustomer(); if ($customer) { - $this->_subscription->loadByCustomerId($customer->getId()); + $websiteId = (int)$this->_storeManager->getWebsite()->getId(); + $this->_subscription->loadByCustomer((int)$customer->getId(), $websiteId); } } return $this->_subscription; @@ -128,12 +139,14 @@ public function getIsSubscribed() public function isNewsletterEnabled() { return $this->getLayout() - ->getBlockSingleton(\Magento\Customer\Block\Form\Register::class) + ->getBlockSingleton(Register::class) ->isNewsletterEnabled(); } /** - * @return \Magento\Newsletter\Model\Subscriber + * Create new instance of Subscriber + * + * @return Subscriber */ protected function _createSubscriber() { @@ -141,7 +154,7 @@ protected function _createSubscriber() } /** - * @return string + * @inheritdoc */ protected function _toHtml() { diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php index 46a8dcfb28f1b..0d94a01698b31 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php @@ -5,14 +5,30 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Form\Generic; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Adminhtml\Form\Element\Newsletter\Subscriptions as SubscriptionsElement; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\System\Store as SystemStore; use Magento\Ui\Component\Layout\Tabs\TabInterface; /** * Customer account form block + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements TabInterface +class Newsletter extends Generic implements TabInterface { /** * @var string @@ -20,7 +36,7 @@ class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements T protected $_template = 'Magento_Customer::tab/newsletter.phtml'; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $_subscriberFactory; @@ -32,37 +48,57 @@ class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements T /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; /** - * Constructor - * - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Data\FormFactory $formFactory - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @var SystemStore + */ + private $systemStore; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var Share + */ + private $shareConfig; + + /** + * @param Context $context + * @param Registry $registry + * @param FormFactory $formFactory + * @param SubscriberFactory $subscriberFactory * @param AccountManagementInterface $customerAccountManagement + * @param SystemStore $systemStore + * @param CustomerRepositoryInterface $customerRepository + * @param Share $shareConfig * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Data\FormFactory $formFactory, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, + Context $context, + Registry $registry, + FormFactory $formFactory, + SubscriberFactory $subscriberFactory, AccountManagementInterface $customerAccountManagement, + SystemStore $systemStore, + CustomerRepositoryInterface $customerRepository, + Share $shareConfig, array $data = [] ) { $this->_subscriberFactory = $subscriberFactory; $this->customerAccountManagement = $customerAccountManagement; parent::__construct($context, $registry, $formFactory, $data); + $this->systemStore = $systemStore; + $this->customerRepository = $customerRepository; + $this->shareConfig = $shareConfig; } /** - * Return Tab label - * - * @return \Magento\Framework\Phrase + * @inheritdoc */ public function getTabLabel() { @@ -70,9 +106,7 @@ public function getTabLabel() } /** - * Return Tab title - * - * @return \Magento\Framework\Phrase + * @inheritdoc */ public function getTabTitle() { @@ -80,9 +114,7 @@ public function getTabTitle() } /** - * Tab class getter - * - * @return string + * @inheritdoc */ public function getTabClass() { @@ -90,9 +122,7 @@ public function getTabClass() } /** - * Return URL link to Tab content - * - * @return string + * @inheritdoc */ public function getTabUrl() { @@ -100,9 +130,7 @@ public function getTabUrl() } /** - * Tab should be loaded trough Ajax call - * - * @return bool + * @inheritdoc */ public function isAjaxLoaded() { @@ -110,19 +138,15 @@ public function isAjaxLoaded() } /** - * Can show tab in tabs - * - * @return boolean + * @inheritdoc */ public function canShowTab() { - return $this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + return (bool)$this->getCurrentCustomerId(); } /** - * Tab is hidden - * - * @return boolean + * @inheritdoc */ public function isHidden() { @@ -130,77 +154,256 @@ public function isHidden() } /** - * Initialize the form. + * @inheritdoc + */ + protected function _prepareForm() + { + $this->initForm(); + + return $this; + } + + /** + * Init form values * * @return $this - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function initForm() { if (!$this->canShowTab()) { return $this; } - /**@var \Magento\Framework\Data\Form $form */ + $form = $this->_formFactory->create(); $form->setHtmlIdPrefix('_newsletter'); - $customerId = $this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); - $subscriber = $this->_subscriberFactory->create()->loadByCustomerId($customerId); - $this->_coreRegistry->register('subscriber', $subscriber, true); + $this->setForm($form); + $fieldset = $form->addFieldset( + 'base_fieldset', + [ + 'legend' => __('Newsletter Information'), + 'class' => 'customer-newsletter-fieldset' . (!$this->isSingleWebsiteMode() ? ' multi-website' : ''), + ] + ); - $fieldset = $form->addFieldset('base_fieldset', ['legend' => __('Newsletter Information')]); + $customerSubscriptions = $this->getCustomerSubscriptionsOnWebsites(); + if (empty($customerSubscriptions)) { + return $this; + } - $fieldset->addField( - 'subscription', + if ($this->isSingleWebsiteMode()) { + $this->prepareFormSingleWebsite($fieldset, $customerSubscriptions); + $this->updateFromSession($form, $this->getCurrentCustomerId()); + } else { + $this->prepareFormMultiplyWebsite($fieldset, $customerSubscriptions); + } + + if ($this->customerAccountManagement->isReadonly($this->getCurrentCustomerId())) { + $fieldset->setReadonly(true, true); + } + + return $this; + } + + /** + * Prepare form fields for single website mode + * + * @param Fieldset $fieldset + * @param array $subscriptions + * @return void + */ + private function prepareFormSingleWebsite(Fieldset $fieldset, array $subscriptions): void + { + $customer = $this->getCurrentCustomer(); + $websiteId = (int)$this->_storeManager->getStore($customer->getStoreId())->getWebsiteId(); + $customerSubscription = $subscriptions[$websiteId] ?? $this->retrieveSubscriberData($customer, $websiteId); + + $checkboxElement = $fieldset->addField( + 'subscription_status_' . $websiteId, 'checkbox', [ 'label' => __('Subscribed to Newsletter'), - 'name' => 'subscription', + 'name' => "subscription_status[$websiteId]", 'data-form-part' => $this->getData('target_form'), - 'onchange' => 'this.value = this.checked;' + 'value' => $customerSubscription['status'], + 'onchange' => 'this.value = this.checked;', ] ); - - if ($this->customerAccountManagement->isReadonly($customerId)) { - $form->getElement('subscription')->setReadonly(true, true); + $checkboxElement->setIsChecked($customerSubscription['status']); + if (!$this->isSingleStoreMode()) { + $fieldset->addField( + 'subscription_store_' . $websiteId, + 'select', + [ + 'label' => __('Subscribed on Store View'), + 'name' => "subscription_store[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'values' => $customerSubscription['store_options'], + 'value' => $customerSubscription['store_id'] ?? null, + ] + ); } - $isSubscribed = $subscriber->isSubscribed(); - $form->setValues(['subscription' => $isSubscribed ? 'true' : 'false']); - $form->getElement('subscription')->setIsChecked($isSubscribed); - - $this->updateFromSession($form, $customerId); - - $changedDate = $this->getStatusChangedDate(); - if ($changedDate) { + if (!empty($customerSubscription['last_updated'])) { + $text = $customerSubscription['status'] ? __('Last Date Subscribed') : __('Last Date Unsubscribed'); $fieldset->addField( - 'change_status_date', + 'change_status_date_' . $websiteId, 'label', [ - 'label' => $isSubscribed ? __('Last Date Subscribed') : __('Last Date Unsubscribed'), - 'value' => $changedDate, + 'label' => $text, + 'value' => $customerSubscription['last_updated'], 'bold' => true ] ); } + } - $this->setForm($form); - return $this; + /** + * Prepare form fields for multiply website mode + * + * @param Fieldset $fieldset + * @param array $subscriptions + * @return void + */ + private function prepareFormMultiplyWebsite(Fieldset $fieldset, array $subscriptions): void + { + $fieldset->addType('customer_subscription', SubscriptionsElement::class); + $fieldset->addField( + 'subscription', + 'customer_subscription', + [ + 'label' => __('Subscribed to Newsletter'), + 'name' => 'subscription', + 'subscriptions' => $subscriptions, + 'target_form' => $this->getData('target_form'), + 'class' => 'newsletter-subscriptions', + 'customer_id' => $this->getCurrentCustomerId(), + ] + ); + } + + /** + * Get current customer id + * + * @return int + */ + private function getCurrentCustomerId(): int + { + return (int)$this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + } + + /** + * Get current customer model + * + * @return CustomerInterface|null + */ + private function getCurrentCustomer(): ?CustomerInterface + { + $customerId = $this->getCurrentCustomerId(); + try { + $customer = $this->customerRepository->getById($customerId); + } catch (NoSuchEntityException $e) { + return null; + } + + return $customer; + } + + /** + * Get Customer Subscriptions on Websites + * + * @return array + */ + private function getCustomerSubscriptionsOnWebsites(): array + { + $customer = $this->getCurrentCustomer(); + if ($customer === null) { + return []; + } + + $subscriptions = []; + foreach ($this->_storeManager->getWebsites() as $website) { + /** Skip websites without stores */ + if ($website->getStoresCount() === 0) { + continue; + } + $websiteId = (int)$website->getId(); + $subscriptions[$websiteId] = $this->retrieveSubscriberData($customer, $websiteId); + } + + return $subscriptions; + } + + /** + * Retrieve subscriber data + * + * @param CustomerInterface $customer + * @param int $websiteId + * @return array + */ + private function retrieveSubscriberData(CustomerInterface $customer, int $websiteId): array + { + $subscriber = $this->_subscriberFactory->create()->loadByCustomer((int)$customer->getId(), $websiteId); + $storeOptions = $this->systemStore->getStoreOptionsTree(false, [], [], [$websiteId]); + $subscriberData = $subscriber->getData(); + $subscriberData['last_updated'] = $this->getSubscriberStatusChangeDate($subscriber); + $subscriberData['website_id'] = $websiteId; + $subscriberData['website_name'] = $this->systemStore->getWebsiteName($websiteId); + $subscriberData['status'] = $subscriber->isSubscribed(); + $subscriberData['store_options'] = $storeOptions; + + return $subscriberData; + } + + /** + * Is single systemStore mode + * + * @return bool + */ + private function isSingleStoreMode(): bool + { + return $this->_storeManager->isSingleStoreMode(); + } + + /** + * Is single website mode + * + * @return bool + */ + private function isSingleWebsiteMode(): bool + { + return $this->isSingleStoreMode() + || !$this->shareConfig->isGlobalScope() + || count($this->_storeManager->getWebsites()) === 1; } /** * Update form elements from session data * - * @param \Magento\Framework\Data\Form $form + * @param Form $form * @param int $customerId * @return void */ - protected function updateFromSession(\Magento\Framework\Data\Form $form, $customerId) + protected function updateFromSession(Form $form, $customerId) { + if (!$this->isSingleWebsiteMode()) { + return; + } $data = $this->_backendSession->getCustomerFormData(); - if (!empty($data)) { - $dataCustomerId = isset($data['customer']['entity_id']) ? $data['customer']['entity_id'] : null; - if (isset($data['subscription']) && $dataCustomerId == $customerId) { - $form->getElement('subscription')->setIsChecked($data['subscription']); - } + $sessionCustomerId = $data['customer']['entity_id'] ?? null; + if ($sessionCustomerId === null || (int)$sessionCustomerId !== (int)$customerId) { + return; + } + + $websiteId = (int)$this->getCurrentCustomer()->getWebsiteId(); + $statusSessionValue = $data['subscription_status'][$websiteId] ?? null; + if ($statusSessionValue !== null) { + $subscribeElement = $form->getElement('subscription_status_' . $websiteId); + $subscribeElement->setValue($statusSessionValue); + $subscribeElement->setChecked($statusSessionValue); + } + $storeSessionValue = $data['subscription_store'][$websiteId] ?? null; + $storeElement = $form->getElement('subscription_store_' . $websiteId); + if ($storeSessionValue !== null && $storeElement !== null) { + $storeElement->setValue($storeSessionValue); } } @@ -211,28 +414,32 @@ protected function updateFromSession(\Magento\Framework\Data\Form $form, $custom */ public function getStatusChangedDate() { - $subscriber = $this->_coreRegistry->registry('subscriber'); - if ($subscriber->getChangeStatusAt()) { - return $this->formatDate( - $subscriber->getChangeStatusAt(), - \IntlDateFormatter::MEDIUM, - true - ); + $customer = $this->getCurrentCustomerId(); + if ($customer === null) { + return ''; } + $customerId = (int)$customer->getId(); + $subscriber = $this->_subscriberFactory->create()->loadByCustomer($customerId, (int)$customer->getWebsiteId()); - return null; + return $this->getSubscriberStatusChangeDate($subscriber); } /** + * Retrieve the date when the subscriber status changed + * + * @param Subscriber $subscriber * @return string */ - protected function _toHtml() + private function getSubscriberStatusChangeDate(Subscriber $subscriber): string { - if ($this->canShowTab()) { - $this->initForm(); - return parent::_toHtml(); - } else { + if (empty($subscriber->getChangeStatusAt())) { return ''; } + + return $this->formatDate( + $subscriber->getChangeStatusAt(), + \IntlDateFormatter::MEDIUM, + true + ); } } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php index 4f49c3ba1db9b..97582fbdb19b2 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php @@ -5,47 +5,75 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Grid\Extended; +use Magento\Backend\Helper\Data; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Action; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Status; +use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Registry; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; +use Magento\Store\Model\System\Store as SystemStore; + /** * Adminhtml newsletter queue grid block * * @api * @since 100.0.2 */ -class Grid extends \Magento\Backend\Block\Widget\Grid\Extended +class Grid extends Extended { /** * Core registry * - * @var \Magento\Framework\Registry|null + * @var Registry|null */ protected $_coreRegistry = null; /** - * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory + * @var CollectionFactory */ protected $_collectionFactory; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory - * @param \Magento\Framework\Registry $coreRegistry + * @var Share + */ + private $shareConfig; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @param Context $context + * @param Data $backendHelper + * @param CollectionFactory $collectionFactory + * @param Registry $coreRegistry * @param array $data + * @param Share|null $shareConfig + * @param SystemStore|null $systemStore */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory, - \Magento\Framework\Registry $coreRegistry, - array $data = [] + Context $context, + Data $backendHelper, + CollectionFactory $collectionFactory, + Registry $coreRegistry, + array $data = [], + Share $shareConfig = null, + SystemStore $systemStore = null ) { $this->_coreRegistry = $coreRegistry; $this->_collectionFactory = $collectionFactory; parent::__construct($context, $backendHelper, $data); + $this->shareConfig = $shareConfig ?? ObjectManager::getInstance()->get(Share::class); + $this->systemStore = $systemStore ?? ObjectManager::getInstance()->get(SystemStore::class); } /** - * @return void + * @inheritdoc */ protected function _construct() { @@ -60,7 +88,7 @@ protected function _construct() } /** - * @return string + * @inheritdoc */ public function getGridUrl() { @@ -68,22 +96,19 @@ public function getGridUrl() } /** - * @return $this + * @inheritdoc */ protected function _prepareCollection() { - /** @var $collection \Magento\Newsletter\Model\ResourceModel\Queue\Collection */ - $collection = $this->_collectionFactory->create()->addTemplateInfo()->addSubscriberFilter( - $this->_coreRegistry->registry('subscriber')->getId() - ); - + $customerId = $this->getCurrentCustomerId(); + $collection = $this->_collectionFactory->create()->addTemplateInfo()->addCustomerFilter($customerId); $this->setCollection($collection); return parent::_prepareCollection(); } /** - * @return $this + * @inheritdoc */ protected function _prepareColumns() { @@ -132,6 +157,19 @@ protected function _prepareColumns() ['header' => __('Subject'), 'align' => 'center', 'index' => 'template_subject'] ); + if ($this->isMultiplyWebsiteMode()) { + $this->addColumn( + 'store_view', + [ + 'header' => __('Store View'), + 'align' => 'center', + 'index' => 'subscriber_store_id', + 'type' => 'options', + 'option_groups' => $this->systemStore->getStoreValuesForForm(), + ] + ); + } + $this->addColumn( 'status', [ @@ -139,7 +177,7 @@ protected function _prepareColumns() 'align' => 'center', 'filter' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Filter\Status::class, 'index' => 'queue_status', - 'renderer' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Status::class + 'renderer' => Status::class ] ); @@ -150,10 +188,31 @@ protected function _prepareColumns() 'align' => 'center', 'filter' => false, 'sortable' => false, - 'renderer' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Action::class + 'renderer' => Action::class ] ); return parent::_prepareColumns(); } + + /** + * Get current customer id + * + * @return int + */ + private function getCurrentCustomerId(): int + { + return (int)$this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + } + + /** + * Is multiply website mode + * + * @return bool + */ + private function isMultiplyWebsiteMode(): bool + { + return $this->shareConfig->isGlobalScope() + && count($this->_storeManager->getWebsites()) > 1; + } } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php b/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php new file mode 100644 index 0000000000000..e9686daa3e3ab --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Adminhtml\Form\Element\Newsletter; + +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Escaper; + +/** + * Customer Newsletter Subscriptions Element + */ +class Subscriptions extends AbstractElement +{ + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + /** + * @param Factory $factoryElement + * @param CollectionFactory $factoryCollection + * @param Escaper $escaper + * @param DataPersistorInterface $dataPersistor + * @param array $data + */ + public function __construct( + Factory $factoryElement, + CollectionFactory $factoryCollection, + Escaper $escaper, + DataPersistorInterface $dataPersistor, + $data = [] + ) { + $this->dataPersistor = $dataPersistor; + parent::__construct($factoryElement, $factoryCollection, $escaper, $data); + } + + /** + * @inheritdoc + */ + public function getElementHtml() + { + $websiteHeader = $this->_escape(__('Website')); + $subscribedHeader = $this->_escape(__('Subscribed')); + $storeHeader = $this->_escape(__('Store View')); + $lastUpdatedHeader = $this->_escape(__('Last Updated At')); + $bodyHtml = ''; + foreach ($this->getData('subscriptions') as $subscriptions) { + $storeId = !empty($subscriptions['store_id']) ? (int)$subscriptions['store_id'] : null; + $websiteId = (int)$subscriptions['website_id']; + $websiteName = $this->_escape($subscriptions['website_name']); + $subscribed = (bool)$subscriptions['status']; + $options = (array)$subscriptions['store_options']; + $statusElement = $this->getSubscriptionStatusElementHtml($websiteId, $subscribed); + $storeSelectElement = $this->getStoreSelectElementHtml($websiteId, $options, $storeId); + $lastUpdated = !empty($subscriptions['last_updated']) ? $subscriptions['last_updated'] : ''; + + $bodyHtml .= "<tr><td>{$websiteName}</td><td class=\"subscriber-status\">$statusElement</td>" + . "<td>$storeSelectElement</td><td>$lastUpdated</td></tr>"; + } + $html = '<table class="admin__table-secondary">' + . "<tr><th>{$websiteHeader}</th><th class=\"subscriber-status\">{$subscribedHeader}</th>" + . "<th>{$storeHeader}</th><th>{$lastUpdatedHeader}</th></tr>" + . $bodyHtml + . '</table>'; + + return $html; + } + + /** + * Get store select element html + * + * @param int $websiteId + * @param array $options + * @param int|null $value + * @return string + */ + private function getStoreSelectElementHtml(int $websiteId, array $options, ?int $value): string + { + $name = $this->getData('name'); + $value = $this->getSessionFormValue("{$name}_store", $websiteId) ?? $value; + $elementId = $name . '_store_' . $websiteId; + $element = $this->_factoryElement->create( + 'select', + [ + 'data' => [ + 'name' => "{$name}_store[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'values' => $options, + 'value' => $value, + 'required' => true, + ], + ] + ); + $element->setId($elementId); + $element->setForm($this->getForm()); + if ($this->getReadonly()) { + $element->setReadonly($this->getReadonly(), $this->getDisabled()); + } + + return $element->toHtml(); + } + + /** + * Get subscription status element html + * + * @param int $websiteId + * @param bool $value + * @return string + */ + private function getSubscriptionStatusElementHtml(int $websiteId, bool $value): string + { + $name = $this->getData('name'); + $value = $this->getSessionFormValue("{$name}_status", $websiteId) ?? $value; + $elementId = $name . '_status_' . $websiteId; + $element = $this->_factoryElement->create( + 'checkbox', + [ + 'data' => [ + 'name' => "{$name}_status[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'value' => $value, + 'onchange' => 'this.value = this.checked;', + ], + ] + ); + $element->setId($elementId); + $element->setForm($this->getForm()); + $element->setIsChecked($value); + if ($this->getReadonly()) { + $element->setReadonly($this->getReadonly(), $this->getDisabled()); + } + + return $element->toHtml(); + } + + /** + * Get form data value from current session + * + * @param string $name + * @param int $arrayKey + * @return string|null + */ + private function getSessionFormValue(string $name, int $arrayKey): ?string + { + $data = $this->dataPersistor->get('customer_form'); + $currentCustomerId = $this->getData('customer_id'); + $sessionCustomerId = $data['customer']['entity_id'] ?? null; + if ($sessionCustomerId === null || $currentCustomerId !== (int)$sessionCustomerId) { + return null; + } + + return $data[$name][$arrayKey] ?? null; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php index 29a66bf1ff933..881c5caebcbee 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php @@ -7,10 +7,10 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Newsletter\Model\SubscriberFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; use Magento\Framework\Controller\ResultFactory; @@ -25,27 +25,27 @@ class MassSubscribe extends AbstractMassAction implements HttpPostActionInterfac protected $customerRepository; /** - * @var SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository - * @param SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, CustomerRepositoryInterface $customerRepository, - SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager ) { parent::__construct($context, $filter, $collectionFactory); $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; + $this->subscriptionManager = $subscriptionManager; } /** @@ -58,9 +58,9 @@ protected function massAction(AbstractCollection $collection) { $customersUpdated = 0; foreach ($collection->getAllIds() as $customerId) { - // Verify customer exists - $this->customerRepository->getById($customerId); - $this->subscriberFactory->create()->subscribeCustomerById($customerId); + $customer = $this->customerRepository->getById($customerId); + $storeId = (int)$customer->getStoreId(); + $this->subscriptionManager->subscribeCustomer($customerId, $storeId); $customersUpdated++; } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index fddf18489b9a5..17f420d864df0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -9,7 +9,7 @@ use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Backend\App\Action\Context; -use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; @@ -25,27 +25,27 @@ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterf protected $customerRepository; /** - * @var SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository - * @param SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, CustomerRepositoryInterface $customerRepository, - SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager ) { parent::__construct($context, $filter, $collectionFactory); $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; + $this->subscriptionManager = $subscriptionManager; } /** @@ -59,8 +59,9 @@ protected function massAction(AbstractCollection $collection) $customersUpdated = 0; foreach ($collection->getAllIds() as $customerId) { // Verify customer exists - $this->customerRepository->getById($customerId); - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); + $customer = $this->customerRepository->getById($customerId); + $storeId = (int)$customer->getStoreId(); + $this->subscriptionManager->unsubscribeCustomer($customerId, $storeId); $customersUpdated++; } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 3ee33af9ec073..b85b735ea9c4f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -5,25 +5,45 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Model\Address\Mapper; -use Magento\Customer\Model\AddressRegistry; -use Magento\Framework\Api\DataObjectHelper; use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Customer\Api\Data\CustomerInterfaceFactory; -use Magento\Framework\DataObjectFactory as ObjectFactory; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Customer\Api\AddressMetadataInterface; -use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Helper\View; +use Magento\Customer\Model\Address\Mapper; +use Magento\Customer\Model\AddressFactory; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerFactory; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory as ObjectFactory; +use Magento\Framework\Exception\AbstractAggregateException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Math\Random; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\LayoutFactory; +use Magento\Framework\View\Result\PageFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; /** * Save customer action. @@ -37,6 +57,11 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpP */ private $emailNotification; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * @var AddressRegistry */ @@ -45,60 +70,62 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpP /** * Constructor * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\AddressFactory $addressFactory - * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory - * @param \Magento\Customer\Helper\View $viewHelper - * @param \Magento\Framework\Math\Random $random + * @param Context $context + * @param Registry $coreRegistry + * @param FileFactory $fileFactory + * @param CustomerFactory $customerFactory + * @param AddressFactory $addressFactory + * @param FormFactory $formFactory + * @param SubscriberFactory $subscriberFactory + * @param View $viewHelper + * @param Random $random * @param CustomerRepositoryInterface $customerRepository - * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter + * @param ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param Mapper $addressMapper * @param AccountManagementInterface $customerAccountManagement * @param AddressRepositoryInterface $addressRepository * @param CustomerInterfaceFactory $customerDataFactory * @param AddressInterfaceFactory $addressDataFactory * @param \Magento\Customer\Model\Customer\Mapper $customerMapper - * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + * @param DataObjectProcessor $dataObjectProcessor * @param DataObjectHelper $dataObjectHelper * @param ObjectFactory $objectFactory * @param \Magento\Framework\View\LayoutFactory $layoutFactory - * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory - * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory - * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory + * @param LayoutFactory $resultLayoutFactory + * @param PageFactory $resultPageFactory + * @param ForwardFactory $resultForwardFactory + * @param JsonFactory $resultJsonFactory + * @param SubscriptionManagerInterface $subscriptionManager * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Framework\App\Response\Http\FileFactory $fileFactory, - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\AddressFactory $addressFactory, - \Magento\Customer\Model\Metadata\FormFactory $formFactory, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - \Magento\Customer\Helper\View $viewHelper, - \Magento\Framework\Math\Random $random, + Context $context, + Registry $coreRegistry, + FileFactory $fileFactory, + CustomerFactory $customerFactory, + AddressFactory $addressFactory, + FormFactory $formFactory, + SubscriberFactory $subscriberFactory, + View $viewHelper, + Random $random, CustomerRepositoryInterface $customerRepository, - \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, + ExtensibleDataObjectConverter $extensibleDataObjectConverter, Mapper $addressMapper, AccountManagementInterface $customerAccountManagement, AddressRepositoryInterface $addressRepository, CustomerInterfaceFactory $customerDataFactory, AddressInterfaceFactory $addressDataFactory, \Magento\Customer\Model\Customer\Mapper $customerMapper, - \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, + DataObjectProcessor $dataObjectProcessor, DataObjectHelper $dataObjectHelper, ObjectFactory $objectFactory, \Magento\Framework\View\LayoutFactory $layoutFactory, - \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory, - \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, - \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, + LayoutFactory $resultLayoutFactory, + PageFactory $resultPageFactory, + ForwardFactory $resultForwardFactory, + JsonFactory $resultJsonFactory, + SubscriptionManagerInterface $subscriptionManager, AddressRegistry $addressRegistry = null ) { parent::__construct( @@ -128,6 +155,7 @@ public function __construct( $resultForwardFactory, $resultJsonFactory ); + $this->subscriptionManager = $subscriptionManager; $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); } @@ -186,7 +214,7 @@ protected function _extractData( $formData = $metadataForm->compactData($formData); // Initialize additional attributes - /** @var \Magento\Framework\DataObject $object */ + /** @var DataObject $object */ $object = $this->_objectFactory->create(['data' => $this->getRequest()->getPostValue()]); $requestData = $object->getData($scope); foreach ($additionalAttributes as $attributeCode) { @@ -196,7 +224,7 @@ protected function _extractData( // Unset unused attributes $formAttributes = $metadataForm->getAttributes(); foreach ($formAttributes as $attribute) { - /** @var \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute */ + /** @var AttributeMetadataInterface $attribute */ $attributeCode = $attribute->getAttributeCode(); if ($attribute->getFrontendInput() != 'boolean' && $formData[$attributeCode] === false @@ -281,7 +309,7 @@ protected function _extractCustomerAddressData(array & $extractedCustomerData) /** * Save customer action * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -312,7 +340,7 @@ public function execute() $this->dataObjectHelper->populateWithArray( $customer, $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $this->_eventManager->dispatch( @@ -334,17 +362,7 @@ public function execute() $customerId = $customer->getId(); } - $isSubscribed = null; - if ($this->_authorization->isAllowed(null)) { - $isSubscribed = $this->getRequest()->getPost('subscription'); - } - if ($isSubscribed !== null) { - if ($isSubscribed !== '0') { - $this->_subscriberFactory->create()->subscribeCustomerById($customerId); - } else { - $this->_subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - } + $this->updateSubscriptions($customer); // After save $this->_eventManager->dispatch( @@ -364,7 +382,7 @@ public function execute() $this->_addSessionErrorMessages($messages); $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; - } catch (\Magento\Framework\Exception\AbstractAggregateException $exception) { + } catch (AbstractAggregateException $exception) { $errors = $exception->getErrors(); $messages = []; foreach ($errors as $error) { @@ -406,6 +424,34 @@ public function execute() return $resultRedirect; } + /** + * Update customer website subscriptions + * + * @param CustomerInterface $customer + * @return void + */ + private function updateSubscriptions(CustomerInterface $customer): void + { + if (!$this->_authorization->isAllowed(null)) { + return; + } + + $subscriptionStatus = (array)$this->getRequest()->getParam('subscription_status'); + $subscriptionStore = (array)$this->getRequest()->getParam('subscription_store'); + if (empty($subscriptionStatus)) { + return; + } + + foreach ($subscriptionStatus as $websiteId => $status) { + $storeId = $subscriptionStore[$websiteId] ?? $customer->getStoreId(); + if ($status) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), $storeId); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), $storeId); + } + } + } + /** * Get email notification * @@ -415,7 +461,7 @@ public function execute() private function getEmailNotification() { if (!($this->emailNotification instanceof EmailNotificationInterface)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get( + return ObjectManager::getInstance()->get( EmailNotificationInterface::class ); } else { diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml new file mode 100644 index 0000000000000..49373bb7bebf9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="assertSubscribedToNewsletter"/> + </actionGroup> + + <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + + <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml new file mode 100644 index 0000000000000..49ea772569cc0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <checkOption selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="subscribeToNewsletter"/> + <click selector="{{AdminCustomerMainActionsSection.saveAndContinue}}" stepKey="saveAndContinue"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see userInput="You saved the customer." stepKey="seeSuccessMessage"/> + </actionGroup> + + <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml index 51b4b54c5c8b6..e6bdf2819e2a5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml @@ -9,6 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditCustomerNewsletterSection"> - <element name="subscribedToNewsletter" type="checkbox" selector="//div[@class='admin__field-control control']/input[@name='subscription']"/> + <element name="subscribedStatus" type="checkbox" selector="//div[@class='admin__field-control control']//input[@name='subscription_status[{{websiteId}}]']" parameterized="true"/> + <element name="subscribedStore" type="select" selector="//div[@class='admin__field-control control']//select[@name='subscription_store[{{websiteId}}]']" parameterized="true"/> + <element name="subscribedLastUpdatedDate" type="text" selector="//div[@class='admin__field-control control']//div[@class='field-change_status_date_{{websiteId}}']//div[@class='control-value']" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml index 22ad60ff5de34..5d09f819bcbc0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml @@ -49,6 +49,6 @@ <waitForPageLoad stepKey="waitForEditLinkLoad"/> <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickNewsLetter"/> <waitForPageLoad stepKey="waitForNewsletterTabToOpen"/> - <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedToNewsletter}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus('1')}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml new file mode 100644 index 0000000000000..6c1a27c395917 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCustomerSubscribeNewsletterPerWebsiteTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Subscriptions"/> + <title value="Newsletter subscriptions per website"/> + <description value="Admin should be able to subscribe customer to newsletters on each website separately"/> + <testCaseId value="MC-22173"/> + <severity value="MAJOR"/> + <group value="customer"/> + </annotations> + <before> + <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="NewStoreViewData"/> + </actionGroup> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> + </after> + + <!-- Create a new Store View --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="customStore" value="NewStoreViewData"/> + </actionGroup> + <!-- Switch to the new Store View on storefront --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <waitForPageLoad stepKey="waitForNavigateHomePage"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="NewStoreViewData"/> + </actionGroup> + <!-- Create a new customer and sign up newsletter on the new Store View --> + <actionGroup ref="StorefrontCreateCustomerSignedUpNewsletterActionGroup" stepKey="createCustomer"> + <argument name="customer" value="CustomerEntityOne" /> + </actionGroup> + <!-- Go to the customer edit page on admin area --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCustomerGrid"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickToEditCustomerPage"/> + <waitForPageLoad stepKey="waitForOpenCustomerPage"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabCustomerId"/> + <!-- Assert that created customer is subscribed to newsletter on the new Store View --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" stepKey="assertSubscribedToNewsletter"> + <argument name="storeView" value="NewStoreViewData"/> + </actionGroup> + <!-- Create second website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{secondCustomWebsite.code}}"/> + </actionGroup> + <!-- Create second store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStoreGroup"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <!-- Create second store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="StoreGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> + </actionGroup> + <!-- Grab second website id into $grabFromCurrentUrlGetSecondWebsiteId --> + <actionGroup ref="AdminGetWebsiteIdActionGroup" stepKey="getSecondWebsiteId"> + <argument name="website" value="secondCustomWebsite"/> + </actionGroup> + <!-- Go to the customer edit page on admin area --> + <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> + <argument name="customerId" value="$grabCustomerId"/> + </actionGroup> + <!-- Assert that customer still subscribed to newsletter on default website --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewsletters" stepKey="assertStillSubscribedToNewsletter"/> + <!-- Subscribe to newsletters customer on the second website --> + <actionGroup ref="AdminSubscribeCustomerToNewslettersAndSelectStoreView" stepKey="subscribeToNewsletterSecondWebsite"> + <argument name="websiteId" value="$grabFromCurrentUrlGetSecondWebsiteId"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + <!-- Assert that created customer is subscribed to newsletter on second website --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" stepKey="assertSubscribedToNewsletterSecondWebsite"> + <argument name="websiteId" value="$grabFromCurrentUrlGetSecondWebsiteId"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php index 1c252bfc75a53..22a22742cdb8d 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -3,192 +3,322 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Customer\Test\Unit\Block\Adminhtml\Edit\Tab; +use Magento\Backend\Block\Template\Context; use Magento\Backend\Model\Session; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Checkbox; +use Magento\Framework\Data\Form\Element\Fieldset; +use \Magento\Framework\Data\Form\Element\Select; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\System\Store as SystemStore; +use Magento\Store\Model\Website; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** + * Test Customer account form block + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class NewsletterTest extends \PHPUnit\Framework\TestCase +class NewsletterTest extends TestCase { /** - * @var \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter + * @var Newsletter + */ + private $model; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * Store manager + * + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var Registry|MockObject */ - protected $model; + private $registryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ - protected $contextMock; + private $formFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var SubscriberFactory|MockObject */ - protected $registryMock; + private $subscriberFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AccountManagementInterface|MockObject */ - protected $formFactoryMock; + private $accountManagementMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var UrlInterface|MockObject */ - protected $subscriberFactoryMock; + private $urlBuilderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $accountManagementMock; + private $backendSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var SystemStore|MockObject */ - protected $urlBuilderMock; + private $systemStore; /** - * @var Session|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ - protected $backendSessionMock; + private $customerRepository; + /** + * @var Share|MockObject + */ + private $shareConfig; + + /** + * @inheritdoc + */ public function setUp() { - $this->contextMock = $this->createMock(\Magento\Backend\Block\Template\Context::class); - $this->registryMock = $this->createMock(\Magento\Framework\Registry::class); - $this->formFactoryMock = $this->createMock(\Magento\Framework\Data\FormFactory::class); + $this->contextMock = $this->createMock(Context::class); + $this->registryMock = $this->createMock(Registry::class); + $this->formFactoryMock = $this->createMock(FormFactory::class); $this->subscriberFactoryMock = $this->createPartialMock( - \Magento\Newsletter\Model\SubscriberFactory::class, + SubscriberFactory::class, ['create'] ); - $this->accountManagementMock = $this->createMock(\Magento\Customer\Api\AccountManagementInterface::class); - $this->urlBuilderMock = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->backendSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) + $this->accountManagementMock = $this->createMock(AccountManagementInterface::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->backendSessionMock = $this->getMockBuilder(Session::class) ->setMethods(['getCustomerFormData']) ->disableOriginalConstructor() ->getMock(); - $this->contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($this->urlBuilderMock); - $this->contextMock->expects($this->once())->method('getBackendSession')->willReturn($this->backendSessionMock); - - $this->model = new \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter( - $this->contextMock, - $this->registryMock, - $this->formFactoryMock, - $this->subscriberFactoryMock, - $this->accountManagementMock + $this->contextMock->expects($this->once()) + ->method('getUrlBuilder') + ->willReturn($this->urlBuilderMock); + $this->contextMock->expects($this->once()) + ->method('getBackendSession') + ->willReturn($this->backendSessionMock); + $this->contextMock->method('getStoreManager') + ->willReturn($this->storeManager); + $this->systemStore = $this->createMock(SystemStore::class); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + $this->shareConfig = $this->createMock(Share::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Newsletter::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'formFactory' => $this->formFactoryMock, + 'subscriberFactory' => $this->subscriberFactoryMock, + 'customerAccountManagement' => $this->accountManagementMock, + 'systemStore' => $this->systemStore, + 'customerRepository' => $this->customerRepository, + 'shareConfig' => $this->shareConfig, + ] ); } + /** + * Test to initialize the form without current customer + */ public function testInitFormCanNotShowTab() { - $this->registryMock->expects($this->once())->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + $this->registryMock->expects($this->once()) + ->method('registry') + ->with(RegistryConstants::CURRENT_CUSTOMER_ID) ->willReturn(false); + $this->assertSame($this->model, $this->model->initForm()); } + /** + * Test to initialize the form + */ public function testInitForm() { $customerId = 1; + $websiteId = 1; + $storeId = 2; + $websiteName = 'Website Name'; + $isSubscribed = true; - $subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $fieldsetMock = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $elementMock = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Checkbox::class, ['setIsChecked']); - $formMock = $this->createPartialMock( - \Magento\Framework\Data\Form::class, - ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] - ); - $this->registryMock->expects($this->exactly(3)) - ->method('registry') - ->willReturnMap( - [ - [RegistryConstants::CURRENT_CUSTOMER_ID, $customerId], - ['subscriber', $subscriberMock], - ] - ); - $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); - $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $this->registryMock->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + ->willReturn($customerId); + + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getWebsiteId')->willReturn($websiteId); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + $subscriberMock = $this->createMock(Subscriber::class); + $subscriberMock->method('loadByCustomer')->with($customerId, $websiteId)->willReturnSelf(); + $subscriberMock->method('isSubscribed')->willReturn($isSubscribed); + $subscriberMock->method('getData')->willReturn([]); $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); - $subscriberMock->expects($this->once())->method('loadByCustomerId')->with($customerId)->willReturnSelf(); - $this->registryMock->expects($this->once())->method('register')->with('subscriber', $subscriberMock); - $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); - $fieldsetMock->expects($this->once())->method('addField')->willReturn($elementMock); - $this->accountManagementMock->expects($this->once())->method('isReadOnly')->with($customerId) - ->willReturn(false); - $subscriberMock->expects($this->once())->method('isSubscribed')->willReturn(true); - $this->urlBuilderMock->expects($this->once())->method('getBaseUrl')->willReturn('domain.com'); - $this->backendSessionMock->expects($this->once())->method('getCustomerFormData')->willReturn(null); + $website = $this->createMock(Website::class); + $website->method('getStoresCount')->willReturn(1); + $website->method('getId')->willReturn($websiteId); + $store = $this->createMock(Store::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->storeManager->method('getWebsites')->willReturn([$website]); + $this->storeManager->method('isSingleStoreMode')->willReturn(true); + $this->systemStore->method('getStoreOptionsTree')->willReturn([]); + $this->systemStore->method('getWebsiteName')->with($websiteId)->willReturn($websiteName); - $formMock->expects($this->once()) - ->method('getElement') - ->willReturnMap( + $statusElementMock = $this->createMock(Checkbox::class); + $statusElementMock->expects($this->once()) + ->method('setIsChecked') + ->with($isSubscribed); + $fieldsetMock = $this->createMock(Fieldset::class); + $fieldsetMock->expects($this->once()) + ->method('addField') + ->with( + 'subscription_status_' . $websiteId, + 'checkbox', [ - ['subscription', $elementMock], + 'label' => __('Subscribed to Newsletter'), + 'name' => "subscription_status[$websiteId]", + 'data-form-part' => null, + 'value' => $isSubscribed, + 'onchange' => 'this.value = this.checked;' ] - ); - - $elementMock->expects($this->once()) - ->method('setIsChecked') - ->with(true); + ) + ->willReturn($statusElementMock); + $fieldsetMock->expects($this->once())->method('setReadonly')->with(true, true); + $formMock = $this->createPartialMock( + Form::class, + ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] + ); + $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); + $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); + $this->accountManagementMock->expects($this->once()) + ->method('isReadOnly') + ->with($customerId) + ->willReturn(true); + $this->backendSessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn(null); $this->assertSame($this->model, $this->model->initForm()); } + /** + * Test to initialize the form with session form data + */ public function testInitFormWithCustomerFormData() { $customerId = 1; + $websiteId = 1; + $storeId = 2; + $websiteName = 'Website Name'; + $isSubscribed = true; + $isSubscribedCustomerSession = false; - $subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $fieldsetMock = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $elementMock = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Checkbox::class, ['setIsChecked']); + $this->registryMock->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + ->willReturn($customerId); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getWebsiteId')->willReturn($websiteId); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + $subscriberMock = $this->createMock(Subscriber::class); + $subscriberMock->method('loadByCustomer')->with($customerId, $websiteId)->willReturnSelf(); + $subscriberMock->method('isSubscribed')->willReturn($isSubscribed); + $subscriberMock->method('getData')->willReturn([]); + $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); + $website = $this->createMock(Website::class); + $website->method('getStoresCount')->willReturn(1); + $website->method('getId')->willReturn($websiteId); + $store = $this->createMock(Store::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->storeManager->method('getWebsites')->willReturn([$website]); + $this->storeManager->method('isSingleStoreMode')->willReturn(true); + $this->systemStore->method('getStoreOptionsTree')->willReturn([]); + $this->systemStore->method('getWebsiteName')->with($websiteId)->willReturn($websiteName); + $statusElementMock = $this->createMock(Checkbox::class); + $statusElementMock->expects($this->once()) + ->method('setIsChecked') + ->with($isSubscribed); + $fieldsetMock = $this->createMock(Fieldset::class); + $fieldsetMock->expects($this->once()) + ->method('addField') + ->with( + 'subscription_status_' . $websiteId, + 'checkbox', + [ + 'label' => __('Subscribed to Newsletter'), + 'name' => "subscription_status[$websiteId]", + 'data-form-part' => null, + 'value' => $isSubscribed, + 'onchange' => 'this.value = this.checked;' + ] + ) + ->willReturn($statusElementMock); + $fieldsetMock->expects($this->once())->method('setReadonly')->with(true, true); + $statusElementForm = $this->createPartialMock(Checkbox::class, ['setChecked', 'setValue']); + $statusElementForm->method('setValue') + ->with($isSubscribedCustomerSession); + $statusElementForm->method('setChecked') + ->with($isSubscribedCustomerSession); + $storeElementForm = $this->createPartialMock(Select::class, ['setValue']); + $storeElementForm->method('setValue') + ->with(Store::DEFAULT_STORE_ID); $formMock = $this->createPartialMock( - \Magento\Framework\Data\Form::class, + Form::class, ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] ); - $this->registryMock->expects($this->exactly(3)) - ->method('registry') + $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); + $formMock->method('getElement') ->willReturnMap( [ - [RegistryConstants::CURRENT_CUSTOMER_ID, $customerId], - ['subscriber', $subscriberMock], + ['subscription_status_' . $websiteId, $statusElementForm], + ['subscription_store_' . $websiteId, $storeElementForm], ] ); $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); - $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); - $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); - $subscriberMock->expects($this->once())->method('loadByCustomerId')->with($customerId)->willReturnSelf(); - $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); - $fieldsetMock->expects($this->once())->method('addField')->willReturn($elementMock); - $this->accountManagementMock->expects($this->once())->method('isReadOnly')->with($customerId) - ->willReturn(false); - $subscriberMock->expects($this->once())->method('isSubscribed')->willReturn(false); - $this->urlBuilderMock->expects($this->once())->method('getBaseUrl')->willReturn('domain.com'); - + $this->accountManagementMock->expects($this->once()) + ->method('isReadOnly') + ->with($customerId) + ->willReturn(true); $this->backendSessionMock->expects($this->once()) ->method('getCustomerFormData') - ->willReturn([ - 'customer' => [ - 'entity_id' => $customerId, - ], - 'subscription' => true, - ]); - - $formMock->expects($this->exactly(2)) - ->method('getElement') - ->willReturnMap( - [ - ['subscription', $elementMock], - ] - ); - - $elementMock->expects($this->exactly(2)) - ->method('setIsChecked') - ->willReturnMap( + ->willReturn( [ - [false], - [true], + 'customer' => ['entity_id' => $customerId], + 'subscription_status' => [$websiteId => $isSubscribedCustomerSession] ] ); diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php new file mode 100644 index 0000000000000..f6d6777654c5b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php @@ -0,0 +1,185 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Block\Adminhtml\From\Element\Newsletter; + +use Magento\Customer\Block\Adminhtml\Form\Element\Newsletter\Subscriptions; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for Customer Newsletter Subscriptions Element + */ +class SubscriptionsTest extends TestCase +{ + /** + * @var Factory|MockObject + */ + private $factoryElement; + + /** + * @var CollectionFactory|MockObject + */ + private $factoryCollection; + + /** + * @var Escaper|MockObject + */ + private $escaper; + + /** + * @var DataPersistorInterface|MockObject + */ + private $dataPersistor; + + /** + * @var Subscriptions + */ + private $element; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->factoryElement = $this->createMock(Factory::class); + $this->factoryCollection = $this->createMock(CollectionFactory::class); + $this->escaper = $this->createMock(Escaper::class); + $this->dataPersistor = $this->createMock(DataPersistorInterface::class); + + $objectManager = new ObjectManager($this); + $this->element = $objectManager->getObject( + Subscriptions::class, + [ + 'factoryElement' => $this->factoryElement, + 'factoryCollection' => $this->factoryCollection, + 'escaper' => $this->escaper, + 'dataPersistor' => $this->dataPersistor, + 'data' => [] + ] + ); + } + + /** + * Test to Get the Html for the element + * + * @param array $data + * @param array $elementsHtml + * @param string $expectedHtml + * @return void + * @dataProvider getElementHtmlDataProvider + */ + public function testGetElementHtml(array $data, array $elementsHtml, string $expectedHtml): void + { + $this->escaper->method('escapeHtml')->withAnyParameters()->willReturnArgument(0); + $selectElementId = $data['name'] . '_store_' . $data['subscriptions'][0]['website_id']; + $selectElement = $this->createMock(AbstractElement::class); + $selectElement->expects($this->once())->method('setId')->with($selectElementId); + $selectElement->expects($this->once())->method('setForm')->willReturnSelf(); + $selectElement->method('toHtml')->willReturn($elementsHtml['store']); + $statusElementId = $data['name'] . '_status_' . $data['subscriptions'][0]['website_id']; + $statusElement = $this->createMock(AbstractElement::class); + $statusElement->expects($this->once())->method('setId')->with($statusElementId); + $statusElement->expects($this->once())->method('setForm')->willReturnSelf(); + $statusElement->method('toHtml')->willReturn($elementsHtml['status']); + $this->factoryElement->method('create')->willReturnMap( + [ + [ + 'select', + [ + 'data' => [ + 'name' => "{$data['name']}_store[{$data['subscriptions'][0]['website_id']}]", + 'data-form-part' => $data['target_form'], + 'values' => $data['subscriptions'][0]['store_options'], + 'value' => $data['subscriptions'][0]['store_id'], + 'required' => true, + ] + ], + $selectElement + ], + [ + 'checkbox', + [ + 'data' => [ + 'name' => "{$data['name']}_status[{$data['subscriptions'][0]['website_id']}]", + 'data-form-part' => $data['target_form'], + 'value' => $data['subscriptions'][0]['status'], + 'onchange' => 'this.value = this.checked;', + ] + ], + $statusElement + ] + ] + ); + $this->dataPersistor->method('get')->willReturn([]); + $this->element->setData($data); + + $this->assertEquals($expectedHtml, $this->element->getElementHtml()); + } + + /** + * Data provider for test to get the html + * + * @return array + */ + public function getElementHtmlDataProvider(): array + { + $customerId = 33; + $elementName = 'element_name'; + $targetForm = 'target_form'; + $websiteId = 1; + $websiteName = 'Website 1'; + $storeId = 2; + $status = true; + $storeOptions = ['array_of_store_options']; + $lastUpdated = 'last updated'; + $storeElementHtml = 'storeElementHtml'; + $statusElementHtml = 'statusElementHtml'; + $outputHtmlTemplate = "<table class=\"admin__table-secondary\">" + . "<tr><th>%s</th><th class=\"subscriber-status\">%s</th><th>%s</th><th>%s</th></tr>" + . "<tr><td>%s</td><td class=\"subscriber-status\">%s</td><td>%s</td><td>%s</td></tr></table>"; + + return [ + [ + 'data' => [ + 'customer_id' => $customerId, + 'name' => $elementName, + 'target_form' => $targetForm, + 'subscriptions' => [ + [ + 'store_id' => $storeId, + 'website_id' => $websiteId, + 'website_name' => $websiteName, + 'status' => $status, + 'store_options' => $storeOptions, + 'last_updated' => $lastUpdated, + ], + ], + ], + 'elementsHtml' => [ + 'status' => $statusElementHtml, + 'store' => $storeElementHtml, + ], + 'expectedHtml' => sprintf( + $outputHtmlTemplate, + 'Website', + 'Subscribed', + 'Store View', + 'Last Updated At', + $websiteName, + $statusElementHtml, + $storeElementHtml, + $lastUpdated + ) + ], + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index 33e578224400b..8f672fbfb4da6 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -6,119 +6,130 @@ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Controller\Adminhtml\Index\MassSubscribe; +use Magento\Customer\Model\ResourceModel\Customer\Collection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Message\Manager; +use Magento\Framework\ObjectManager\ObjectManager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Ui\Component\MassAction\Filter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class MassSubscribeTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MassSubscribeTest extends \PHPUnit\Framework\TestCase +class MassSubscribeTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\MassSubscribe + * @var MassSubscribe */ protected $massAction; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ protected $contextMock; /** - * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var Redirect|MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager|MockObject */ protected $objectManagerMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ protected $customerCollectionMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $customerCollectionFactoryMock; /** - * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + * @var Filter|MockObject */ protected $filterMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriptionManagerInterface|MockObject */ - protected $subscriberMock; + private $subscriptionManager; + /** + * @inheritdoc + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); - $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->responseMock = $this->createMock(ResponseInterface::class); + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerMock = $this->createPartialMock( - \Magento\Framework\ObjectManager\ObjectManager::class, + ObjectManager::class, ['create'] ); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->messageManagerMock = $this->createMock(Manager::class); $this->customerCollectionMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); $this->customerCollectionFactoryMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class) + $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); - $resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) ->disableOriginalConstructor() ->getMock(); $resultFactoryMock->expects($this->any()) ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->with(ResultFactory::TYPE_REDIRECT) ->willReturn($redirectMock); - $this->subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $subscriberFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->subscriberMock); - - $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); $resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); @@ -132,7 +143,7 @@ protected function setUp() ->method('getResultFactory') ->willReturn($resultFactoryMock); - $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->filterMock = $this->createMock(Filter::class); $this->filterMock->expects($this->once()) ->method('getCollection') ->with($this->customerCollectionMock) @@ -140,35 +151,37 @@ protected function setUp() $this->customerCollectionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->customerCollectionMock); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->getMockForAbstractClass(); $this->massAction = $objectManagerHelper->getObject( - \Magento\Customer\Controller\Adminhtml\Index\MassSubscribe::class, + MassSubscribe::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, 'collectionFactory' => $this->customerCollectionFactoryMock, 'customerRepository' => $this->customerRepositoryMock, - 'subscriberFactory' => $subscriberFactoryMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); } + /** + * Test to mass subscribe customers to newsletters + */ public function testExecute() { - $customersIds = [10, 11, 12]; - - $this->customerCollectionMock->expects($this->any()) - ->method('getAllIds') - ->willReturn($customersIds); - - $this->customerRepositoryMock->expects($this->any()) - ->method('getById') - ->willReturnMap([[10, true], [11, true], [12, true]]); - - $this->subscriberMock->expects($this->any()) - ->method('subscribeCustomerById') - ->willReturnMap([[10, true], [11, true], [12, true]]); + $storeId = 2; + $customerId = 10; + $customersIds = [$customerId, $customerId, $customerId]; + + $this->customerCollectionMock->method('getAllIds')->willReturn($customersIds); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepositoryMock->method('getById')->with($customerId)->willReturn($customer); + $this->subscriptionManager->expects($this->exactly(3)) + ->method('subscribeCustomer') + ->with($customerId, $storeId); $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') @@ -182,6 +195,9 @@ public function testExecute() $this->massAction->execute(); } + /** + * Test to mass subscribe customers to newsletters with throws exception + */ public function testExecuteWithException() { $customersIds = [10, 11, 12]; diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 971efc0e490bc..303a11989236f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -6,119 +6,130 @@ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe; +use Magento\Customer\Model\ResourceModel\Customer\Collection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Message\Manager; +use Magento\Framework\ObjectManager\ObjectManager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Ui\Component\MassAction\Filter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * Class MassUnsubscribeTest * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MassUnsubscribeTest extends \PHPUnit\Framework\TestCase +class MassUnsubscribeTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe + * @var MassUnsubscribe */ protected $massAction; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ protected $contextMock; /** - * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var Redirect|MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager|MockObject */ protected $objectManagerMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ protected $customerCollectionMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $customerCollectionFactoryMock; /** - * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + * @var Filter|MockObject */ protected $filterMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriptionManagerInterface|MockObject */ - protected $subscriberMock; + private $subscriptionManager; + /** + * @inheritdoc + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); - $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->responseMock = $this->createMock(ResponseInterface::class); + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerMock = $this->createPartialMock( - \Magento\Framework\ObjectManager\ObjectManager::class, + ObjectManager::class, ['create'] ); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->messageManagerMock = $this->createMock(Manager::class); $this->customerCollectionMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); $this->customerCollectionFactoryMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class) + $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); - $resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) ->disableOriginalConstructor() ->getMock(); $resultFactoryMock->expects($this->any()) ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->with(ResultFactory::TYPE_REDIRECT) ->willReturn($redirectMock); - $this->subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $subscriberFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->subscriberMock); - - $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); $resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); @@ -132,7 +143,7 @@ protected function setUp() ->method('getResultFactory') ->willReturn($resultFactoryMock); - $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->filterMock = $this->createMock(Filter::class); $this->filterMock->expects($this->once()) ->method('getCollection') ->with($this->customerCollectionMock) @@ -140,35 +151,37 @@ protected function setUp() $this->customerCollectionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->customerCollectionMock); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->getMockForAbstractClass(); $this->massAction = $objectManagerHelper->getObject( - \Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe::class, + MassUnsubscribe::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, 'collectionFactory' => $this->customerCollectionFactoryMock, 'customerRepository' => $this->customerRepositoryMock, - 'subscriberFactory' => $subscriberFactoryMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); } + /** + * Test to mass unsubscribe customers from newsletters + */ public function testExecute() { - $customersIds = [10, 11, 12]; - - $this->customerCollectionMock->expects($this->any()) - ->method('getAllIds') - ->willReturn($customersIds); - - $this->customerRepositoryMock->expects($this->any()) - ->method('getById') - ->willReturnMap([[10, true], [11, true], [12, true]]); - - $this->subscriberMock->expects($this->any()) - ->method('unsubscribeCustomerById') - ->willReturnMap([[10, true], [11, true], [12, true]]); + $storeId = 2; + $customerId = 10; + $customersIds = [$customerId, $customerId, $customerId]; + + $this->customerCollectionMock->method('getAllIds')->willReturn($customersIds); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepositoryMock->method('getById')->with($customerId)->willReturn($customer); + $this->subscriptionManager->expects($this->exactly(3)) + ->method('unsubscribeCustomer') + ->with($customerId, $storeId); $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') @@ -182,6 +195,9 @@ public function testExecute() $this->massAction->execute(); } + /** + * Test to mass unsubscribe customers to newsletters with throws exception + */ public function testExecuteWithException() { $customersIds = [10, 11, 12]; diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 9724ac13dde8c..2e729873961c0 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -5,13 +5,45 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\Session; +use Magento\Backend\Model\View\Result\Forward; +use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Controller\Adminhtml\Index\Save; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\AccountManagement; +use Magento\Customer\Model\Address\Mapper; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\Error; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Page\Config; +use Magento\Framework\View\Page\Title; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * Testing Save Customer use case from admin page @@ -20,220 +52,226 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @covers \Magento\Customer\Controller\Adminhtml\Index\Save */ -class SaveTest extends \PHPUnit\Framework\TestCase +class SaveTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\Save + * @var Save */ protected $model; /** - * @var \Magento\Backend\App\Action\Context + * @var Context */ protected $context; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ protected $requestMock; /** - * @var \Magento\Backend\Model\View\Result\ForwardFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ForwardFactory|MockObject */ protected $resultForwardFactoryMock; /** - * @var \Magento\Backend\Model\View\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var Forward|MockObject */ protected $resultForwardMock; /** - * @var \Magento\Framework\View\Result\PageFactory|\PHPUnit_Framework_MockObject_MockObject + * @var PageFactory|MockObject */ protected $resultPageFactoryMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var Page|MockObject */ protected $resultPageMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ protected $pageConfigMock; /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject + * @var Title|MockObject */ protected $pageTitleMock; /** - * @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ protected $sessionMock; /** - * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ protected $formFactoryMock; /** - * @var \Magento\Framework\DataObjectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectFactory|MockObject */ protected $objectFactoryMock; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerInterfaceFactory|MockObject */ protected $customerDataFactoryMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Customer\Model\Customer\Mapper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Customer\Mapper|MockObject */ protected $customerMapperMock; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ protected $dataHelperMock; /** - * @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AuthorizationInterface|MockObject */ protected $authorizationMock; /** - * @var \Magento\Newsletter\Model\SubscriberFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriberFactory|MockObject */ protected $subscriberFactoryMock; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registryMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectFactory|MockObject */ protected $redirectFactoryMock; /** - * @var \Magento\Customer\Model\AccountManagement|\PHPUnit_Framework_MockObject_MockObject + * @var AccountManagement|MockObject */ protected $managementMock; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AddressInterfaceFactory|MockObject */ protected $addressDataFactoryMock; /** - * @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var EmailNotificationInterface|MockObject */ protected $emailNotificationMock; /** - * @var \Magento\Customer\Model\Address\Mapper|\PHPUnit_Framework_MockObject_MockObject + * @var Mapper|MockObject */ protected $customerAddressMapperMock; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AddressRepositoryInterface|MockObject */ protected $customerAddressRepositoryMock; + /** + * @var SubscriptionManagerInterface|MockObject + */ + private $subscriptionManager; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor() ->getMock(); $this->resultForwardFactoryMock = $this->getMockBuilder( - \Magento\Backend\Model\View\Result\ForwardFactory::class + ForwardFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->resultForwardMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Forward::class) + $this->resultForwardMock = $this->getMockBuilder(Forward::class) ->disableOriginalConstructor() ->getMock(); - $this->resultPageFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + $this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $this->resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->setMethods(['setActiveMenu', 'getConfig', 'addBreadcrumb']) ->getMock(); - $this->pageConfigMock = $this->getMockBuilder(\Magento\Framework\View\Page\Config::class) + $this->pageConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->pageTitleMock = $this->getMockBuilder(\Magento\Framework\View\Page\Title::class) + $this->pageTitleMock = $this->getMockBuilder(Title::class) ->disableOriginalConstructor() ->getMock(); - $this->sessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->setMethods(['unsCustomerFormData', 'setCustomerFormData']) ->getMock(); - $this->formFactoryMock = $this->getMockBuilder(\Magento\Customer\Model\Metadata\FormFactory::class) + $this->formFactoryMock = $this->getMockBuilder(FormFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->objectFactoryMock = $this->getMockBuilder(\Magento\Framework\DataObjectFactory::class) + $this->objectFactoryMock = $this->getMockBuilder(DataObjectFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $this->customerDataFactoryMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class + CustomerInterfaceFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->disableOriginalConstructor() ->getMock(); $this->customerAddressRepositoryMock = $this->getMockBuilder( - \Magento\Customer\Api\AddressRepositoryInterface::class + AddressRepositoryInterface::class )->disableOriginalConstructor()->getMock(); $this->customerMapperMock = $this->getMockBuilder( \Magento\Customer\Model\Customer\Mapper::class )->disableOriginalConstructor()->getMock(); $this->customerAddressMapperMock = $this->getMockBuilder( - \Magento\Customer\Model\Address\Mapper::class + Mapper::class )->disableOriginalConstructor()->getMock(); $this->dataHelperMock = $this->getMockBuilder( - \Magento\Framework\Api\DataObjectHelper::class + DataObjectHelper::class )->disableOriginalConstructor()->getMock(); - $this->authorizationMock = $this->getMockBuilder(\Magento\Framework\AuthorizationInterface::class) + $this->authorizationMock = $this->getMockBuilder(AuthorizationInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) + $this->subscriberFactoryMock = $this->getMockBuilder(SubscriberFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->registryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->registryMock = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->redirectFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\RedirectFactory::class) + $this->redirectFactoryMock = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->managementMock = $this->getMockBuilder(\Magento\Customer\Model\AccountManagement::class) + $this->managementMock = $this->getMockBuilder(AccountManagement::class) ->disableOriginalConstructor() ->setMethods(['createAccount']) ->getMock(); - $this->addressDataFactoryMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterfaceFactory::class) + $this->addressDataFactoryMock = $this->getMockBuilder(AddressInterfaceFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -241,10 +279,10 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Customer\Controller\Adminhtml\Index\Save::class, + Save::class, [ 'resultForwardFactory' => $this->resultForwardFactoryMock, 'resultPageFactory' => $this->resultPageFactoryMock, @@ -265,6 +303,7 @@ protected function setUp() 'resultRedirectFactory' => $this->redirectFactoryMock, 'addressRepository' => $this->customerAddressRepositoryMock, 'addressMapper' => $this->customerAddressMapperMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); @@ -282,7 +321,10 @@ protected function setUp() public function testExecuteWithExistentCustomer() { $customerId = 22; - $subscription = 'true'; + $customerEmail = 'customer@email.com'; + $subscriptionWebsite = 1; + $subscriptionStatus = true; + $subscriptionStore = 3; $postValue = [ 'customer' => [ 'entity_id' => $customerId, @@ -290,7 +332,8 @@ public function testExecuteWithExistentCustomer() 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'subscription' => $subscription, + 'subscription_status' => [$subscriptionWebsite => $subscriptionStatus], + 'subscription_store' => [$subscriptionWebsite => $subscriptionStore], ]; $extractedData = [ 'entity_id' => $customerId, @@ -324,9 +367,9 @@ public function testExecuteWithExistentCustomer() 'id' => $customerId, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') @@ -346,15 +389,20 @@ public function testExecuteWithExistentCustomer() ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') + ->with('customer') + ->willReturn($postValue['customer']); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') ->willReturnMap( [ - ['customer', null, $postValue['customer']], - ['subscription', null, $subscription], + ['subscription_status', null, [$subscriptionWebsite => $subscriptionStatus]], + ['subscription_store', null, [$subscriptionWebsite => $subscriptionStore]], + ['back', false, true], ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->atLeastOnce()) @@ -371,7 +419,7 @@ public function testExecuteWithExistentCustomer() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->once()) ->method('extractData') @@ -400,32 +448,26 @@ public function testExecuteWithExistentCustomer() ] ); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ - $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class - )->disableOriginalConstructor()->getMock(); - + /** @var CustomerInterface|MockObject $customerMock */ + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->method('getId')->willReturn($customerId); $this->customerDataFactoryMock->expects($this->once()) ->method('create') ->willReturn($customerMock); - - $this->customerRepositoryMock->expects($this->exactly(2)) - ->method('getById') + $this->customerRepositoryMock->method('getById') ->with($customerId) ->willReturn($customerMock); - $this->customerMapperMock->expects($this->exactly(2)) ->method('toFlatArray') ->with($customerMock) ->willReturn($savedData); - $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ [ $customerMock, - $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, + $mergedData, CustomerInterface::class, $this->dataHelperMock ], ] @@ -435,10 +477,7 @@ public function testExecuteWithExistentCustomer() ->method('save') ->with($customerMock) ->willReturnSelf(); - - $customerEmail = 'customer@email.com'; $customerMock->expects($this->once())->method('getEmail')->willReturn($customerEmail); - $customerMock->expects($this->once()) ->method('getAddresses') ->willReturn([]); @@ -453,25 +492,12 @@ public function testExecuteWithExistentCustomer() ->with(null) ->willReturn(true); - /** @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject $subscriberMock */ - $subscriberMock = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->subscriberFactoryMock->expects($this->once()) - ->method('create') - ->with() - ->willReturn($subscriberMock); - - $subscriberMock->expects($this->once()) - ->method('subscribeCustomerById') - ->with($customerId); - $subscriberMock->expects($this->never()) - ->method('unsubscribeCustomerById'); + $this->subscriptionManager->expects($this->once()) + ->method($subscriptionStatus ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $subscriptionStore); $this->sessionMock->expects($this->once()) ->method('unsCustomerFormData'); - $this->registryMock->expects($this->once()) ->method('register') ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); @@ -481,13 +507,8 @@ public function testExecuteWithExistentCustomer() ->with(__('You saved the customer.')) ->willReturnSelf(); - $this->requestMock->expects($this->once()) - ->method('getParam') - ->with('back', false) - ->willReturn(true); - - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -511,14 +532,17 @@ public function testExecuteWithExistentCustomer() public function testExecuteWithNewCustomer() { $customerId = 22; + $subscriptionWebsite = 1; + $subscriptionStatus = false; + $subscriptionStore = 3; - $subscription = '0'; $postValue = [ 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'subscription' => $subscription, + 'subscription_status' => [$subscriptionWebsite => $subscriptionStatus], + 'subscription_store' => [$subscriptionWebsite => $subscriptionStore], ]; $extractedData = [ 'coolness' => false, @@ -530,9 +554,9 @@ public function testExecuteWithNewCustomer() CustomerInterface::DEFAULT_SHIPPING => null, 'confirmation' => false, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') @@ -552,15 +576,20 @@ public function testExecuteWithNewCustomer() ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') + ->with('customer') + ->willReturn($postValue['customer']); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') ->willReturnMap( [ - ['customer', null, $postValue['customer']], - ['subscription', null, $subscription], + ['subscription_status', null, [$subscriptionWebsite => $subscriptionStatus]], + ['subscription_store', null, [$subscriptionWebsite => $subscriptionStore]], + ['back', false, false], ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->atLeastOnce()) @@ -577,7 +606,7 @@ public function testExecuteWithNewCustomer() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->once()) ->method('extractData') @@ -607,60 +636,36 @@ public function testExecuteWithNewCustomer() ] ); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ - $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class - )->disableOriginalConstructor()->getMock(); - + /** @var CustomerInterface|MockObject $customerMock */ + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->method('getId')->willReturn($customerId); $this->customerDataFactoryMock->expects($this->once()) ->method('create') ->willReturn($customerMock); - $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ [ $customerMock, - $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, + $mergedData, CustomerInterface::class, $this->dataHelperMock ], ] ); - $this->managementMock->expects($this->once()) ->method('createAccount') ->with($customerMock, null, '') ->willReturn($customerMock); - - $customerMock->expects($this->once()) - ->method('getId') - ->willReturn($customerId); - $this->authorizationMock->expects($this->once()) ->method('isAllowed') ->with(null) ->willReturn(true); - - /** @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject $subscriberMock */ - $subscriberMock = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->subscriberFactoryMock->expects($this->once()) - ->method('create') - ->with() - ->willReturn($subscriberMock); - - $subscriberMock->expects($this->once()) - ->method('unsubscribeCustomerById') - ->with($customerId); - $subscriberMock->expects($this->never()) - ->method('subscribeCustomerById'); - + $this->subscriptionManager->expects($this->once()) + ->method($subscriptionStatus ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $subscriptionStore); $this->sessionMock->expects($this->once()) ->method('unsCustomerFormData'); - $this->registryMock->expects($this->once()) ->method('register') ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); @@ -670,13 +675,8 @@ public function testExecuteWithNewCustomer() ->with(__('You saved the customer.')) ->willReturnSelf(); - $this->requestMock->expects($this->once()) - ->method('getParam') - ->with('back', false) - ->willReturn(false); - - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -713,9 +713,9 @@ public function testExecuteWithNewCustomerAndValidationException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -741,8 +741,8 @@ public function testExecuteWithNewCustomerAndValidationException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -756,7 +756,7 @@ public function testExecuteWithNewCustomerAndValidationException() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -780,9 +780,9 @@ public function testExecuteWithNewCustomerAndValidationException() Form::DONT_IGNORE_INVISIBLE )->willReturn($customerFormMock); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|MockObject $customerMock */ $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -814,7 +814,7 @@ public function testExecuteWithNewCustomerAndValidationException() $this->messageManagerMock->expects($this->once()) ->method('addMessage') - ->with(new \Magento\Framework\Message\Error('Validator Exception')); + ->with(new Error('Validator Exception')); $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') @@ -825,8 +825,8 @@ public function testExecuteWithNewCustomerAndValidationException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -864,9 +864,9 @@ public function testExecuteWithNewCustomerAndLocalizedException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -892,8 +892,8 @@ public function testExecuteWithNewCustomerAndLocalizedException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -906,9 +906,9 @@ public function testExecuteWithNewCustomerAndLocalizedException() ->with(['data' => $postValue]) ->willReturn($objectMock); - /** @var Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var Form|MockObject $formMock */ $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -933,7 +933,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() )->willReturn($customerFormMock); $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -943,7 +943,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->managementMock->expects($this->once()) ->method('createAccount') ->with($customerMock, null, '') - ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('Localized Exception'))); + ->willThrowException(new LocalizedException(__('Localized Exception'))); $customerMock->expects($this->never()) ->method('getId'); @@ -965,7 +965,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->messageManagerMock->expects($this->once()) ->method('addMessage') - ->with(new \Magento\Framework\Message\Error('Localized Exception')); + ->with(new Error('Localized Exception')); $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') @@ -976,8 +976,8 @@ public function testExecuteWithNewCustomerAndLocalizedException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -1015,9 +1015,9 @@ public function testExecuteWithNewCustomerAndException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -1043,8 +1043,8 @@ public function testExecuteWithNewCustomerAndException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -1058,7 +1058,7 @@ public function testExecuteWithNewCustomerAndException() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -1082,9 +1082,9 @@ public function testExecuteWithNewCustomerAndException() Form::DONT_IGNORE_INVISIBLE )->willReturn($customerFormMock); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|MockObject $customerMock */ $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -1128,8 +1128,8 @@ public function testExecuteWithNewCustomerAndException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml b/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml index 12d4902fb1892..d8bcc59689fac 100644 --- a/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml +++ b/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ ?> +<?php /** @var \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter $block */ ?> <div class="entry-edit"> <?= $block->getForm()->getHtml() ?> </div> diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php deleted file mode 100644 index 1acb418e7bba6..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer; - -use Magento\Newsletter\Model\SubscriberFactory; - -/** - * Change subscription status. Subscribe OR unsubscribe if required - */ -class ChangeSubscriptionStatus -{ - /** - * @var SubscriberFactory - */ - private $subscriberFactory; - - /** - * @param SubscriberFactory $subscriberFactory - */ - public function __construct( - SubscriberFactory $subscriberFactory - ) { - $this->subscriberFactory = $subscriberFactory; - } - - /** - * Change subscription status. Subscribe OR unsubscribe if required - * - * @param int $customerId - * @param bool $subscriptionStatus - * @return void - */ - public function execute(int $customerId, bool $subscriptionStatus): void - { - $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - - if ($subscriptionStatus === true && !$subscriber->isSubscribed()) { - $this->subscriberFactory->create()->subscribeCustomerById($customerId); - } elseif ($subscriptionStatus === false && $subscriber->isSubscribed()) { - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php index 969c205329f2b..a631b7ba86194 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php @@ -14,6 +14,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Api\Data\StoreInterface; /** @@ -36,11 +37,6 @@ class CreateCustomerAccount */ private $accountManagement; - /** - * @var ChangeSubscriptionStatus - */ - private $changeSubscriptionStatus; - /** * @var ValidateCustomerData */ @@ -51,30 +47,35 @@ class CreateCustomerAccount */ private $dataObjectProcessor; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * CreateCustomerAccount constructor. * * @param DataObjectHelper $dataObjectHelper * @param CustomerInterfaceFactory $customerFactory * @param AccountManagementInterface $accountManagement - * @param ChangeSubscriptionStatus $changeSubscriptionStatus * @param DataObjectProcessor $dataObjectProcessor * @param ValidateCustomerData $validateCustomerData + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( DataObjectHelper $dataObjectHelper, CustomerInterfaceFactory $customerFactory, AccountManagementInterface $accountManagement, - ChangeSubscriptionStatus $changeSubscriptionStatus, DataObjectProcessor $dataObjectProcessor, - ValidateCustomerData $validateCustomerData + ValidateCustomerData $validateCustomerData, + SubscriptionManagerInterface $subscriptionManager ) { $this->dataObjectHelper = $dataObjectHelper; $this->customerFactory = $customerFactory; $this->accountManagement = $accountManagement; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; $this->validateCustomerData = $validateCustomerData; $this->dataObjectProcessor = $dataObjectProcessor; + $this->subscriptionManager = $subscriptionManager; } /** @@ -94,7 +95,11 @@ public function execute(array $data, StoreInterface $store): CustomerInterface } if (isset($data['is_subscribed'])) { - $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + if ((bool)$data['is_subscribed']) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } } return $customer; } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php index f7bf26513dc2e..d82b8c6f941fa 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php @@ -14,6 +14,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Api\DataObjectHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Api\Data\StoreInterface; /** @@ -38,11 +39,6 @@ class UpdateCustomerAccount */ private $dataObjectHelper; - /** - * @var ChangeSubscriptionStatus - */ - private $changeSubscriptionStatus; - /** * @var ValidateCustomerData */ @@ -53,28 +49,33 @@ class UpdateCustomerAccount */ private $restrictedKeys; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * @param SaveCustomer $saveCustomer * @param CheckCustomerPassword $checkCustomerPassword * @param DataObjectHelper $dataObjectHelper - * @param ChangeSubscriptionStatus $changeSubscriptionStatus * @param ValidateCustomerData $validateCustomerData + * @param SubscriptionManagerInterface $subscriptionManager * @param array $restrictedKeys */ public function __construct( SaveCustomer $saveCustomer, CheckCustomerPassword $checkCustomerPassword, DataObjectHelper $dataObjectHelper, - ChangeSubscriptionStatus $changeSubscriptionStatus, ValidateCustomerData $validateCustomerData, + SubscriptionManagerInterface $subscriptionManager, array $restrictedKeys = [] ) { $this->saveCustomer = $saveCustomer; $this->checkCustomerPassword = $checkCustomerPassword; $this->dataObjectHelper = $dataObjectHelper; $this->restrictedKeys = $restrictedKeys; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; $this->validateCustomerData = $validateCustomerData; + $this->subscriptionManager = $subscriptionManager; } /** @@ -112,7 +113,11 @@ public function execute(CustomerInterface $customer, array $data, StoreInterface $this->saveCustomer->execute($customer); if (isset($data['is_subscribed'])) { - $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + if ((bool)$data['is_subscribed']) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } } } } diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index d7d511e2d1906..01012e39a992a 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriptionManagerInterface; /** * Customers newsletter subscription save controller @@ -34,9 +35,9 @@ class Save extends \Magento\Newsletter\Controller\Manage implements HttpPostActi protected $customerRepository; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; /** * Initialize dependencies. @@ -46,7 +47,7 @@ class Save extends \Magento\Newsletter\Controller\Manage implements HttpPostActi * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param CustomerRepository $customerRepository - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( \Magento\Framework\App\Action\Context $context, @@ -54,13 +55,13 @@ public function __construct( \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator, \Magento\Store\Model\StoreManagerInterface $storeManager, CustomerRepository $customerRepository, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager ) { $this->storeManager = $storeManager; $this->formKeyValidator = $formKeyValidator; $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; parent::__construct($context, $customerSession); + $this->subscriptionManager = $subscriptionManager; } /** @@ -80,28 +81,24 @@ public function execute() } else { try { $customer = $this->customerRepository->getById($customerId); - $storeId = $this->storeManager->getStore()->getId(); + $storeId = (int)$this->storeManager->getStore()->getId(); $customer->setStoreId($storeId); - $isSubscribedState = $customer->getExtensionAttributes() - ->getIsSubscribed(); - $isSubscribedParam = (boolean)$this->getRequest() - ->getParam('is_subscribed', false); + $isSubscribedState = $customer->getExtensionAttributes()->getIsSubscribed(); + $isSubscribedParam = (boolean)$this->getRequest()->getParam('is_subscribed', false); if ($isSubscribedParam !== $isSubscribedState) { // No need to validate customer and customer address while saving subscription preferences $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); if ($isSubscribedParam) { - $subscribeModel = $this->subscriberFactory->create() - ->subscribeCustomerById($customerId); - $subscribeStatus = $subscribeModel->getStatus(); - if ($subscribeStatus == Subscriber::STATUS_SUBSCRIBED) { + $subscribeModel = $this->subscriptionManager->subscribeCustomer((int)$customerId, $storeId); + $subscribeStatus = (int)$subscribeModel->getStatus(); + if ($subscribeStatus === Subscriber::STATUS_SUBSCRIBED) { $this->messageManager->addSuccess(__('We have saved your subscription.')); } else { $this->messageManager->addSuccess(__('A confirmation request has been sent.')); } } else { - $this->subscriberFactory->create() - ->unsubscribeCustomerById($customerId); + $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); $this->messageManager->addSuccess(__('We have removed your newsletter subscription.')); } } else { diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php index 7557f1610b4f4..ea52ae8aaa864 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php @@ -4,7 +4,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Newsletter\Controller\Subscriber; use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; @@ -14,11 +13,14 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Phrase; use Magento\Framework\Validator\EmailAddress as EmailValidator; use Magento\Newsletter\Controller\Subscriber as SubscriberController; use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Newsletter\Model\SubscriberFactory; @@ -40,6 +42,11 @@ class NewAction extends SubscriberController implements HttpPostActionInterface */ private $emailValidator; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * Initialize dependencies. * @@ -49,6 +56,7 @@ class NewAction extends SubscriberController implements HttpPostActionInterface * @param StoreManagerInterface $storeManager * @param CustomerUrl $customerUrl * @param CustomerAccountManagement $customerAccountManagement + * @param SubscriptionManagerInterface $subscriptionManager * @param EmailValidator $emailValidator */ public function __construct( @@ -58,9 +66,11 @@ public function __construct( StoreManagerInterface $storeManager, CustomerUrl $customerUrl, CustomerAccountManagement $customerAccountManagement, + SubscriptionManagerInterface $subscriptionManager, EmailValidator $emailValidator = null ) { $this->customerAccountManagement = $customerAccountManagement; + $this->subscriptionManager = $subscriptionManager; $this->emailValidator = $emailValidator ?: ObjectManager::getInstance()->get(EmailValidator::class); parent::__construct( $context, @@ -132,7 +142,7 @@ protected function validateEmailFormat($email) /** * New subscription action * - * @return \Magento\Framework\Controller\Result\Redirect + * @return Redirect */ public function execute() { @@ -144,29 +154,55 @@ public function execute() $this->validateGuestSubscription(); $this->validateEmailAvailable($email); - $subscriber = $this->_subscriberFactory->create()->loadByEmail($email); + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + /** @var Subscriber $subscriber */ + $subscriber = $this->_subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); if ($subscriber->getId() - && (int) $subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED - ) { + && (int)$subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED) { throw new LocalizedException( __('This email address is already subscribed.') ); } - $status = (int) $this->_subscriberFactory->create()->subscribe($email); - $this->messageManager->addSuccessMessage($this->getSuccessMessage($status)); + $storeId = (int)$this->_storeManager->getStore()->getId(); + $currentCustomerId = $this->getSessionCustomerId($email); + $subscriber = $currentCustomerId + ? $this->subscriptionManager->subscribeCustomer($currentCustomerId, $storeId) + : $this->subscriptionManager->subscribe($email, $storeId); + $message = $this->getSuccessMessage((int)$subscriber->getSubscriberStatus()); + $this->messageManager->addSuccessMessage($message); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong with the subscription.')); } } - /** @var \Magento\Framework\Controller\Result\Redirect $redirect */ - $redirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); + /** @var Redirect $redirect */ + $redirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $redirectUrl = $this->_redirect->getRedirectUrl(); return $redirect->setUrl($redirectUrl); } + /** + * Get customer id from session if he is owner of the email + * + * @param string $email + * @return int|null + */ + private function getSessionCustomerId(string $email): ?int + { + if (!$this->_customerSession->isLoggedIn()) { + return null; + } + + $customer = $this->_customerSession->getCustomerDataObject(); + if ($customer->getEmail() !== $email) { + return null; + } + + return (int)$this->_customerSession->getId(); + } + /** * Get success message * diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 309bfadab41b3..59e67e7aa32a7 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -5,12 +5,19 @@ */ namespace Magento\Newsletter\Model\Plugin; -use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Customer\Model\Config\Share; use Magento\Framework\Api\ExtensionAttributesFactory; -use Magento\Newsletter\Model\ResourceModel\Subscriber; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; use Magento\Customer\Api\Data\CustomerExtensionInterface; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; /** * Newsletter Plugin for customer @@ -18,42 +25,70 @@ class CustomerPlugin { /** - * Factory used for manipulating newsletter subscriptions - * - * @var SubscriberFactory + * @var ExtensionAttributesFactory */ - private $subscriberFactory; + private $extensionFactory; /** - * @var ExtensionAttributesFactory + * @var CollectionFactory */ - private $extensionFactory; + private $collectionFactory; + + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; /** - * @var Subscriber + * @var Share */ - private $subscriberResource; + private $shareConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; /** * @var array */ - private $customerSubscriptionStatus = []; + private $customerSubscriber = []; + + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @var LoggerInterface + */ + private $logger; /** - * Initialize dependencies. - * * @param SubscriberFactory $subscriberFactory * @param ExtensionAttributesFactory $extensionFactory - * @param Subscriber $subscriberResource + * @param CollectionFactory $collectionFactory + * @param SubscriptionManagerInterface $subscriptionManager + * @param Share $shareConfig + * @param StoreManagerInterface $storeManager + * @param LoggerInterface $logger */ public function __construct( SubscriberFactory $subscriberFactory, ExtensionAttributesFactory $extensionFactory, - Subscriber $subscriberResource + CollectionFactory $collectionFactory, + SubscriptionManagerInterface $subscriptionManager, + Share $shareConfig, + StoreManagerInterface $storeManager, + LoggerInterface $logger ) { $this->subscriberFactory = $subscriberFactory; $this->extensionFactory = $extensionFactory; - $this->subscriberResource = $subscriberResource; + $this->collectionFactory = $collectionFactory; + $this->subscriptionManager = $subscriptionManager; + $this->shareConfig = $shareConfig; + $this->storeManager = $storeManager; + $this->logger = $logger; } /** @@ -61,128 +96,228 @@ public function __construct( * * If we have extension attribute (is_subscribed) we need to subscribe that customer * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param CustomerInterface $result * @param CustomerInterface $customer * @return CustomerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave(CustomerRepository $subject, CustomerInterface $result, CustomerInterface $customer) - { - $resultId = $result->getId(); - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->subscriberFactory->create(); + public function afterSave( + CustomerRepositoryInterface $subject, + CustomerInterface $result, + CustomerInterface $customer + ) { + $subscriber = $this->getSubscriber($result); + $subscribeStatus = $this->getIsSubscribedFromExtensionAttr($customer) ?? $subscriber->isSubscribed(); + $needToUpdate = $this->isSubscriptionChanged($result, $subscriber, $subscribeStatus); - $subscriber->updateSubscription($resultId); - // update the result only if the original customer instance had different value. - $initialExtensionAttributes = $result->getExtensionAttributes(); - if ($initialExtensionAttributes === null) { - /** @var CustomerExtensionInterface $initialExtensionAttributes */ - $initialExtensionAttributes = $this->extensionFactory->create(CustomerInterface::class); - $result->setExtensionAttributes($initialExtensionAttributes); + /** + * If subscriber is waiting to confirm customer registration + * and customer is already confirmed registration + * than need to subscribe customer + */ + if ($subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED && empty($result->getConfirmation())) { + $needToUpdate = true; + $subscribeStatus = true; } + if ($needToUpdate) { + $storeId = $this->getCurrentStoreId($result); + $subscriber = $subscribeStatus + ? $this->subscriptionManager->subscribeCustomer((int)$result->getId(), $storeId) + : $this->subscriptionManager->unsubscribeCustomer((int)$result->getId(), $storeId); + $this->customerSubscriber[(int)$result->getId()] = $subscriber; + } + $this->addIsSubscribedExtensionAttr($result, $subscriber->isSubscribed()); + + return $result; + } + /** + * Get subscription status from extension customer attribute + * + * @param CustomerInterface $customer + * @return bool|null + */ + private function getIsSubscribedFromExtensionAttr(CustomerInterface $customer): ?bool + { $newExtensionAttributes = $customer->getExtensionAttributes(); - if ($newExtensionAttributes - && $initialExtensionAttributes->getIsSubscribed() !== $newExtensionAttributes->getIsSubscribed() - ) { - if ($newExtensionAttributes->getIsSubscribed()) { - $subscriber->subscribeCustomerById($resultId); - } else { - $subscriber->unsubscribeCustomerById($resultId); - } + if ($newExtensionAttributes === null || $newExtensionAttributes->getIsSubscribed() === null) { + return null; } - $isSubscribed = $subscriber->isSubscribed(); - $this->customerSubscriptionStatus[$resultId] = $isSubscribed; - $initialExtensionAttributes->setIsSubscribed($isSubscribed); + return (bool)$newExtensionAttributes->getIsSubscribed(); + } - return $result; + /** + * Get is customer subscription changed + * + * @param CustomerInterface $customer + * @param Subscriber $subscriber + * @param bool $newStatus + * @return bool + */ + private function isSubscriptionChanged(CustomerInterface $customer, Subscriber $subscriber, bool $newStatus): bool + { + if ($subscriber->isSubscribed() !== $newStatus) { + return true; + } + + if (!$subscriber->getId()) { + return false; + } + + /** + * If customer has changed email or subscriber was loaded by email + * than need to update customer subscription + */ + return $customer->getEmail() !== $subscriber->getEmail() || (int)$subscriber->getCustomerId() === 0; } /** * Plugin around delete customer that updates any newsletter subscription that may have existed. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param callable $deleteCustomerById Function we are wrapping around * @param int $customerId Input to the function * @return bool */ public function aroundDeleteById( - CustomerRepository $subject, + CustomerRepositoryInterface $subject, callable $deleteCustomerById, $customerId ) { $customer = $subject->getById($customerId); $result = $deleteCustomerById($customerId); - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->subscriberFactory->create(); - $subscriber->loadByEmail($customer->getEmail()); - if ($subscriber->getId()) { - $subscriber->delete(); - } + $this->deleteSubscriptionsAfterCustomerDelete($customer); + return $result; } /** * Plugin after delete customer that updates any newsletter subscription that may have existed. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param bool $result * @param CustomerInterface $customer * @return bool * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterDelete(CustomerRepository $subject, $result, CustomerInterface $customer) + public function afterDelete(CustomerRepositoryInterface $subject, $result, CustomerInterface $customer) { - $subscriber = $this->subscriberFactory->create(); - $subscriber->loadByEmail($customer->getEmail()); - if ($subscriber->getId()) { - $subscriber->delete(); - } + $this->deleteSubscriptionsAfterCustomerDelete($customer); return $result; } /** * Plugin after getById customer that obtains newsletter subscription status for given customer. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param CustomerInterface $customer * @return CustomerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetById(CustomerRepository $subject, CustomerInterface $customer) + public function afterGetById(CustomerRepositoryInterface $subject, CustomerInterface $customer) { - $extensionAttributes = $customer->getExtensionAttributes(); + $isSubscribed = $this->getSubscriber($customer)->isSubscribed(); + $this->addIsSubscribedExtensionAttr($customer, $isSubscribed); + + return $customer; + } + /** + * Set Is Subscribed extension attribute + * + * @param CustomerInterface $customer + * @param bool $isSubscribed + */ + private function addIsSubscribedExtensionAttr(CustomerInterface $customer, bool $isSubscribed): void + { + $extensionAttributes = $customer->getExtensionAttributes(); if ($extensionAttributes === null) { /** @var CustomerExtensionInterface $extensionAttributes */ $extensionAttributes = $this->extensionFactory->create(CustomerInterface::class); $customer->setExtensionAttributes($extensionAttributes); } - if ($extensionAttributes->getIsSubscribed() === null) { - $isSubscribed = $this->isSubscribed($customer); - $extensionAttributes->setIsSubscribed($isSubscribed); + $extensionAttributes->setIsSubscribed($isSubscribed); + } + + /** + * Delete customer subscriptions + * + * @param CustomerInterface $customer + * @return void + */ + private function deleteSubscriptionsAfterCustomerDelete(CustomerInterface $customer): void + { + $collection = $this->collectionFactory->create(); + $collection->addFieldToFilter('subscriber_email', $customer->getEmail()); + if ($this->shareConfig->isWebsiteScope()) { + try { + $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); + $collection->addFieldToFilter('store_id', ['in' => $storeIds]); + } catch (NoSuchEntityException $exception) { + $this->logger->error($exception); + } + } + /** @var Subscriber $subscriber */ + foreach ($collection as $subscriber) { + $subscriber->delete(); } + } - return $customer; + /** + * Get Subscriber model by customer + * + * @param CustomerInterface $customer + * @return Subscriber + */ + private function getSubscriber(CustomerInterface $customer): Subscriber + { + $customerId = (int)$customer->getId(); + if (isset($this->customerSubscriber[$customerId])) { + return $this->customerSubscriber[$customerId]; + } + + /** @var Subscriber $subscriber */ + $subscriber = $this->subscriberFactory->create(); + $websiteId = $this->getCurrentWebsiteId($customer); + $subscriber->loadByCustomer((int)$customer->getId(), $websiteId); + /** + * If subscriber was't found by customer id then try to find subscriber by customer email. + * It need when the customer is creating and he has already subscribed as guest by same email. + */ + if (!$subscriber->getId()) { + $subscriber->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + $this->customerSubscriber[$customerId] = $subscriber; + + return $subscriber; } /** - * This method returns newsletters subscription status for given customer. + * Retrieve current website id * * @param CustomerInterface $customer - * @return bool + * @return int + */ + private function getCurrentWebsiteId(CustomerInterface $customer): int + { + return (int)$this->storeManager->getStore($this->getCurrentStoreId($customer))->getWebsiteId(); + } + + /** + * Retrieve current store id + * + * @param CustomerInterface $customer + * @return int */ - private function isSubscribed(CustomerInterface $customer) + private function getCurrentStoreId(CustomerInterface $customer): int { - $customerId = $customer->getId(); - if (!isset($this->customerSubscriptionStatus[$customerId])) { - $subscriber = $this->subscriberResource->loadByCustomerData($customer); - $this->customerSubscriptionStatus[$customerId] = isset($subscriber['subscriber_status']) - && $subscriber['subscriber_status'] == 1; + $storeId = (int)$this->storeManager->getStore()->getId(); + if ($storeId === Store::DEFAULT_STORE_ID) { + $storeId = (int)$customer->getStoreId(); } - return $this->customerSubscriptionStatus[$customerId]; + return $storeId; } } diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php index 52009dad6614b..1fc68771e74cb 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php @@ -208,6 +208,31 @@ public function addSubscriberFilter($subscriberId) return $this; } + /** + * Set filter for queue by customer + * + * @param int $customerId + * @return $this + */ + public function addCustomerFilter(int $customerId): self + { + $this->getSelect() + ->join( + ['link' => $this->getTable('newsletter_queue_link')], + 'main_table.queue_id=link.queue_id', + ['letter_sent_at'] + )->join( + ['subscriber' => $this->getTable('newsletter_subscriber')], + 'link.subscriber_id=subscriber.subscriber_id', + ['subscriber_store_id' => 'subscriber.store_id'] + )->where( + 'subscriber.customer_id = ?', + $customerId + ); + + return $this; + } + /** * Add filter by only ready for sending item * diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index 8ca489d89c1df..6391219e23c7e 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -5,23 +5,30 @@ */ namespace Magento\Newsletter\Model\ResourceModel; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Math\Random; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Newsletter\Model\Subscriber as SubscriberModel; use Magento\Store\Model\StoreManagerInterface; /** * Newsletter subscriber resource model * - * @author Magento Core Team <core@magentocommerce.com> - * + * @author Magento Core Team <core@magentocommerce.com> * @api * @since 100.0.2 */ -class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Subscriber extends AbstractDb { /** * DB connection * - * @var \Magento\Framework\DB\Adapter\AdapterInterface + * @var AdapterInterface */ protected $connection; @@ -42,12 +49,12 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * Date * - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ protected $_date; /** - * @var \Magento\Framework\Math\Random + * @var Random */ protected $mathRandom; @@ -61,16 +68,16 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * Construct * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Stdlib\DateTime\DateTime $date - * @param \Magento\Framework\Math\Random $mathRandom + * @param Context $context + * @param DateTime $date + * @param Random $mathRandom * @param string $connectionName * @param StoreManagerInterface $storeManager */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Stdlib\DateTime\DateTime $date, - \Magento\Framework\Math\Random $mathRandom, + Context $context, + DateTime $date, + Random $mathRandom, $connectionName = null, StoreManagerInterface $storeManager = null ) { @@ -104,65 +111,51 @@ public function setMessagesScope($scope) } /** - * Load subscriber from DB by email + * Load by subscriber email * - * @param string $subscriberEmail + * @param string $email + * @param int $websiteId * @return array */ - public function loadByEmail($subscriberEmail) + public function loadBySubscriberEmail(string $email, int $websiteId): array { - $select = $this->connection->select()->from($this->getMainTable())->where('subscriber_email=:subscriber_email'); - - $result = $this->connection->fetchRow($select, ['subscriber_email' => $subscriberEmail]); - - if (!$result) { + $storeIds = $this->storeManager->getWebsite($websiteId)->getStoreIds(); + $select = $this->connection->select() + ->from($this->getMainTable()) + ->where('subscriber_email = ?', $email) + ->where('store_id IN (?)', $storeIds) + ->limit(1); + + $data = $this->connection->fetchRow($select); + if (!$data) { return []; } - return $result; + return $data; } /** - * Load subscriber by customer + * Load by customer id * - * @param \Magento\Customer\Api\Data\CustomerInterface $customer + * @param int $customerId + * @param int $websiteId * @return array */ - public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer) + public function loadByCustomerId(int $customerId, int $websiteId): array { - $storeIds = $this->storeManager->getWebsite()->getStoreIds(); - - if ($customer->getId()) { - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('customer_id = ?', $customer->getId()) - ->where('store_id IN (?)', $storeIds) - ->limit(1); - - $result = $this->connection->fetchRow($select); - - if ($result) { - return $result; - } - } - - if ($customer->getEmail()) { - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('subscriber_email = ?', $customer->getEmail()) - ->where('store_id IN (?)', $storeIds) - ->limit(1); - - $result = $this->connection->fetchRow($select); - - if ($result) { - return $result; - } + $storeIds = $this->storeManager->getWebsite($websiteId)->getStoreIds(); + $select = $this->connection->select() + ->from($this->getMainTable()) + ->where('customer_id = ?', $customerId) + ->where('store_id IN (?)', $storeIds) + ->limit(1); + + $data = $this->connection->fetchRow($select); + if (!$data) { + return []; } - return []; + return $data; } /** @@ -178,12 +171,12 @@ protected function _generateRandomCode() /** * Updates data when subscriber received * - * @param \Magento\Newsletter\Model\Subscriber $subscriber + * @param SubscriberModel $subscriber * @param \Magento\Newsletter\Model\Queue $queue * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ - public function received(\Magento\Newsletter\Model\Subscriber $subscriber, \Magento\Newsletter\Model\Queue $queue) + public function received(SubscriberModel $subscriber, \Magento\Newsletter\Model\Queue $queue) { $this->connection->beginTransaction(); try { @@ -196,8 +189,41 @@ public function received(\Magento\Newsletter\Model\Subscriber $subscriber, \Mage $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); - throw new \Magento\Framework\Exception\LocalizedException(__('We cannot mark as received subscriber.')); + throw new LocalizedException(__('We cannot mark as received subscriber.')); } return $this; } + + /** + * Load subscriber from DB by email + * + * @param string $subscriberEmail + * @return array + * @deprecated The subscription should be loaded by website id + * @see loadBySubscriberEmail + */ + public function loadByEmail($subscriberEmail) + { + $websiteId = (int)$this->storeManager->getWebsite()->getId(); + return $this->loadBySubscriberEmail((string)$subscriberEmail, $websiteId); + } + + /** + * Load subscriber by customer + * + * @param CustomerInterface $customer + * @return array + * @deprecated The subscription should be loaded by website id + * @see loadByCustomerId + */ + public function loadByCustomerData(CustomerInterface $customer) + { + $websiteId = (int)$this->storeManager->getWebsite()->getId(); + $data = $this->loadByCustomerId((int)$customer->getId(), $websiteId); + if (empty($data)) { + $data = $this->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + + return $data; + } } diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index 5df9feacf654b..f33b9929435c3 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -9,9 +9,23 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\MailException; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Math\Random; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\Translate\Inline\StateInterface; +use Magento\Newsletter\Helper\Data; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Subscriber model @@ -33,12 +47,11 @@ * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) * * @api * @since 100.0.2 */ -class Subscriber extends \Magento\Framework\Model\AbstractModel +class Subscriber extends AbstractModel { const STATUS_SUBSCRIBED = 1; const STATUS_NOT_ACTIVE = 2; @@ -80,14 +93,14 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel /** * Newsletter data * - * @var \Magento\Newsletter\Helper\Data + * @var Data */ protected $_newsletterData = null; /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $_scopeConfig; @@ -100,14 +113,14 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel /** * Date - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ private $dateTime; /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; @@ -122,12 +135,12 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel protected $customerAccountManagement; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder + * @var TransportBuilder */ protected $_transportBuilder; /** - * @var \Magento\Framework\Translate\Inline\StateInterface + * @var StateInterface */ protected $inlineTranslation; @@ -142,51 +155,56 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel private $dataObjectHelper; /** - * Initialize dependencies. - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Newsletter\Helper\Data $newsletterData - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + + /** + * @param Context $context + * @param Registry $registry + * @param Data $newsletterData + * @param ScopeConfigInterface $scopeConfig + * @param TransportBuilder $transportBuilder + * @param StoreManagerInterface $storeManager * @param \Magento\Customer\Model\Session $customerSession * @param CustomerRepositoryInterface $customerRepository * @param AccountManagementInterface $customerAccountManagement - * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param StateInterface $inlineTranslation + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $data - * @param \Magento\Framework\Stdlib\DateTime\DateTime|null $dateTime + * @param DateTime|null $dateTime * @param CustomerInterfaceFactory|null $customerFactory * @param DataObjectHelper|null $dataObjectHelper + * @param SubscriptionManagerInterface|null $subscriptionManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Newsletter\Helper\Data $newsletterData, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder, - \Magento\Store\Model\StoreManagerInterface $storeManager, + Context $context, + Registry $registry, + Data $newsletterData, + ScopeConfigInterface $scopeConfig, + TransportBuilder $transportBuilder, + StoreManagerInterface $storeManager, \Magento\Customer\Model\Session $customerSession, CustomerRepositoryInterface $customerRepository, AccountManagementInterface $customerAccountManagement, - \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + StateInterface $inlineTranslation, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [], - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime = null, + DateTime $dateTime = null, CustomerInterfaceFactory $customerFactory = null, - DataObjectHelper $dataObjectHelper = null + DataObjectHelper $dataObjectHelper = null, + SubscriptionManagerInterface $subscriptionManager = null ) { $this->_newsletterData = $newsletterData; $this->_scopeConfig = $scopeConfig; $this->_transportBuilder = $transportBuilder; $this->_storeManager = $storeManager; $this->_customerSession = $customerSession; - $this->dateTime = $dateTime ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Stdlib\DateTime\DateTime::class + $this->dateTime = $dateTime ?: ObjectManager::getInstance()->get( + DateTime::class ); $this->customerFactory = $customerFactory ?: ObjectManager::getInstance() ->get(CustomerInterfaceFactory::class); @@ -195,6 +213,8 @@ public function __construct( $this->customerRepository = $customerRepository; $this->customerAccountManagement = $customerAccountManagement; $this->inlineTranslation = $inlineTranslation; + $this->subscriptionManager = $subscriptionManager ?: ObjectManager::getInstance() + ->get(SubscriptionManagerInterface::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -205,7 +225,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Newsletter\Model\ResourceModel\Subscriber::class); + $this->_init(ResourceModel\Subscriber::class); } /** @@ -357,51 +377,38 @@ public function isSubscribed() } /** - * Load subscriber data from resource model by email + * Load by subscriber email * - * @param string $subscriberEmail + * @param string $email + * @param int $websiteId * @return $this */ - public function loadByEmail($subscriberEmail) + public function loadBySubscriberEmail(string $email, int $websiteId): Subscriber { - $storeId = $this->_storeManager->getStore()->getId(); - $customerData = ['store_id' => $storeId, 'email'=> $subscriberEmail]; + /** @var ResourceModel\Subscriber $resource */ + $resource = $this->getResource(); + $data = $resource->loadBySubscriberEmail($email, $websiteId); + $this->addData($data); + $this->setOrigData(); - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerFactory->create(); - $this->dataObjectHelper->populateWithArray( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $this->addData($this->getResource()->loadByCustomerData($customer)); return $this; } /** - * Load subscriber info by customerId + * Load by customer id * * @param int $customerId + * @param int $websiteId * @return $this */ - public function loadByCustomerId($customerId) + public function loadByCustomer(int $customerId, int $websiteId): Subscriber { - try { - $customerData = $this->customerRepository->getById($customerId); - $customerData->setStoreId($this->_storeManager->getStore()->getId()); - if ($customerData->getWebsiteId() === null) { - $customerData->setWebsiteId($this->_storeManager->getStore()->getWebsiteId()); - } - $data = $this->getResource()->loadByCustomerData($customerData); - $this->addData($data); - if (!empty($data) && $customerData->getId() && !$this->getCustomerId()) { - $this->setCustomerId($customerData->getId()); - $this->setSubscriberConfirmCode($this->randomSequence()); - $this->save(); - } - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock - } catch (NoSuchEntityException $e) { - } + /** @var ResourceModel\Subscriber $resource */ + $resource = $this->getResource(); + $data = $resource->loadByCustomerId($customerId, $websiteId); + $this->addData($data); + $this->setOrigData(); + return $this; } @@ -418,95 +425,23 @@ public function randomSequence($length = 32) $char = array_merge(range('a', 'z'), range(0, 9)); $charLen = count($char) - 1; for ($i = 0; $i < $length; $i++) { - $disc = \Magento\Framework\Math\Random::getRandomNumber(0, $charLen); + $disc = Random::getRandomNumber(0, $charLen); $par[$i] = $char[$disc]; $id = $id . $char[$disc]; } return $id; } - /** - * Subscribes by email - * - * @param string $email - * @throws \Exception - * @return int - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function subscribe($email) - { - $this->loadByEmail($email); - - if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) { - return $this->getStatus(); - } - - if (!$this->getId()) { - $this->setSubscriberConfirmCode($this->randomSequence()); - } - - $isConfirmNeed = $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRMATION_FLAG, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == 1 ? true : false; - - $isSubscribeOwnEmail = $this->_customerSession->isLoggedIn() - && $this->_customerSession->getCustomerDataObject()->getEmail() == $email; - - if (!$this->getId() || $this->getStatus() == self::STATUS_UNSUBSCRIBED - || $this->getStatus() == self::STATUS_NOT_ACTIVE - ) { - if ($isConfirmNeed === true) { - $this->setStatus(self::STATUS_NOT_ACTIVE); - } else { - $this->setStatus(self::STATUS_SUBSCRIBED); - } - $this->setSubscriberEmail($email); - } - - if ($isSubscribeOwnEmail) { - try { - $customer = $this->customerRepository->getById($this->_customerSession->getCustomerId()); - $this->setStoreId($customer->getStoreId()); - $this->setCustomerId($customer->getId()); - } catch (NoSuchEntityException $e) { - $this->setStoreId($this->_storeManager->getStore()->getId()); - $this->setCustomerId(0); - } - } else { - $this->setStoreId($this->_storeManager->getStore()->getId()); - $this->setCustomerId(0); - } - - $this->setStatusChanged(true); - - try { - /* Save model before sending out email */ - $this->save(); - if ($isConfirmNeed === true) { - $this->sendConfirmationRequestEmail(); - } else { - $this->sendConfirmationSuccessEmail(); - } - return $this->getStatus(); - } catch (\Exception $e) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception($e->getMessage()); - } - } - /** * Unsubscribes loaded subscription * - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @return $this */ public function unsubscribe() { if ($this->hasCheckCode() && $this->getCode() != $this->getCheckCode()) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('This is an invalid subscription confirmation code.') ); } @@ -518,149 +453,6 @@ public function unsubscribe() return $this; } - /** - * Subscribe the customer with the id provided - * - * @param int $customerId - * @return $this - */ - public function subscribeCustomerById($customerId) - { - return $this->_updateCustomerSubscription($customerId, true); - } - - /** - * Unsubscribe the customer with the id provided - * - * @param int $customerId - * @return $this - */ - public function unsubscribeCustomerById($customerId) - { - return $this->_updateCustomerSubscription($customerId, false); - } - - /** - * Update the subscription based on latest information of associated customer. - * - * @param int $customerId - * @return $this - */ - public function updateSubscription($customerId) - { - $this->loadByCustomerId($customerId); - $this->_updateCustomerSubscription($customerId, $this->isSubscribed()); - return $this; - } - - /** - * Saving customer subscription status - * - * @param int $customerId - * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed - * @return $this - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function _updateCustomerSubscription($customerId, $subscribe) - { - try { - $customerData = $this->customerRepository->getById($customerId); - } catch (NoSuchEntityException $e) { - return $this; - } - - $this->loadByCustomerId($customerId); - if (!$subscribe && !$this->getId()) { - return $this; - } - - if (!$this->getId()) { - $this->setSubscriberConfirmCode($this->randomSequence()); - } - - $sendInformationEmail = false; - $status = self::STATUS_SUBSCRIBED; - $isConfirmNeed = $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRMATION_FLAG, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == 1 ? true : false; - if ($subscribe) { - if (AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED - == $this->customerAccountManagement->getConfirmationStatus($customerId) - ) { - if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) { - // if a customer was already subscribed then keep the subscribed - $status = self::STATUS_SUBSCRIBED; - } else { - $status = self::STATUS_UNCONFIRMED; - } - } elseif ($isConfirmNeed) { - if ($this->getStatus() != self::STATUS_SUBSCRIBED) { - $status = self::STATUS_NOT_ACTIVE; - } - } - } elseif (($this->getStatus() == self::STATUS_UNCONFIRMED) && ($customerData->getConfirmation() === null)) { - $status = self::STATUS_SUBSCRIBED; - $sendInformationEmail = true; - } elseif (($this->getStatus() == self::STATUS_NOT_ACTIVE) && ($customerData->getConfirmation() === null)) { - $status = self::STATUS_NOT_ACTIVE; - } else { - $status = self::STATUS_UNSUBSCRIBED; - } - /** - * If subscription status has been changed then send email to the customer - */ - if ($status != self::STATUS_UNCONFIRMED && $status != $this->getStatus()) { - $sendInformationEmail = true; - } - - if ($status != $this->getStatus()) { - $this->setStatusChanged(true); - } - - $this->setStatus($status); - - $storeId = $customerData->getStoreId(); - if ((int)$customerData->getStoreId() === 0) { - $storeId = $this->_storeManager->getWebsite($customerData->getWebsiteId())->getDefaultStore()->getId(); - } - - if (!$this->getId()) { - $this->setStoreId($storeId) - ->setCustomerId($customerData->getId()) - ->setEmail($customerData->getEmail()); - } else { - $this->setStoreId($storeId) - ->setEmail($customerData->getEmail()); - } - - $this->save(); - $sendSubscription = $sendInformationEmail; - if ($sendSubscription === null xor $sendSubscription && $this->isStatusChanged()) { - try { - switch ($status) { - case self::STATUS_UNSUBSCRIBED: - $this->sendUnsubscriptionEmail(); - break; - case self::STATUS_SUBSCRIBED: - $this->sendConfirmationSuccessEmail(); - break; - case self::STATUS_NOT_ACTIVE: - if ($isConfirmNeed) { - $this->sendConfirmationRequestEmail(); - } - break; - } - } catch (MailException $e) { - // If we are not able to send a new account email, this should be ignored - $this->_logger->critical($e); - } - } - return $this; - } - /** * Confirms subscriber newsletter * @@ -684,10 +476,10 @@ public function confirm($code) /** * Mark receiving subscriber of queue newsletter * - * @param \Magento\Newsletter\Model\Queue $queue + * @param Queue $queue * @return Subscriber */ - public function received(\Magento\Newsletter\Model\Queue $queue) + public function received(Queue $queue) { $this->getResource()->received($this, $queue); return $this; @@ -700,54 +492,13 @@ public function received(\Magento\Newsletter\Model\Queue $queue) */ public function sendConfirmationRequestEmail() { - if ($this->getImportMode()) { - return $this; - } - - if (!$this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; - } - - $this->inlineTranslation->suspend(); - - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->setTemplateOptions( - [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), - ] - )->setTemplateVars( - [ - 'subscriber' => $this, - 'store' => $this->_storeManager->getStore(), - 'subscriber_data' => [ - 'confirmation_link' => $this->getConfirmationLink(), - ], - ] - )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->addTo( - $this->getEmail(), - $this->getName() - ); - $transport = $this->_transportBuilder->getTransport(); - $transport->sendMessage(); - - $this->inlineTranslation->resume(); + $vars = [ + 'store' => $this->_storeManager->getStore($this->getStoreId()), + 'subscriber_data' => [ + 'confirmation_link' => $this->getConfirmationLink(), + ], + ]; + $this->sendEmail(self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, self::XML_PATH_CONFIRM_EMAIL_IDENTITY, $vars); return $this; } @@ -759,48 +510,7 @@ public function sendConfirmationRequestEmail() */ public function sendConfirmationSuccessEmail() { - if ($this->getImportMode()) { - return $this; - } - - if (!$this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; - } - - $this->inlineTranslation->suspend(); - - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->setTemplateOptions( - [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), - ] - )->setTemplateVars( - ['subscriber' => $this] - )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->addTo( - $this->getEmail(), - $this->getName() - ); - $transport = $this->_transportBuilder->getTransport(); - $transport->sendMessage(); - - $this->inlineTranslation->resume(); + $this->sendEmail(self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, self::XML_PATH_SUCCESS_EMAIL_IDENTITY); return $this; } @@ -811,40 +521,45 @@ public function sendConfirmationSuccessEmail() * @return $this */ public function sendUnsubscriptionEmail() + { + $this->sendEmail(self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY); + + return $this; + } + + /** + * Send email about change status + * + * @param string $emailTemplatePath + * @param string $emailIdentityPath + * @param array $templateVars + * @return void + */ + private function sendEmail(string $emailTemplatePath, string $emailIdentityPath, array $templateVars = []): void { if ($this->getImportMode()) { - return $this; + return; } - if (!$this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; + + $template = $this->_scopeConfig->getValue($emailTemplatePath, ScopeInterface::SCOPE_STORE, $this->getStoreId()); + $identity = $this->_scopeConfig->getValue($emailIdentityPath, ScopeInterface::SCOPE_STORE, $this->getStoreId()); + if (!$template || !$identity) { + return; } + $templateVars += ['subscriber' => $this]; $this->inlineTranslation->suspend(); - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) + $template )->setTemplateOptions( [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), + 'area' => Area::AREA_FRONTEND, + 'store' => $this->getStoreId(), ] )->setTemplateVars( - ['subscriber' => $this] + $templateVars )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) + $identity )->addTo( $this->getEmail(), $this->getName() @@ -853,8 +568,6 @@ public function sendUnsubscriptionEmail() $transport->sendMessage(); $this->inlineTranslation->resume(); - - return $this; } /** @@ -884,4 +597,125 @@ public function beforeSave() } return $this; } + + /** + * Load subscriber data from resource model by email + * + * @param string $subscriberEmail + * @return $this + * @deprecated The subscription should be loaded by website id + * @see loadBySubscriberEmail + */ + public function loadByEmail($subscriberEmail) + { + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + $this->loadBySubscriberEmail($subscriberEmail, $websiteId); + + return $this; + } + + /** + * Load subscriber info by customerId + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be loaded by website id + * @see loadByCustomer + */ + public function loadByCustomerId($customerId) + { + try { + $customer = $this->customerRepository->getById($customerId); + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + $this->loadByCustomer((int)$customerId, $websiteId); + if ($customer->getId() && !$this->getCustomerId()) { + $this->setCustomerId($customer->getId()); + $this->setSubscriberConfirmCode($this->randomSequence()); + $this->save(); + } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (NoSuchEntityException $e) { + } + return $this; + } + + /** + * Subscribes by email + * + * @param string $email + * @return int + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribe + */ + public function subscribe($email) + { + $storeId = (int)$this->_storeManager->getStore()->getId(); + $subscriber = $this->subscriptionManager->subscribe($email, $storeId); + $this->addData($subscriber->getData()); + + return $this->getStatus(); + } + + /** + * Subscribe the customer with the id provided + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + public function subscribeCustomerById($customerId) + { + return $this->_updateCustomerSubscription($customerId, true); + } + + /** + * Unsubscribe the customer with the id provided + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::unsubscribeCustomer + */ + public function unsubscribeCustomerById($customerId) + { + return $this->_updateCustomerSubscription($customerId, false); + } + + /** + * Update the subscription based on latest information of associated customer. + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + public function updateSubscription($customerId) + { + $this->loadByCustomerId($customerId); + $this->_updateCustomerSubscription($customerId, $this->isSubscribed()); + return $this; + } + + /** + * Saving customer subscription status + * + * @param int $customerId + * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + protected function _updateCustomerSubscription($customerId, $subscribe) + { + $storeId = (int)$this->_storeManager->getStore()->getId(); + if ($subscribe) { + $subscriber = $this->subscriptionManager->subscribeCustomer((int)$customerId, $storeId); + } else { + $subscriber = $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); + } + $this->addData($subscriber->getData()); + + return $this; + } } diff --git a/app/code/Magento/Newsletter/Model/SubscriptionManager.php b/app/code/Magento/Newsletter/Model/SubscriptionManager.php new file mode 100644 index 0000000000000..846d095625e0c --- /dev/null +++ b/app/code/Magento/Newsletter/Model/SubscriptionManager.php @@ -0,0 +1,314 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Model; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\MailException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; + +/** + * Class to update newsletter subscription status + */ +class SubscriptionManager implements SubscriptionManagerInterface +{ + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var AccountManagementInterface + */ + private $customerAccountManagement; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param SubscriberFactory $subscriberFactory + * @param LoggerInterface $logger + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig + * @param AccountManagementInterface $customerAccountManagement + * @param CustomerRepositoryInterface $customerRepository + */ + public function __construct( + SubscriberFactory $subscriberFactory, + LoggerInterface $logger, + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig, + AccountManagementInterface $customerAccountManagement, + CustomerRepositoryInterface $customerRepository + ) { + $this->subscriberFactory = $subscriberFactory; + $this->logger = $logger; + $this->storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; + $this->customerAccountManagement = $customerAccountManagement; + $this->customerRepository = $customerRepository; + } + + /** + * @inheritdoc + */ + public function subscribe(string $email, int $storeId): Subscriber + { + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + $subscriber = $this->subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); + $currentStatus = (int)$subscriber->getStatus(); + if ($currentStatus === Subscriber::STATUS_SUBSCRIBED) { + return $subscriber; + } + + $status = $this->isConfirmNeed($storeId) ? Subscriber::STATUS_NOT_ACTIVE : Subscriber::STATUS_SUBSCRIBED; + if (!$subscriber->getId()) { + $subscriber->setSubscriberConfirmCode($subscriber->randomSequence()); + $subscriber->setSubscriberEmail($email); + } + $subscriber->setStatus($status) + ->setStoreId($storeId) + ->save(); + + $this->sendEmailAfterChangeStatus($subscriber); + + return $subscriber; + } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email, int $storeId, string $confirmCode): Subscriber + { + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + /** @var Subscriber $subscriber */ + $subscriber = $this->subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); + if (!$subscriber->getId()) { + return $subscriber; + } + $subscriber->setCheckCode($confirmCode); + $subscriber->unsubscribe(); + + return $subscriber; + } + + /** + * @inheritdoc + */ + public function subscribeCustomer(int $customerId, int $storeId): Subscriber + { + return $this->updateCustomerSubscription($customerId, $storeId, true); + } + + /** + * @inheritdoc + */ + public function unsubscribeCustomer(int $customerId, int $storeId): Subscriber + { + return $this->updateCustomerSubscription($customerId, $storeId, false); + } + + /** + * Update customer newsletter subscription + * + * @param int $customerId + * @param int $storeId + * @param bool $status + * @return Subscriber + */ + private function updateCustomerSubscription(int $customerId, int $storeId, bool $status): Subscriber + { + $customer = $this->customerRepository->getById($customerId); + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + $subscriber = $this->loadSubscriberByCustomer($customer, $websiteId); + if (!$status && !$subscriber->getId()) { + return $subscriber; + } + + $newStatus = $this->getNewSubscriptionStatus($subscriber, $customer, $storeId, $status); + $needToSendLetter = $this->saveSubscriber($subscriber, $customer, $storeId, $newStatus); + if ($needToSendLetter) { + $this->sendEmailAfterChangeStatus($subscriber); + } + + return $subscriber; + } + + /** + * Load subscriber model by customer + * + * @param CustomerInterface $customer + * @param int $websiteId + * @return Subscriber + */ + private function loadSubscriberByCustomer(CustomerInterface $customer, int $websiteId): Subscriber + { + $subscriber = $this->subscriberFactory->create(); + $subscriber->loadByCustomer((int)$customer->getId(), $websiteId); + if (!$subscriber->getId()) { + $subscriber->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + + return $subscriber; + } + + /** + * Save Subscriber model + * + * @param Subscriber $subscriber + * @param CustomerInterface $customer + * @param int $storeId + * @param int $status + * @return bool Need to send email + */ + private function saveSubscriber( + Subscriber $subscriber, + CustomerInterface $customer, + int $storeId, + int $status + ): bool { + $statusChanged = (int)$subscriber->getStatus() !== $status; + $emailChanged = $subscriber->getEmail() !== $customer->getEmail(); + if ($subscriber->getId() + && !$statusChanged + && (int)$subscriber->getCustomerId() === (int)$customer->getId() + && (int)$subscriber->getStoreId() === $storeId + && !$emailChanged + ) { + return false; + } + + if (!$subscriber->getId()) { + $subscriber->setSubscriberConfirmCode($subscriber->randomSequence()); + } + $subscriber->setStatus($status) + ->setStatusChanged($statusChanged) + ->setCustomerId($customer->getId()) + ->setStoreId($storeId) + ->setEmail($customer->getEmail()) + ->save(); + + if ($statusChanged) { + return true; + } + + /** + * If the subscriber is waiting to confirm from the customer + * and customer changed the email + * than need to send confirmation letter to the new email + */ + return $status === Subscriber::STATUS_NOT_ACTIVE && $emailChanged; + } + + /** + * Get new subscription status + * + * @param Subscriber $subscriber + * @param CustomerInterface $customer + * @param int $storeId + * @param bool $subscribe + * @return int + */ + private function getNewSubscriptionStatus( + Subscriber $subscriber, + CustomerInterface $customer, + int $storeId, + bool $subscribe + ): int { + $currentStatus = (int)$subscriber->getStatus(); + // If the current status is already as needed then return them + if (($subscribe && $currentStatus === Subscriber::STATUS_SUBSCRIBED) + || (!$subscribe && $currentStatus === Subscriber::STATUS_UNSUBSCRIBED) + ) { + return $currentStatus; + } + + $status = $currentStatus; + if ($subscribe) { + $customerConfirmStatus = $this->customerAccountManagement->getConfirmationStatus($customer->getId()); + if ($customerConfirmStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { + $status = Subscriber::STATUS_UNCONFIRMED; + } elseif ($this->isConfirmNeed($storeId)) { + $status = Subscriber::STATUS_NOT_ACTIVE; + } else { + $status = Subscriber::STATUS_SUBSCRIBED; + } + } elseif ($currentStatus === Subscriber::STATUS_SUBSCRIBED) { + $status = Subscriber::STATUS_UNSUBSCRIBED; + } + + return $status; + } + + /** + * Sends out email to customer after change subscription status + * + * @param Subscriber $subscriber + * @return void + */ + private function sendEmailAfterChangeStatus(Subscriber $subscriber): void + { + $status = (int)$subscriber->getStatus(); + if ($status === Subscriber::STATUS_UNCONFIRMED) { + return; + } + + try { + switch ($status) { + case Subscriber::STATUS_UNSUBSCRIBED: + $subscriber->sendUnsubscriptionEmail(); + break; + case Subscriber::STATUS_SUBSCRIBED: + $subscriber->sendConfirmationSuccessEmail(); + break; + case Subscriber::STATUS_NOT_ACTIVE: + $subscriber->sendConfirmationRequestEmail(); + break; + } + } catch (MailException $e) { + // If we are not able to send a new account email, this should be ignored + $this->logger->critical($e); + } + } + + /** + * Is need to confirm subscription + * + * @param int $storeId + * @return bool + */ + private function isConfirmNeed(int $storeId): bool + { + return (bool)$this->scopeConfig->isSetFlag( + Subscriber::XML_PATH_CONFIRMATION_FLAG, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } +} diff --git a/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php b/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php new file mode 100644 index 0000000000000..8b92d825bdbcd --- /dev/null +++ b/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Model; + +/** + * Interface to update newsletter subscription status + */ +interface SubscriptionManagerInterface +{ + /** + * Subscribe to newsletters by email + * + * @param string $email + * @param int $storeId + * @return Subscriber + */ + public function subscribe(string $email, int $storeId): Subscriber; + + /** + * Unsubscribe from newsletters by email + * + * @param string $email + * @param int $storeId + * @param string $confirmCode + * @return Subscriber + */ + public function unsubscribe(string $email, int $storeId, string $confirmCode): Subscriber; + + /** + * Subscribe customer to newsletter + * + * @param int $customerId + * @param int $storeId + * @return Subscriber + */ + public function subscribeCustomer(int $customerId, int $storeId): Subscriber; + + /** + * Unsubscribe customer from newsletter + * + * @param int $customerId + * @param int $storeId + * @return Subscriber + */ + public function unsubscribeCustomer(int $customerId, int $storeId): Subscriber; +} diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php index e809b7e37a432..52b3df8cb8aa6 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php @@ -5,149 +5,160 @@ */ namespace Magento\Newsletter\Test\Unit\Model\Plugin; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share; use Magento\Customer\Model\ResourceModel\CustomerRepository; use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Framework\Api\ExtensionAttributesFactory; -use Magento\Newsletter\Model\ResourceModel\Subscriber; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Newsletter\Model\Plugin\CustomerPlugin; +use Magento\Newsletter\Model\ResourceModel\Subscriber\Collection; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Website; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class CustomerPluginTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Newsletter Plugin for customer + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CustomerPluginTest extends TestCase { /** - * @var \Magento\Newsletter\Model\Plugin\CustomerPlugin + * @var SubscriberFactory|MockObject */ - private $plugin; + private $subscriberFactory; /** - * @var \Magento\Newsletter\Model\SubscriberFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ExtensionAttributesFactory|MockObject */ - private $subscriberFactory; + private $extensionFactory; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ - private $subscriber; + private $collectionFactory; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var SubscriptionManagerInterface|MockObject */ - private $objectManager; + private $subscriptionManager; /** - * @var ExtensionAttributesFactory|\PHPUnit_Framework_MockObject_MockObject + * @var Share|MockObject */ - private $extensionFactoryMock; + private $shareConfig; /** - * @var CustomerExtensionInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - private $customerExtensionMock; + private $storeManager; /** - * @var Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - private $subscriberResourceMock; + private $objectManager; /** - * @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerPlugin */ - private $customerMock; + private $plugin; + /** + * @inheritdoc + */ protected function setUp() { - $this->subscriberFactory = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->subscriber = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->setMethods( - [ - 'loadByEmail', - 'getId', - 'delete', - 'updateSubscription', - 'subscribeCustomerById', - 'unsubscribeCustomerById', - 'isSubscribed', - ] - )->disableOriginalConstructor() - ->getMock(); - $this->extensionFactoryMock = $this->getMockBuilder(ExtensionAttributesFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->customerExtensionMock = $this->getMockBuilder(CustomerExtensionInterface::class) - ->setMethods(['getIsSubscribed', 'setIsSubscribed']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->subscriberResourceMock = $this->getMockBuilder(Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - $this->customerMock = $this->getMockBuilder(CustomerInterface::class) - ->setMethods(['getExtensionAttributes']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->subscriberFactory->expects($this->any())->method('create')->willReturn($this->subscriber); - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - + $this->subscriberFactory = $this->createMock(SubscriberFactory::class); + $this->extensionFactory = $this->createMock(ExtensionAttributesFactory::class); + $this->collectionFactory = $this->createMock(CollectionFactory::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->shareConfig = $this->createMock(Share::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->objectManager = new ObjectManager($this); $this->plugin = $this->objectManager->getObject( - \Magento\Newsletter\Model\Plugin\CustomerPlugin::class, + CustomerPlugin::class, [ 'subscriberFactory' => $this->subscriberFactory, - 'extensionFactory' => $this->extensionFactoryMock, - 'subscriberResource' => $this->subscriberResourceMock, + 'extensionFactory' => $this->extensionFactory, + 'collectionFactory' => $this->collectionFactory, + 'subscriptionManager' => $this->subscriptionManager, + 'shareConfig' => $this->shareConfig, + 'storeManager' => $this->storeManager, ] ); } /** - * @param bool $subscriptionOriginalValue - * @param bool $subscriptionNewValue + * Test to update customer subscription after save customer + * + * @param int|null $originalStatus + * @param bool|null $newValue + * @param bool|null $expectedSubscribe * @dataProvider afterSaveDataProvider - * @return void */ - public function testAfterSave($subscriptionOriginalValue, $subscriptionNewValue) + public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expectedSubscribe) { - $customerId = 1; - /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $result */ - $result = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - /** @var CustomerRepository | \PHPUnit_Framework_MockObject_MockObject $subject */ - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $storeId = 2; + $websiteId = 1; + $customerId = 3; + $customerEmail = 'email@example.com'; + + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn($storeId); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->willReturn($store); - /** @var CustomerExtensionInterface|\PHPUnit_Framework_MockObject_MockObject $resultExtensionAttributes */ - $resultExtensionAttributes = $this->getMockBuilder(CustomerExtensionInterface::class) - ->setMethods(['getIsSubscribed', 'setIsSubscribed']) - ->getMockForAbstractClass(); - $result->expects($this->atLeastOnce())->method('getId')->willReturn($customerId); - $result->expects($this->any())->method('getExtensionAttributes')->willReturn(null); - $this->extensionFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($resultExtensionAttributes); - $result->expects($this->once()) - ->method('setExtensionAttributes') - ->with($resultExtensionAttributes) + $subscriber = $this->createMock(Subscriber::class); + $subscriber->method('getStatus')->willReturn($originalStatus); + $subscriber->method('getEmail')->willReturn($customerEmail); + $subscriber->method('isSubscribed')->willReturn($originalStatus === Subscriber::STATUS_SUBSCRIBED); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerEmail, $websiteId) ->willReturnSelf(); - $this->customerMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->customerExtensionMock); - $resultExtensionAttributes->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn($subscriptionOriginalValue); - $this->customerExtensionMock->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn($subscriptionNewValue); + $this->subscriberFactory->method('create')->willReturn($subscriber); - if ($subscriptionOriginalValue !== $subscriptionNewValue) { - if ($subscriptionNewValue) { - $this->subscriber->expects($this->once())->method('subscribeCustomerById')->with($customerId); - } else { - $this->subscriber->expects($this->once())->method('unsubscribeCustomerById')->with($customerId); - } - $this->subscriber->expects($this->once())->method('isSubscribed')->willReturn($subscriptionNewValue); - $resultExtensionAttributes->expects($this->once())->method('setIsSubscribed')->with($subscriptionNewValue); + $customerExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['getIsSubscribed']); + $customerExtension->method('getIsSubscribed')->willReturn($newValue); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getExtensionAttributes')->willReturn($customerExtension); + + $resultIsSubscribed = $newValue ?? $originalStatus === Subscriber::STATUS_SUBSCRIBED; + if ($expectedSubscribe !== null) { + $resultSubscriber = $this->createMock(Subscriber::class); + $resultSubscriber->method('isSubscribed')->willReturn($resultIsSubscribed); + $this->subscriptionManager->expects($this->once()) + ->method($expectedSubscribe ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $storeId) + ->willReturn($resultSubscriber); + } else { + $this->subscriptionManager->expects($this->never())->method('subscribeCustomer'); + $this->subscriptionManager->expects($this->never())->method('unsubscribeCustomer'); } + $resultExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['setIsSubscribed']); + $resultExtension->expects($this->once())->method('setIsSubscribed')->with($resultIsSubscribed); + /** @var CustomerInterface|MockObject $result */ + $result = $this->createMock(CustomerInterface::class); + $result->method('getId')->willReturn($customerId); + $result->method('getEmail')->willReturn($customerEmail); + $result->method('getExtensionAttributes')->willReturn($resultExtension); - $this->assertEquals($result, $this->plugin->afterSave($subject, $result, $this->customerMock)); + /** @var CustomerRepository|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $this->assertEquals($result, $this->plugin->afterSave($subject, $result, $customer)); } /** @@ -156,115 +167,135 @@ public function testAfterSave($subscriptionOriginalValue, $subscriptionNewValue) public function afterSaveDataProvider() { return [ - [true, true], - [false, false], - [true, false], - [false, true], + [null, null, null], + [null, true, true], + [null, false, null], + [Subscriber::STATUS_SUBSCRIBED, null, null], + [Subscriber::STATUS_SUBSCRIBED, true, null], + [Subscriber::STATUS_SUBSCRIBED, false, false], + [Subscriber::STATUS_UNSUBSCRIBED, null, null], + [Subscriber::STATUS_UNSUBSCRIBED, true, true], + [Subscriber::STATUS_UNSUBSCRIBED, false, null], + [Subscriber::STATUS_UNCONFIRMED, null, true], + [Subscriber::STATUS_UNCONFIRMED, true, true], + [Subscriber::STATUS_UNCONFIRMED, false, true], ]; } + /** + * Test to delete subscriptions after delete customer + */ public function testAfterDelete() { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $customer->expects($this->once())->method('getEmail')->willReturn('test@test.com'); - $this->subscriber->expects($this->once())->method('loadByEmail')->with('test@test.com')->willReturnSelf(); - $this->subscriber->expects($this->once())->method('getId')->willReturn(1); - $this->subscriber->expects($this->once())->method('delete')->willReturnSelf(); + $customerEmail = 'email@example.com'; + $websiteId = 1; + $storeIds = [1, 2]; + + $subscriber = $this->createMock(Subscriber::class); + $subscriber->expects($this->once())->method('delete'); + $collection = $this->createMock(Collection::class); + $collection->expects($this->once()) + ->method('addFieldToFilter') + ->with('subscriber_email', $customerEmail) + ->willReturnSelf(); + $collection->method('getIterator')->willReturn(new \ArrayIterator([$subscriber])); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); + $this->shareConfig->method('isWebsiteScope')->willReturn(false); + $website = $this->createMock(Website::class); + $website->method('getStoreIds')->willReturn($storeIds); + $this->storeManager->method('getWebsite')->with($websiteId)->willReturn($website); - $this->assertEquals(true, $this->plugin->afterDelete($subject, true, $customer)); + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getEmail')->willReturn($customerEmail); + + $this->assertTrue($this->plugin->afterDelete($subject, true, $customer)); } + /** + * Test to delete subscriptions after delete customer by id + */ public function testAroundDeleteById() { $customerId = 1; + $customerEmail = 'test@test.com'; + $websiteId = 1; + $storeIds = [1, 2]; $deleteCustomerById = function () { return true; }; - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $subject->expects($this->once())->method('getById')->willReturn($customer); - $customer->expects($this->once())->method('getEmail')->willReturn('test@test.com'); - $this->subscriber->expects($this->once())->method('loadByEmail')->with('test@test.com')->willReturnSelf(); - $this->subscriber->expects($this->once())->method('getId')->willReturn(1); - $this->subscriber->expects($this->once())->method('delete')->willReturnSelf(); + $customer = $this->createMock(CustomerInterface::class); + $customer->expects($this->once())->method('getEmail')->willReturn($customerEmail); + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $subject->expects($this->once())->method('getById')->with($customerId)->willReturn($customer); + + $subscriber = $this->createMock(Subscriber::class); + $subscriber->expects($this->once())->method('delete'); + $collection = $this->createMock(Collection::class); + $collection->expects($this->once()) + ->method('addFieldToFilter') + ->with('subscriber_email', $customerEmail) + ->willReturnSelf(); + $collection->method('getIterator')->willReturn(new \ArrayIterator([$subscriber])); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); + $this->shareConfig->method('isWebsiteScope')->willReturn(false); + $website = $this->createMock(Website::class); + $website->method('getStoreIds')->willReturn($storeIds); + $this->storeManager->method('getWebsite')->with($websiteId)->willReturn($website); - $this->assertEquals(true, $this->plugin->aroundDeleteById($subject, $deleteCustomerById, $customerId)); + $this->assertTrue($this->plugin->aroundDeleteById($subject, $deleteCustomerById, $customerId)); } /** - * @param int|null $subscriberStatusKey - * @param int|null $subscriberStatusValue - * @param bool $isSubscribed - * @dataProvider afterGetByIdDataProvider - * @return void + * Test to load extension attribute after get by id */ - public function testAfterGetByIdCreatesExtensionAttributesIfItIsNotSet( - $subscriberStatusKey, - $subscriberStatusValue, - $isSubscribed - ) { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $subscriber = [$subscriberStatusKey => $subscriberStatusValue]; + public function testAfterGetByIdCreatesExtensionAttributes(): void + { + $storeId = 2; + $websiteId = 1; + $customerId = 3; + $customerEmail = 'email@example.com'; + $subscribed = true; - $this->extensionFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->customerExtensionMock); - $this->customerMock->expects($this->once()) - ->method('setExtensionAttributes') - ->with($this->customerExtensionMock) - ->willReturnSelf(); - $this->customerMock->expects($this->any()) - ->method('getId') - ->willReturn(1); - $this->subscriberResourceMock->expects($this->once()) - ->method('loadByCustomerData') - ->with($this->customerMock) - ->willReturn($subscriber); - $this->customerExtensionMock->expects($this->once())->method('setIsSubscribed')->with($isSubscribed); + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn($storeId); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->willReturn($store); - $this->assertEquals( - $this->customerMock, - $this->plugin->afterGetById($subject, $this->customerMock) - ); - } + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerEmail); - public function testAfterGetByIdSetsIsSubscribedFlagIfItIsNotSet() - { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $subscriber = ['subscriber_id' => 1, 'subscriber_status' => 1]; - - $this->customerMock->expects($this->any()) - ->method('getExtensionAttributes') - ->willReturn($this->customerExtensionMock); - $this->customerExtensionMock->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn(null); - $this->subscriberResourceMock->expects($this->once()) - ->method('loadByCustomerData') - ->with($this->customerMock) - ->willReturn($subscriber); - $this->customerExtensionMock->expects($this->once()) - ->method('setIsSubscribed') + $subscriber = $this->createMock(Subscriber::class); + $subscriber->method('getEmail')->willReturn($customerEmail); + $subscriber->method('isSubscribed')->willReturn($subscribed); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerEmail, $websiteId) ->willReturnSelf(); + $this->subscriberFactory->method('create')->willReturn($subscriber); - $this->assertEquals( - $this->customerMock, - $this->plugin->afterGetById($subject, $this->customerMock) + $customerExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + ['getIsSubscribed', 'setIsSubscribed'] ); - } + $customerExtension->expects($this->once())->method('setIsSubscribed')->with($subscribed); + $this->extensionFactory->expects($this->once())->method('create')->willReturn($customerExtension); + $customer->expects($this->once())->method('setExtensionAttributes')->with($customerExtension); - /** - * @return array - */ - public function afterGetByIdDataProvider() - { - return [ - ['subscriber_status', 1, true], - ['subscriber_status', 2, false], - ['subscriber_status', 3, false], - ['subscriber_status', 4, false], - [null, null, false], - ]; + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $this->assertEquals( + $customer, + $this->plugin->afterGetById($subject, $customer) + ); } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php index 6ccbba9f8828b..c3814563aa46c 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php @@ -5,118 +5,152 @@ */ namespace Magento\Newsletter\Test\Unit\Model; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Session; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Translate\Inline\StateInterface; +use Magento\Newsletter\Helper\Data; +use Magento\Newsletter\Model\Queue; use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test Subscriber model functionality + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class SubscriberTest extends \PHPUnit\Framework\TestCase +class SubscriberTest extends TestCase { /** - * @var \Magento\Newsletter\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $newsletterData; + private $newsletterData; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $scopeConfig; + private $scopeConfig; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var TransportBuilder|MockObject */ - protected $transportBuilder; + private $transportBuilder; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManager; + private $storeManager; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $customerSession; + private $customerSession; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ - protected $customerRepository; + private $customerRepository; /** - * @var \Magento\Customer\Api\AccountManagementInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AccountManagementInterface|MockObject */ - protected $customerAccountManagement; + private $customerAccountManagement; /** - * @var \Magento\Framework\Translate\Inline\StateInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StateInterface|MockObject */ - protected $inlineTranslation; + private $inlineTranslation; /** - * @var \Magento\Newsletter\Model\ResourceModel\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Newsletter\Model\ResourceModel\Subscriber|MockObject */ - protected $resource; + private $resource; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ - protected $objectManager; + private $objectManager; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ private $dataObjectHelper; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerInterfaceFactory|MockObject */ private $customerFactory; /** - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ - protected $subscriber; + private $subscriber; + /** + * @inheritdoc + */ protected function setUp() { - $this->newsletterData = $this->createMock(\Magento\Newsletter\Helper\Data::class); - $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->transportBuilder = $this->createPartialMock(\Magento\Framework\Mail\Template\TransportBuilder::class, [ + $this->newsletterData = $this->createMock(Data::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->transportBuilder = $this->createPartialMock( + TransportBuilder::class, + [ 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', 'setFrom', 'addTo', 'getTransport' - ]); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->customerSession = $this->createPartialMock(\Magento\Customer\Model\Session::class, [ + ] + ); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->customerSession = $this->createPartialMock( + Session::class, + [ 'isLoggedIn', 'getCustomerDataObject', 'getCustomerId' - ]); - $this->customerRepository = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $this->customerAccountManagement = $this->createMock(\Magento\Customer\Api\AccountManagementInterface::class); - $this->inlineTranslation = $this->createMock(\Magento\Framework\Translate\Inline\StateInterface::class); - $this->resource = $this->createPartialMock(\Magento\Newsletter\Model\ResourceModel\Subscriber::class, [ + ] + ); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + $this->customerAccountManagement = $this->createMock(AccountManagementInterface::class); + $this->inlineTranslation = $this->createMock(StateInterface::class); + $this->resource = $this->createPartialMock( + \Magento\Newsletter\Model\ResourceModel\Subscriber::class, + [ 'loadByEmail', 'getIdFieldName', 'save', - 'loadByCustomerData', - 'received' - ]); - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + 'loadByCustomer', + 'received', + 'loadBySubscriberEmail', + 'loadByCustomerId', + ] + ); + $this->objectManager = new ObjectManager($this); - $this->customerFactory = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterfaceFactory::class) + $this->customerFactory = $this->getMockBuilder(CustomerInterfaceFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->dataObjectHelper = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + $this->dataObjectHelper = $this->getMockBuilder(DataObjectHelper::class) ->disableOriginalConstructor() ->getMock(); $this->subscriber = $this->objectManager->getObject( - \Magento\Newsletter\Model\Subscriber::class, + Subscriber::class, [ 'newsletterData' => $this->newsletterData, 'scopeConfig' => $this->scopeConfig, @@ -128,250 +162,78 @@ protected function setUp() 'inlineTranslation' => $this->inlineTranslation, 'resource' => $this->resource, 'customerFactory' => $this->customerFactory, - 'dataObjectHelper' => $this->dataObjectHelper - ] - ); - } - - public function testSubscribe() - { - $email = 'subscriber_email@magento.com'; - $storeId = 1; - $customerData = ['store_id' => $storeId, 'email' => $email]; - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn($storeId); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerFactory->expects($this->once())->method('create')->willReturn($customer); - $this->dataObjectHelper->expects($this->once())->method('populateWithArray')->with( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $this->resource->expects($this->any())->method('loadByCustomerData')->with($customer)->willReturn( - [ - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, - 'subscriber_email' => $email, - 'name' => 'subscriber_name' + 'dataObjectHelper' => $this->dataObjectHelper, ] ); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(true); - $customerDataModel = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel); - $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1); - $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email); - $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel); - $customerDataModel->expects($this->any())->method('getStoreId')->willReturn($storeId); - $customerDataModel->expects($this->any())->method('getId')->willReturn(1); - $this->sendEmailCheck(); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } - public function testSubscribeNotLoggedIn() + /** + * Test to Load by subscriber email + * + * @return void + */ + public function testLoadBySubscriberEmail(): void { - $email = 'subscriber_email@magento.com'; - $storeId = 1; - $customerData = ['store_id' => $storeId, 'email' => $email]; - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn($storeId); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerFactory->expects($this->once())->method('create')->willReturn($customer); - $this->dataObjectHelper->expects($this->once())->method('populateWithArray')->with( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class + $email = 'subscriber_email@example.com'; + $websiteId = 1; + $subscriberData = ['some_filed' => 'value']; + + $this->resource->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturn($subscriberData); + + $this->assertEquals( + $subscriberData, + $this->subscriber->loadBySubscriberEmail($email, $websiteId)->getData() ); - $this->resource->expects($this->any())->method('loadByCustomerData')->with($customer)->willReturn( - [ - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, - 'subscriber_email' => $email, - 'name' => 'subscriber_name' - ] - ); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(false); - $customerDataModel = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel); - $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1); - $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email); - $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel); - $customerDataModel->expects($this->any())->method('getStoreId')->willReturn($storeId); - $customerDataModel->expects($this->any())->method('getId')->willReturn(1); - $this->sendEmailCheck(); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } - public function testUpdateSubscription() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $this->customerAccountManagement->expects($this->once()) - ->method('getConfirmationStatus') - ->with($customerId) - ->willReturn('account_confirmation_required'); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->setMethods(['getId']) - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - - $this->assertEquals($this->subscriber, $this->subscriber->updateSubscription($customerId)); - } - - public function testUnsubscribeCustomerById() + /** + * Test to Load by customer + * + * @return void + */ + public function testLoadByCustomer(): void { - $storeId = 2; $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); + $websiteId = 1; + $subscriberData = ['some_filed' => 'value']; - $this->subscriber->unsubscribeCustomerById($customerId); - } - - public function testSubscribeCustomerById() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); + $this->resource->expects($this->once()) + ->method('loadByCustomerId') + ->with($customerId, $websiteId) + ->willReturn($subscriberData); - $this->subscriber->subscribeCustomerById($customerId); - } - - public function testSubscribeCustomerById1() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); - $this->customerAccountManagement->expects($this->once()) - ->method('getConfirmationStatus') - ->willReturn(\Magento\Customer\Api\AccountManagementInterface::ACCOUNT_CONFIRMATION_NOT_REQUIRED); - $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); - - $this->subscriber->subscribeCustomerById($customerId); - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->getStatus()); - } - - public function testSubscribeCustomerByIdAfterConfirmation() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNCONFIRMED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); - $this->customerAccountManagement->expects($this->never())->method('getConfirmationStatus'); - $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); - - $this->subscriber->updateSubscription($customerId); - $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->subscriber->getStatus()); + $this->assertEquals( + $subscriberData, + $this->subscriber->loadByCustomer($customerId, $websiteId)->getData() + ); } + /** + * Test to unsubscribe customer from newsletters + */ public function testUnsubscribe() { $this->resource->expects($this->once())->method('save')->willReturnSelf(); - $this->sendEmailCheck(); + $subscriberData = [ + 'store_id' => 2, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, + Subscriber::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY + ); $this->assertEquals($this->subscriber, $this->subscriber->unsubscribe()); } /** + * Test to try unsubscribe customer from newsletters with wrong confirmation code + * * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage This is an invalid subscription confirmation code. */ @@ -383,6 +245,9 @@ public function testUnsubscribeException() $this->subscriber->unsubscribe(); } + /** + * Test to get subscriber full name + */ public function testGetSubscriberFullName() { $this->subscriber->setFirstname('John'); @@ -391,6 +256,9 @@ public function testGetSubscriberFullName() $this->assertEquals('John Doe', $this->subscriber->getSubscriberFullName()); } + /** + * Test to confirm customer subscription + */ public function testConfirm() { $code = 111; @@ -400,6 +268,9 @@ public function testConfirm() $this->assertTrue($this->subscriber->confirm($code)); } + /** + * Test to doesn't confirm customer subscription + */ public function testConfirmWrongCode() { $code = 111; @@ -408,9 +279,12 @@ public function testConfirmWrongCode() $this->assertFalse($this->subscriber->confirm($code)); } + /** + * Test to mark receiving subscriber of queue newsletter + */ public function testReceived() { - $queue = $this->getMockBuilder(\Magento\Newsletter\Model\Queue::class) + $queue = $this->getMockBuilder(Queue::class) ->disableOriginalConstructor() ->getMock(); $this->resource->expects($this->once())->method('received')->with($this->subscriber, $queue)->willReturnSelf(); @@ -419,28 +293,103 @@ public function testReceived() } /** - * @return $this + * Test to Sends out confirmation email + * + * @return void */ - protected function sendEmailCheck() + public function testSendConfirmationRequestEmail(): void { - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->setMethods(['getId']) - ->getMock(); - $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->transportBuilder->expects($this->once())->method('setTemplateIdentifier')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setTemplateOptions')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setTemplateVars')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setFrom')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('addTo')->willReturnSelf(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn(1); - $this->transportBuilder->expects($this->once())->method('getTransport')->willReturn($transport); + $confirmLink = 'confirm link'; + $storeId = 2; + $subscriberData = [ + 'store_id' => $storeId, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $store = $this->createMock(StoreInterface::class); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->newsletterData->expects($this->once()) + ->method('getConfirmationUrl') + ->with($this->subscriber) + ->willReturn($confirmLink); + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_CONFIRM_EMAIL_TEMPLATE, + Subscriber::XML_PATH_CONFIRM_EMAIL_IDENTITY, + [ + 'store' => $store, + 'subscriber_data' => [ + 'confirmation_link' => $confirmLink, + ], + ] + ); + $this->assertEquals($this->subscriber, $this->subscriber->sendConfirmationRequestEmail()); + } + + /** + * Test to Sends out success email + * + * @return void + */ + public function testSendConfirmationSuccessEmail(): void + { + $subscriberData = [ + 'store_id' => 2, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_SUCCESS_EMAIL_TEMPLATE, + Subscriber::XML_PATH_SUCCESS_EMAIL_IDENTITY + ); + $this->assertEquals($this->subscriber, $this->subscriber->sendConfirmationSuccessEmail()); + } + + /** + * Check to send email + * + * @param string $templateConfigPath + * @param string $identityTemplatePath + * @return void + */ + private function sendEmailCheck(string $templateConfigPath, string $identityTemplatePath, array $vars = []): void + { + $template = 'email_template'; + $identity = 'email_identity'; + $vars += ['subscriber' => $this->subscriber]; + + $this->scopeConfig->method('getValue') + ->willReturnMap( + [ + [$templateConfigPath, ScopeInterface::SCOPE_STORE, $this->subscriber->getStoreId(), $template], + [$identityTemplatePath, ScopeInterface::SCOPE_STORE, $this->subscriber->getStoreId(), $identity], + ] + ); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateIdentifier') + ->with($template) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateOptions') + ->with(['area' => Area::AREA_FRONTEND, 'store' => $this->subscriber->getStoreId()]) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateVars') + ->with($vars) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('addTo') + ->with($this->subscriber->getEmail(), $this->subscriber->getName()) + ->willReturnSelf(); + $transport = $this->createMock(TransportInterface::class); $transport->expects($this->once())->method('sendMessage')->willReturnSelf(); + $this->transportBuilder->expects($this->once())->method('getTransport')->willReturn($transport); $this->inlineTranslation->expects($this->once())->method('suspend')->willReturnSelf(); $this->inlineTranslation->expects($this->once())->method('resume')->willReturnSelf(); - - return $this; } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php new file mode 100644 index 0000000000000..ecb30f12742c8 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php @@ -0,0 +1,651 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Test\Unit\Model; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManager; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + +/** + * Test to update newsletter subscription status + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SubscriptionManagerTest extends TestCase +{ + /** + * @var SubscriberFactory|MockObject + */ + private $subscriberFactory; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; + + /** + * @var AccountManagementInterface|MockObject + */ + private $customerAccountManagement; + + /** + * @var CustomerRepositoryInterface|MockObject + */ + private $customerRepository; + + /** + * @var SubscriptionManager + */ + private $subscriptionManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->subscriberFactory = $this->createMock(SubscriberFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->customerAccountManagement = $this->createMock(AccountManagementInterface::class); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + + $objectManager = new ObjectManager($this); + $this->subscriptionManager = $objectManager->getObject( + SubscriptionManager::class, + [ + 'subscriberFactory' => $this->subscriberFactory, + 'logger' => $this->logger, + 'storeManager' => $this->storeManager, + 'scopeConfig' => $this->scopeConfig, + 'customerAccountManagement' => $this->customerAccountManagement, + 'customerRepository' => $this->customerRepository, + ] + ); + } + + /** + * Test to Subscribe to newsletters by email + * + * @param array $subscriberData + * @param string $email + * @param int $storeId + * @param bool $isConfirmNeed + * @param array $expectedData + * @dataProvider subscribeDataProvider + */ + public function testSubscribe( + array $subscriberData, + string $email, + int $storeId, + bool $isConfirmNeed, + array $expectedData + ): void { + $websiteId = 1; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturnSelf(); + $subscriber->setData($subscriberData); + if (empty($subscriberData['id'])) { + $subscriber->method('randomSequence')->willReturn($expectedData['subscriber_confirm_code']); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + $this->scopeConfig->method('isSetFlag') + ->with(Subscriber::XML_PATH_CONFIRMATION_FLAG, ScopeInterface::SCOPE_STORE, $storeId) + ->willReturn($isConfirmNeed); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->subscribe($email, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Subscribe customer data provider + * + * @return array + */ + public function subscribeDataProvider(): array + { + return [ + 'Subscribe new' => [ + 'subscriber_data' => [], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + ], + 'Subscribe new: confirm required' => [ + 'subscriber_data' => [], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + ], + 'Subscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + 'customer_id' => 0, + ], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + 'customer_id' => 0, + ], + ], + ]; + } + + /** + * Test to Unsubscribe from newsletters by email + */ + public function testUnsubscribe(): void + { + $email = 'email@example.com'; + $storeId = 2; + $websiteId = 1; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $confirmCode = 'confirm code'; + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + ['loadBySubscriberEmail', 'getId', 'setCheckCode', 'unsubscribe'] + ); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturnSelf(); + $subscriber->method('getId')->willReturn(1); + $subscriber->expects($this->once())->method('setCheckCode')->with($confirmCode)->willReturnSelf(); + $subscriber->expects($this->once())->method('unsubscribe')->willReturnSelf(); + $this->subscriberFactory->method('create')->willReturn($subscriber); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->unsubscribe($email, $storeId, $confirmCode) + ); + } + + /** + * Test to Subscribe customer to newsletter + * + * @param array $subscriberData + * @param array $customerData + * @param int $storeId + * @param bool $isConfirmNeed + * @param array $expectedData + * @param bool $needToSendEmail + * @dataProvider subscribeCustomerDataProvider + */ + public function testSubscribeCustomer( + array $subscriberData, + array $customerData, + int $storeId, + bool $isConfirmNeed, + array $expectedData, + bool $needToSendEmail + ): void { + $websiteId = 1; + $customerId = $customerData['id']; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerData['email']); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadByCustomer', + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerData['email'], $websiteId) + ->willReturnSelf(); + } + $subscriber->setData($subscriberData); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->method('randomSequence')->willReturn($expectedData['subscriber_confirm_code']); + } + $sendEmailMethod = $this->getSendEmailMethod($expectedData['subscriber_status'] ?? 0); + if ($needToSendEmail) { + $subscriber->expects($this->once())->method($sendEmailMethod); + } else { + $subscriber->expects($this->never())->method('sendConfirmationRequestEmail'); + $subscriber->expects($this->never())->method('sendConfirmationSuccessEmail'); + $subscriber->expects($this->never())->method('sendUnsubscriptionEmail'); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + $this->scopeConfig->method('isSetFlag') + ->with(Subscriber::XML_PATH_CONFIRMATION_FLAG, ScopeInterface::SCOPE_STORE, $storeId) + ->willReturn($isConfirmNeed); + $this->customerAccountManagement + ->method('getConfirmationStatus') + ->willReturn($customerData['confirmation_status']); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->subscribeCustomer($customerId, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Get expected send email method + * + * @param int $status + * @return string + */ + private function getSendEmailMethod(int $status): string + { + switch ($status) { + case Subscriber::STATUS_SUBSCRIBED: + $sendEmailMethod = 'sendConfirmationSuccessEmail'; + break; + case Subscriber::STATUS_NOT_ACTIVE: + $sendEmailMethod = 'sendConfirmationRequestEmail'; + break; + case Subscriber::STATUS_UNSUBSCRIBED: + $sendEmailMethod = 'sendUnsubscriptionEmail'; + break; + default: + $sendEmailMethod = ''; + } + + return $sendEmailMethod; + } + + /** + * Subscribe customer data provider + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function subscribeCustomerDataProvider(): array + { + return [ + 'Subscribe new' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Subscribe new: customer confirm required' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNCONFIRMED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Subscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Subscribe existing: subscription confirm required' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Update subscription data' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 2, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Update subscription data: subscription confirm required ' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 2, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + ]; + } + + /** + * Test to Unsubscribe customer from newsletter + * + * @param array $subscriberData + * @param array $customerData + * @param int $storeId + * @param array $expectedData + * @param bool $needToSendEmail + * @dataProvider unsubscribeCustomerDataProvider + */ + public function testUnsubscribeCustomer( + array $subscriberData, + array $customerData, + int $storeId, + array $expectedData, + bool $needToSendEmail + ): void { + $websiteId = 1; + $customerId = $customerData['id']; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerData['email']); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadByCustomer', + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerData['email'], $websiteId) + ->willReturnSelf(); + } + $subscriber->setData($subscriberData); + $sendEmailMethod = $this->getSendEmailMethod($expectedData['subscriber_status'] ?? 0); + if ($needToSendEmail) { + $subscriber->expects($this->once())->method($sendEmailMethod); + } else { + $subscriber->expects($this->never())->method('sendConfirmationRequestEmail'); + $subscriber->expects($this->never())->method('sendConfirmationSuccessEmail'); + $subscriber->expects($this->never())->method('sendUnsubscriptionEmail'); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->unsubscribeCustomer($customerId, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Unsubscribe customer data provider + * + * @return array + */ + public function unsubscribeCustomerDataProvider(): array + { + return [ + 'Unsubscribe new' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + ], + 'needToSendEmail' => false, + ], + 'Unsubscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Unsubscribe existing: subscription confirm required' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Update subscription data' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + ], + 'store_id' => 2, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + ]; + } +} diff --git a/app/code/Magento/Newsletter/etc/di.xml b/app/code/Magento/Newsletter/etc/di.xml index 179ec19cccfc5..3c35936a2e8aa 100644 --- a/app/code/Magento/Newsletter/etc/di.xml +++ b/app/code/Magento/Newsletter/etc/di.xml @@ -25,4 +25,11 @@ <plugin name="update_newsletter_subscription_on_customer_update" type="Magento\Newsletter\Model\Plugin\CustomerPlugin"/> </type> + <type name="Magento\Newsletter\Model\Subscriber"> + <arguments> + <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument> + </arguments> + </type> + <preference for="Magento\Newsletter\Model\SubscriptionManagerInterface" + type="Magento\Newsletter\Model\SubscriptionManager"/> </config> diff --git a/app/code/Magento/Store/Model/System/Store.php b/app/code/Magento/Store/Model/System/Store.php index 744019b107247..d13781b8c146b 100644 --- a/app/code/Magento/Store/Model/System/Store.php +++ b/app/code/Magento/Store/Model/System/Store.php @@ -52,6 +52,11 @@ class Store extends \Magento\Framework\DataObject implements OptionSourceInterfa */ protected $_storeManager; + /** + * @var string + */ + private $nonEscapableNbspChar; + /** * Init model * Load Website, Group and Store collections @@ -61,6 +66,9 @@ class Store extends \Magento\Framework\DataObject implements OptionSourceInterfa public function __construct(\Magento\Store\Model\StoreManagerInterface $storeManager) { $this->_storeManager = $storeManager; + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $this->nonEscapableNbspChar = html_entity_decode(' ', ENT_NOQUOTES, 'UTF-8'); + return $this->reload(); } @@ -121,9 +129,6 @@ public function getStoreValuesForForm($empty = false, $all = false) $options[] = ['label' => __('All Store Views'), 'value' => 0]; } - // phpcs:ignore Magento2.Functions.DiscouragedFunction - $nonEscapableNbspChar = html_entity_decode(' ', ENT_NOQUOTES, 'UTF-8'); - foreach ($this->_websiteCollection as $website) { $websiteShow = false; foreach ($this->_groupCollection as $group) { @@ -140,13 +145,13 @@ public function getStoreValuesForForm($empty = false, $all = false) $websiteShow = true; } $values[] = [ - 'label' => str_repeat($nonEscapableNbspChar, 4) . $store->getName(), + 'label' => str_repeat($this->nonEscapableNbspChar, 4) . $store->getName(), 'value' => $store->getId(), ]; } if (!empty($values)) { $options[] = [ - 'label' => str_repeat($nonEscapableNbspChar, 4) . $group->getName(), + 'label' => str_repeat($this->nonEscapableNbspChar, 4) . $group->getName(), 'value' => $values, ]; } @@ -216,6 +221,22 @@ public function getStoresStructure($isAll = false, $storeIds = [], $groupIds = [ return $out; } + /** + * Get store options in tree view + * + * @param bool $isAll + * @param array $storeIds + * @param array $groupIds + * @param array $websiteIds + * @return array Format: array(array('value' => '<value>', 'label' => '<label>'), ...) + */ + public function getStoreOptionsTree($isAll = false, $storeIds = [], $groupIds = [], $websiteIds = []): array + { + $storeStructure = $this->getStoresStructure($isAll, $storeIds, $groupIds, $websiteIds); + + return $this->retrieveOptionValues($storeStructure); + } + /** * Website label/value array getter, compatible with form dropdown options * @@ -480,4 +501,35 @@ public function toOptionArray() { return $this->getStoreValuesForForm(); } + + /** + * Retrieve option values + * + * Return array of options as value-label pairs in tree view + * + * @param array $structure + * @param bool $needSpacePrefix + * @return array + */ + private function retrieveOptionValues(array $structure, bool $needSpacePrefix = false): array + { + $prefix = ''; + if ($needSpacePrefix) { + $prefix = str_repeat($this->nonEscapableNbspChar, 4); + } + + $values = []; + foreach ($structure as $item) { + $value = !empty($item['children']) + ? $this->retrieveOptionValues($item['children'], true) + : $item['value']; + + $values[] = [ + 'label' => $prefix . $item['label'], + 'value' => $value, + ]; + } + + return $values; + } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index c8f2530df22e0..0c7dd7e7cb94c 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -78,3 +78,33 @@ } } } + +.customer-newsletter-fieldset.admin__fieldset { + &.multi-website { + > .admin__field > .admin__field-control { + width: ~'calc(100% * 0.75 - 30px)'; + + table { + th.subscriber-status { + text-align: center; + } + + td { + &.subscriber-status { + text-align: center; + } + + select.admin__control-select { + width: 100%; + } + } + } + } + } + + > .admin__field > .admin__field-control { + input[type='checkbox'] { + margin: 8px 0 0 0; + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php index e28a9602b9377..a44101da77d6d 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -61,12 +61,13 @@ public function tearDown() */ public function testRenderingNewsletterBlock() { + $websiteId = 1; $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/edit'); $body = $this->getResponse()->getBody(); $this->assertContains('\u003Cspan\u003ENewsletter Information\u003C\/span\u003E', $body); - $this->assertContains('\u003Cinput id=\"_newslettersubscription\"', $body); + $this->assertContains('\u003Cinput id=\"_newslettersubscription_status_' . $websiteId . '\"', $body); $this->assertNotContains('checked="checked"', $body); $this->assertContains('\u003Cspan\u003ESubscribed to Newsletter\u003C\/span\u003E', $body); $this->assertContains('\u003ENo Newsletter Found\u003C', $body); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 1b7f2c1f7efdd..4fc549f3caf86 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -128,6 +128,7 @@ public function testSaveActionWithInvalidFormData() public function testSaveActionExistingCustomerUnsubscribeNewsletter() { $customerId = 1; + $websiteId = 1; /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); @@ -144,7 +145,7 @@ public function testSaveActionExistingCustomerUnsubscribeNewsletter() 'lastname' => 'test lastname', 'sendemail_store_id' => 1 ], - 'subscription' => '0' + 'subscription_status' => [$websiteId => '0'] ]; $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); From 72705ada1f90fa2afe7fdfdc14258c70d90bd014 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 25 Nov 2019 15:10:52 +0200 Subject: [PATCH 2359/2437] MC-4242: Newsletter subscriptions per website --- app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 59e67e7aa32a7..424a42f629cdc 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -21,6 +21,8 @@ /** * Newsletter Plugin for customer + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CustomerPlugin { From 644c063d7c8ae32dee8861ae73a0fcf6d1e779c4 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 26 Nov 2019 09:21:18 +0200 Subject: [PATCH 2360/2437] MC-4242: Newsletter subscriptions per website --- .../Newsletter/Model/ResourceModel/Queue/Collection.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php index 1fc68771e74cb..0553fae0f3b18 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php @@ -5,6 +5,8 @@ */ namespace Magento\Newsletter\Model\ResourceModel\Queue; +use Magento\Newsletter\Model\Subscriber; + /** * Newsletter queue collection. * @@ -214,7 +216,7 @@ public function addSubscriberFilter($subscriberId) * @param int $customerId * @return $this */ - public function addCustomerFilter(int $customerId): self + public function addCustomerFilter(int $customerId): Collection { $this->getSelect() ->join( From c16f055dae7c035c643bc66f27f6fa5173d2ce2a Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 26 Nov 2019 09:21:56 +0200 Subject: [PATCH 2361/2437] MC-4242: Newsletter subscriptions per website --- .../Magento/Newsletter/Model/ResourceModel/Queue/Collection.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php index 0553fae0f3b18..33c539fbba84f 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php @@ -5,8 +5,6 @@ */ namespace Magento\Newsletter\Model\ResourceModel\Queue; -use Magento\Newsletter\Model\Subscriber; - /** * Newsletter queue collection. * From 51c6d1d18c106a35b9fb4338e986dc38190b21f5 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 27 Nov 2019 15:49:05 +0200 Subject: [PATCH 2362/2437] MC-4242: Newsletter subscriptions per website --- app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 424a42f629cdc..83bafe5bf2887 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -118,7 +118,7 @@ public function afterSave( * and customer is already confirmed registration * than need to subscribe customer */ - if ($subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED && empty($result->getConfirmation())) { + if ((int)$subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED && empty($result->getConfirmation())) { $needToUpdate = true; $subscribeStatus = true; } From 107d68a6929bbbd396e8fb6b68c88227d282c2a2 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 3 Dec 2019 16:27:32 +0200 Subject: [PATCH 2363/2437] MC-4242: Newsletter subscriptions per website --- .../Magento/backend/web/css/styles-old.less | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index 1703e87691788..247316ab0361b 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -3962,22 +3962,6 @@ .grid tr.headings th > span { white-space: normal; } - - .field { - &.field-subscription { - .admin__field-label { - margin-left: 10px; - float: none; - cursor: pointer; - } - - .admin__field-control { - float: left; - width: auto; - margin: 6px 0px 0px 0px; - } - } - } } } From 53548365e5c52bcdb76ce089b85276dbc447c6ff Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 5 Dec 2019 11:23:43 +0200 Subject: [PATCH 2364/2437] MC-4242: Newsletter subscriptions per website --- .../Adminhtml/Index/MassUnsubscribe.php | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index 17f420d864df0..723f325cb08c8 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -6,10 +6,13 @@ namespace Magento\Customer\Controller\Adminhtml\Index; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Backend\App\Action\Context; use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; @@ -29,23 +32,38 @@ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterf */ private $subscriptionManager; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var Share + */ + private $shareConfig; + /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository * @param SubscriptionManagerInterface $subscriptionManager + * @param Share $shareConfig */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, CustomerRepositoryInterface $customerRepository, - SubscriptionManagerInterface $subscriptionManager + SubscriptionManagerInterface $subscriptionManager, + StoreManagerInterface $storeManager, + Share $shareConfig ) { parent::__construct($context, $filter, $collectionFactory); $this->customerRepository = $customerRepository; $this->subscriptionManager = $subscriptionManager; + $this->storeManager = $storeManager; + $this->shareConfig = $shareConfig; } /** @@ -60,8 +78,9 @@ protected function massAction(AbstractCollection $collection) foreach ($collection->getAllIds() as $customerId) { // Verify customer exists $customer = $this->customerRepository->getById($customerId); - $storeId = (int)$customer->getStoreId(); - $this->subscriptionManager->unsubscribeCustomer($customerId, $storeId); + foreach ($this->getUnsubscribeStoreIds($customer) as $storeId) { + $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); + } $customersUpdated++; } @@ -74,4 +93,24 @@ protected function massAction(AbstractCollection $collection) return $resultRedirect; } + + /** + * Get store ids to unsubscribe customer + * + * @param CustomerInterface $customer + * @return array + */ + private function getUnsubscribeStoreIds(CustomerInterface $customer): array + { + $storeIds = []; + if ($this->shareConfig->isGlobalScope()) { + foreach ($this->storeManager->getStores() as $store) { + $storeIds[(int)$store->getWebsiteId()] = (int)$store->getId(); + } + } else { + $storeIds = [(int)$customer->getStoreId()]; + } + + return $storeIds; + } } From c6697f065e461a7f244c5379603ce2a75feece18 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 5 Dec 2019 14:17:27 +0200 Subject: [PATCH 2365/2437] MC-4242: Newsletter subscriptions per website --- .../Controller/Adminhtml/Index/MassUnsubscribe.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index 723f325cb08c8..05f8a24e30c42 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Config\Share; @@ -48,6 +49,7 @@ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterf * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository * @param SubscriptionManagerInterface $subscriptionManager + * @param StoreManagerInterface $storeManager * @param Share $shareConfig */ public function __construct( @@ -70,13 +72,13 @@ public function __construct( * Customer mass unsubscribe action * * @param AbstractCollection $collection - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect */ protected function massAction(AbstractCollection $collection) { $customersUpdated = 0; foreach ($collection->getAllIds() as $customerId) { - // Verify customer exists + // Verify that customer exists $customer = $this->customerRepository->getById($customerId); foreach ($this->getUnsubscribeStoreIds($customer) as $storeId) { $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); @@ -87,7 +89,7 @@ protected function massAction(AbstractCollection $collection) if ($customersUpdated) { $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath($this->getComponentRefererUrl()); From 74958a2004335596deb3f3a2799f06b6d47b1a1f Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 9 Dec 2019 15:46:11 +0200 Subject: [PATCH 2366/2437] MC-4242: Newsletter subscriptions per website --- .../Customer/Controller/Adminhtml/Index/MassSubscribe.php | 2 +- .../Customer/Controller/Adminhtml/Index/MassUnsubscribe.php | 2 +- .../Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php | 3 ++- .../Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php index 881c5caebcbee..453035ad3151b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php @@ -15,7 +15,7 @@ use Magento\Framework\Controller\ResultFactory; /** - * Class MassSubscribe + * Class to mass subscribe customers by ids */ class MassSubscribe extends AbstractMassAction implements HttpPostActionInterface { diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index 05f8a24e30c42..245e06699e6b0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -19,7 +19,7 @@ use Magento\Eav\Model\Entity\Collection\AbstractCollection; /** - * Class MassUnsubscribe + * Class to mass unsubscribe customers by ids */ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterface { diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index 8f672fbfb4da6..d8b88bba2cdbe 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -26,7 +26,8 @@ use PHPUnit\Framework\TestCase; /** - * Class MassSubscribeTest + * Class to test mass subscribe customers by ids + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MassSubscribeTest extends TestCase diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 303a11989236f..8220d50f418be 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -26,7 +26,8 @@ use PHPUnit\Framework\TestCase; /** - * Class MassUnsubscribeTest + * Class to test mass unsubscribe customers by ids + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MassUnsubscribeTest extends TestCase From 3ccaec554c95517564aa41dd72dc3771e2d94907 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 9 Dec 2019 16:26:37 +0200 Subject: [PATCH 2367/2437] MC-4242: Newsletter subscriptions per website --- .../Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php | 2 +- .../Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php index 22a22742cdb8d..d0ea012a11e1e 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -16,7 +16,7 @@ use Magento\Framework\Data\Form; use Magento\Framework\Data\Form\Element\Checkbox; use Magento\Framework\Data\Form\Element\Fieldset; -use \Magento\Framework\Data\Form\Element\Select; +use Magento\Framework\Data\Form\Element\Select; use Magento\Framework\Data\FormFactory; use Magento\Framework\Registry; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php index a44101da77d6d..9ddba4b994b40 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -9,7 +9,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class NewsletterTest + * Test Customer account form block functionality * * @magentoAppArea adminhtml */ From ecca7514a4a956eea2fa7efb34f9f176f00d1e60 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 10 Dec 2019 10:40:13 +0200 Subject: [PATCH 2368/2437] MC-4242: Newsletter subscriptions per website --- .../Magento/Newsletter/Model/Plugin/CustomerPlugin.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 83bafe5bf2887..60b279b659ca6 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -220,8 +220,11 @@ public function afterDelete(CustomerRepositoryInterface $subject, $result, Custo */ public function afterGetById(CustomerRepositoryInterface $subject, CustomerInterface $customer) { - $isSubscribed = $this->getSubscriber($customer)->isSubscribed(); - $this->addIsSubscribedExtensionAttr($customer, $isSubscribed); + $extensionAttributes = $customer->getExtensionAttributes(); + if ($extensionAttributes === null || $extensionAttributes->getIsSubscribed() === null) { + $isSubscribed = $this->getSubscriber($customer)->isSubscribed(); + $this->addIsSubscribedExtensionAttr($customer, $isSubscribed); + } return $customer; } From f4373bcce058fafbfc818a9e17bd355cd9b1d4ef Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Wed, 11 Dec 2019 15:12:13 +0200 Subject: [PATCH 2369/2437] static-test fix --- .../js/jasmine/tests/lib/mage/browser.test.js | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/dev/tests/js/jasmine/tests/lib/mage/browser.test.js b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js index 2c60497ce1bcc..ce76f5ec02b64 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/browser.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js @@ -33,10 +33,13 @@ define([ spyOn($, 'ajax').and.callFake( function () { return { - done: function (data) { - obj.targetElementId = 1; - } - } + /** + * Succes result of ajax request + */ + done: function () { + obj.targetElementId = 1; + } + }; }); obj.openDialog('instance/url', 100, 100, 'title', options); obj.openDialog('instance/url', 100, 100, 'title', options); @@ -48,11 +51,14 @@ define([ spyOn($, 'ajax').and.callFake( function () { return { - done: function (data) { - obj.targetElementId = 'instance/url'; - obj.modalLoaded = true; - } - } + /** + * Succes result of ajax request + */ + done: function () { + obj.targetElementId = 'instance/url'; + obj.modalLoaded = true; + } + }; }); obj.openDialog('instance/url', 100, 100, 'title', undefined); obj.openDialog('instance/url', 100, 100, 'title', undefined); From d12ab03ef3788575ff69f1dc1ed0d9dd785de2ef Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 11 Dec 2019 21:54:17 +0700 Subject: [PATCH 2370/2437] [InstantPurchase] Cover Ui/CustomerAddressesFormatter and Ui/ShippingMethodFormatter by Unit Test --- .../Ui/CustomerAddressesFormatterTest.php | 56 +++++++++++++++++++ .../Model/Ui/ShippingMethodFormatterTest.php | 46 +++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php create mode 100644 app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php new file mode 100644 index 0000000000000..2abe6b82bc3a9 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\Model\Ui; + +use Magento\InstantPurchase\Model\Ui\CustomerAddressesFormatter; +use Magento\Customer\Model\Address; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Directory\Model\Country; +use PHPUnit\Framework\TestCase; + +class CustomerAddressesFormatterTest extends TestCase +{ + /** + * @var CustomerAddressesFormatter|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerAddressesFormatter; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->customerAddressesFormatter = $objectManager->getObject(CustomerAddressesFormatter::class); + } + + /** + * Test format() + */ + public function testFormat() + { + $addressMock = $this->createPartialMock( + Address::class, + ['getName', 'getStreetFull', 'getCity', 'getRegion', 'getPostcode', 'getCountryModel'] + ); + $countryMock = $this->createMock(Country::class); + + $countryMock->expects($this->any())->method('getName')->willReturn('USA'); + $addressMock->expects($this->any())->method('getName')->willReturn('Address Name'); + $addressMock->expects($this->any())->method('getStreetFull')->willReturn('Address Street Full'); + $addressMock->expects($this->any())->method('getCity')->willReturn('Address City'); + $addressMock->expects($this->any())->method('getRegion')->willReturn('Address Region'); + $addressMock->expects($this->any())->method('getPostcode')->willReturn('Address Postcode'); + $addressMock->expects($this->any())->method('getCountryModel')->willReturn($countryMock); + + $this->assertEquals( + 'Address Name, Address Street Full, Address City, Address Region Address Postcode, USA', + $this->customerAddressesFormatter->format($addressMock) + ); + } +} diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php new file mode 100644 index 0000000000000..632392bcb35e3 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\Model\Ui; + +use Magento\InstantPurchase\Model\Ui\ShippingMethodFormatter; +use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; + +class ShippingMethodFormatterTest extends TestCase +{ + /** + * @var ShippingMethodFormatter|\PHPUnit_Framework_MockObject_MockObject + */ + private $shippingMethodFormatter; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->shippingMethodFormatter = $objectManager->getObject(ShippingMethodFormatter::class); + } + + /** + * Test format() + */ + public function testFormat() + { + $shippingMethodMock = $this->createMock(ShippingMethodInterface::class, ['getCarrierTitle', 'getMethodTitle']); + + $shippingMethodMock->expects($this->any())->method('getCarrierTitle')->willReturn('flatrate'); + $shippingMethodMock->expects($this->any())->method('getMethodTitle')->willReturn('flatrate'); + + $this->assertEquals( + 'flatrate - flatrate', + $this->shippingMethodFormatter->format($shippingMethodMock) + ); + } +} From f8452c7a24b12af236b19b66f7d29b49421d0d54 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 11 Dec 2019 21:59:59 +0700 Subject: [PATCH 2371/2437] [InstantPurchase] Cover Ui/CustomerAddressesFormatter and Ui/ShippingMethodFormatter by Unit Test --- .../Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php index 2abe6b82bc3a9..2a53a36a46cd6 100644 --- a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php +++ b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php @@ -44,12 +44,12 @@ public function testFormat() $addressMock->expects($this->any())->method('getName')->willReturn('Address Name'); $addressMock->expects($this->any())->method('getStreetFull')->willReturn('Address Street Full'); $addressMock->expects($this->any())->method('getCity')->willReturn('Address City'); - $addressMock->expects($this->any())->method('getRegion')->willReturn('Address Region'); - $addressMock->expects($this->any())->method('getPostcode')->willReturn('Address Postcode'); + $addressMock->expects($this->any())->method('getRegion')->willReturn('California'); + $addressMock->expects($this->any())->method('getPostcode')->willReturn('12345'); $addressMock->expects($this->any())->method('getCountryModel')->willReturn($countryMock); $this->assertEquals( - 'Address Name, Address Street Full, Address City, Address Region Address Postcode, USA', + 'Address Name, Address Street Full, Address City, California 12345, USA', $this->customerAddressesFormatter->format($addressMock) ); } From 9dea197f30e4dd40966805296efee6b69982710d Mon Sep 17 00:00:00 2001 From: Adam Mellen <github@mellen.io> Date: Wed, 11 Dec 2019 15:16:04 +0000 Subject: [PATCH 2372/2437] Updating wee -> weee as per https://github.com/magento/devdocs/pull/5911 --- app/code/Magento/Weee/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Weee/README.md b/app/code/Magento/Weee/README.md index 61f9b8d6d8970..ef433ec4c96f9 100644 --- a/app/code/Magento/Weee/README.md +++ b/app/code/Magento/Weee/README.md @@ -2,10 +2,10 @@ The Magento_Weee module enables the application of fees/fixed product taxes (FPT) on certain types of products, usually related to electronic devices and recycling. Fixed product taxes can be used to setup a WEEE tax that is a fixed amount, rather than a percentage of the product price. FPT can be configured to be displayed at various places in Magento. Rules, amounts, and display options can be configured in the backend. This module extends the existing functionality of Magento_Tax. -The Magento_Wee module includes the following: +The Magento_Weee module includes the following: * ability to add different number of fixed product taxes to product. They are treated as a product attribute; -* configuration of where Weee appears (on category, product, sales, invoice, or credit memo pages) and whether FPT should be taxed; +* configuration of where WEEE appears (on category, product, sales, invoice, or credit memo pages) and whether FPT should be taxed; * a new line item in the totals section. # System requirements From 01e0f9168b9724a99724dda89f3ee988c729476e Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 11 Dec 2019 22:28:55 +0700 Subject: [PATCH 2373/2437] [Newsletter] Refactor code and Cover Model/Observer class by Unit Test --- .../Magento/Newsletter/Model/Observer.php | 37 ++++++++--- .../Test/Unit/Model/ObserverTest.php | 63 +++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php diff --git a/app/code/Magento/Newsletter/Model/Observer.php b/app/code/Magento/Newsletter/Model/Observer.php index d77371569601f..a6f8ffd461d08 100644 --- a/app/code/Magento/Newsletter/Model/Observer.php +++ b/app/code/Magento/Newsletter/Model/Observer.php @@ -3,8 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Newsletter\Model; +use Magento\Newsletter\Model\ResourceModel\Queue\Collection; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; + /** * Newsletter module observer * @@ -12,20 +18,35 @@ */ class Observer { + /** + * Number of queue + */ + private const COUNT_OF_QUEUE = 3; + + /** + * Number of subscriptions + */ + private const COUNT_OF_SUBSCRIPTIONS = 20; + + /** + * First page in collection + */ + private const FIRST_PAGE = 1; + /** * Queue collection factory * - * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory + * @var CollectionFactory */ protected $_queueCollectionFactory; /** * Construct * - * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $queueCollectionFactory + * @param CollectionFactory $queueCollectionFactory */ public function __construct( - \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $queueCollectionFactory + CollectionFactory $queueCollectionFactory ) { $this->_queueCollectionFactory = $queueCollectionFactory; } @@ -37,13 +58,11 @@ public function __construct( */ public function scheduledSend() { - $countOfQueue = 3; - $countOfSubscriptions = 20; - - /** @var \Magento\Newsletter\Model\ResourceModel\Queue\Collection $collection */ + /** @var Collection $collection */ $collection = $this->_queueCollectionFactory->create(); - $collection->setPageSize($countOfQueue)->setCurPage(1)->addOnlyForSendingFilter()->load(); + $collection->setPageSize(self::COUNT_OF_QUEUE) + ->setCurPage(self::FIRST_PAGE)->addOnlyForSendingFilter()->load(); - $collection->walk('sendPerSubscriber', [$countOfSubscriptions]); + $collection->walk('sendPerSubscriber', [self::COUNT_OF_SUBSCRIPTIONS]); } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php new file mode 100644 index 0000000000000..7bf6d1e1c8671 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\Observer; +use Magento\Newsletter\Model\ResourceModel\Queue\Collection; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; +use PHPUnit\Framework\TestCase; + +class ObserverTest extends TestCase +{ + /** + * @var Observer + */ + private $model; + + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $collectionFactoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + + $this->collectionFactoryMock = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); + + $this->model = $objectManager->getObject( + Observer::class, + [ + 'queueCollectionFactory' => $this->collectionFactoryMock + ] + ); + } + + /** + * Test scheduledSend() method + */ + public function testScheduledSend() + { + $collectionMock = $this->createMock(Collection::class); + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($collectionMock); + $collectionMock->expects($this->once())->method('setPageSize')->with(3)->willReturnSelf(); + $collectionMock->expects($this->once())->method('setCurPage')->with(1)->willReturnSelf(); + $collectionMock->expects($this->once())->method('addOnlyForSendingFilter')->willReturnSelf(); + $collectionMock->expects($this->once())->method('load')->willReturnSelf(); + $collectionMock->expects($this->once())->method('walk')->with('sendPerSubscriber', [20]); + + $this->model->scheduledSend(); + } +} From 29f0417a28ad71d4dd42485b88da125ccd960a2e Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Wed, 11 Dec 2019 18:08:28 +0200 Subject: [PATCH 2374/2437] MC-25098: Product is not deleted from minicart if not included in shared catalog --- app/code/Magento/Checkout/Model/Session.php | 4 +++ .../Magento/Checkout/Model/SessionTest.php | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/code/Magento/Checkout/Model/Session.php b/app/code/Magento/Checkout/Model/Session.php index 4a4861fa9ccd2..7af00f1df8e95 100644 --- a/app/code/Magento/Checkout/Model/Session.php +++ b/app/code/Magento/Checkout/Model/Session.php @@ -272,6 +272,10 @@ public function getQuote() */ $quote = $this->quoteRepository->get($this->getQuoteId()); } + + if ($quote->getTotalsCollectedFlag() === false) { + $quote->collectTotals(); + } } catch (NoSuchEntityException $e) { $this->setQuoteId(null); } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php index 4682453012952..c7802f73a9a47 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\Data\ProductTierPriceInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\Api\FilterBuilder; @@ -54,6 +55,35 @@ protected function setUp() $this->checkoutSession = $this->objectManager->create(Session::class); } + /** + * Tests that quote items and totals are correct when product becomes unavailable. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoAppIsolation enabled + */ + public function testGetQuoteWithUnavailableProduct() + { + $reservedOrderId = 'test01'; + $quoteGrandTotal = 10; + + $quote = $this->getQuote($reservedOrderId); + $this->assertEquals(1, $quote->getItemsCount()); + $this->assertCount(1, $quote->getItems()); + $this->assertEquals($quoteGrandTotal, $quote->getShippingAddress()->getBaseGrandTotal()); + + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); + $product->setStatus(Status::STATUS_DISABLED); + $productRepository->save($product); + $this->checkoutSession->setQuoteId($quote->getId()); + $quote = $this->checkoutSession->getQuote(); + + $this->assertEquals(0, $quote->getItemsCount()); + $this->assertEmpty($quote->getItems()); + $this->assertEquals(0, $quote->getShippingAddress()->getBaseGrandTotal()); + } + /** * Test covers case when quote is not yet initialized and customer data is set to checkout session model. * From 5a1e0fff4c94a732e327ae4aec969365185f0289 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 11 Dec 2019 10:17:12 -0600 Subject: [PATCH 2375/2437] MQE-1921: [MTF-To-MFTF] Process PR 711 --- .../StorefrontClickHeaderLinkActionGroup.xml | 6 +-- .../StorefrontSeeHeaderLinksActionGroup.xml | 4 +- .../Mftf/Section/StorefrontHeaderSection.xml | 3 +- ...teAccountPasswordComplexityActionGroup.xml | 2 +- .../StorefrontCustomerCreateFormSection.xml | 2 +- .../NewCustomerPasswordComplexityTest.xml | 32 ++++++-------- .../Test/NewCustomerPasswordLengthTest.xml | 43 +++++++++++++++++++ .../NewCustomerPasswordComplexityTest.xml | 6 +-- 8 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml index 46c06e909b4d9..052a5ac6a14e6 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml @@ -9,9 +9,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontClickHeaderLinkActionGroup"> <arguments> - <argument name="LinkName" type="string" defaultValue="Create an Account"/> + <argument name="linkName" type="string" defaultValue="Create an Account"/> </arguments> - <click stepKey="ClickTheLink" selector="{{StorefrontHeaderSection.HeaderLinkByText(LinkName)}}"/> - <waitForPageLoad stepKey="Wait"/> + <click stepKey="clickTheLink" selector="{{StorefrontHeaderSection.headerLinkByText(linkName)}}"/> + <waitForPageLoad stepKey="wait"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml index 3155cca583d59..f4498e3b6cdd7 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml @@ -9,8 +9,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSeeHeaderLinksActionGroup"> <arguments> - <argument name="LinkName" type="string" defaultValue="Create an Account"/> + <argument name="linkName" type="string" defaultValue="Create an Account"/> </arguments> - <see stepKey="SeeElement" selector="{{StorefrontHeaderSection.headerlinks}}" userInput="{{LinkName}}"/> + <see stepKey="seeElement" selector="{{StorefrontHeaderSection.headerlinks}}" userInput="{{linkName}}"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml index fefde1e6035f3..7b47c6b49db7b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml @@ -10,7 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontHeaderSection"> <element name="headerlinks" type="text" selector="ul.header.links"/> - <element name="HeaderLinkByText" type="text" selector="//ul[contains(@class, 'header') and contains(@class, 'links')]/li/a[contains(text(),'{{LinkName}}')]" - parameterized="true" /> + <element name="headerLinkByText" type="text" selector="//ul[contains(@class, 'header') and contains(@class, 'links')]/li/a[contains(text(),'{{LinkName}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml index 9d7dbc604f59d..95a03042bec3b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml @@ -12,6 +12,6 @@ <arguments> <argument name="message" type="string"/> </arguments> - <see userInput="{{message}}" selector="{{StorefrontCustomerCreateFormSection.PasswordErrorMessages}}" stepKey="verifyMessage" /> + <see userInput="{{message}}" selector="{{StorefrontCustomerCreateFormSection.passwordErrorMessages}}" stepKey="verifyMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml index 17048dda688e6..9fc26a03b04ee 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -17,7 +17,7 @@ <element name="passwordField" type="input" selector="#password"/> <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> - <element name="PasswordErrorMessages" type="text" selector="#password-error"/> + <element name="passwordErrorMessages" type="text" selector="#password-error"/> </section> <section name="StoreFrontCustomerAdvancedAttributesSection"> <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml index ccd97f83cd0a6..74a9c68cb2f79 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -11,39 +11,33 @@ <test name="NewCustomerPasswordComplexityTest"> <annotations> <features value="Security"/> - <stories value="Checking customer's password length and password complexity"/> - <title value="Notify the customer if password length or complexity is not match to requirements"/> - <description value="Show notifies to the customer if password length or complexity is not match to requirements"/> + <stories value="Checking customer's password complexity"/> + <title value="Notify the customer if password complexity does not match the requirements"/> + <description value="Notify the customer if password complexity does not match the requirements"/> + <testCaseId value="MC-14368"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> - <!-- TEST BODY --> <!-- Go to storefront home page --> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + <!-- See the Registration Link --> - <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="SeeTheLink"/> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="seeTheLink"/> + <!-- Click the Registration Link --> - <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="ClickTheLink"> - <argument name="LinkName" value="Create an Account"/> - </actionGroup> - <!-- Fill Registration Form with Password length is bellow 8 Characters --> - <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="FillRegistrationFormPasswordLengthBellowEightCharacters"> - <argument name="customer" value="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters"/> - </actionGroup> - <!-- See the Error --> - <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="SeeTheErrorPasswordLength"> - <argument name="message" value="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored."/> + <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="clickTheLink"> + <argument name="linkName" value="Create an Account"/> </actionGroup> + <!-- Fill Registration Form with not secure enough password --> - <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="FillRegistrationFormPasswordNotSecure"> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillRegistrationFormPasswordNotSecure"> <argument name="customer" value="Simple_Customer_With_Not_Secure_Password"/> </actionGroup> + <!-- See the Error --> - <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="SeeTheErrorPasswordSecure"> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="seeTheErrorPasswordSecure"> <argument name="message" value="Minimum of different classes of characters in password is 3. Classes of characters: Lower Case, Upper Case, Digits, Special Characters."/> </actionGroup> - <!--Test Body END--> - </test> </tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml new file mode 100644 index 0000000000000..a10059d0603c5 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewCustomerPasswordLengthTest"> + <annotations> + <features value="Security"/> + <stories value="Checking customer's password length"/> + <title value="Notify the customer if password length does not match the requirements"/> + <description value="Notify the customer if password length does not match the requirements"/> + <testCaseId value="MC-14367"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + + <!-- See the Registration Link --> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="seeTheLink"/> + + <!-- Click the Registration Link --> + <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="clickTheLink"> + <argument name="linkName" value="Create an Account"/> + </actionGroup> + + <!-- Fill Registration Form with Password length is bellow 8 Characters --> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillRegistrationFormPasswordLengthBellowEightCharacters"> + <argument name="customer" value="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters"/> + </actionGroup> + + <!-- See the Error --> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="seeTheErrorPasswordLength"> + <argument name="message" value="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored."/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml index 7b6f3e981714c..534f692a36aef 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\NewCustomerPasswordComplexityTest" summary="New customer password complexity" ticketId="MAGETWO-49044"> <variation name="PasswordLengthTest" summary="Customer password length is below 8 characters"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">john</data> <data name="customer/data/lastname" xsi:type="string">doe</data> <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> @@ -16,10 +16,9 @@ <data name="customer/data/password" xsi:type="string">123123</data> <data name="customer/data/password_confirmation" xsi:type="string">123123</data> <constraint name="Magento\Security\Test\Constraint\AssertPasswordLengthErrorMessage" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> <variation name="PasswordComplexityTest" summary="Customer password is not secure enough"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">john</data> <data name="customer/data/lastname" xsi:type="string">doe</data> <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> @@ -27,7 +26,6 @@ <data name="customer/data/password" xsi:type="string">123123qa</data> <data name="customer/data/password_confirmation" xsi:type="string">123123qa</data> <constraint name="Magento\Security\Test\Constraint\AssertPasswordIsNotSecureEnoughMessage" /> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> From 0dd0db5943415f8892d5ee1e3b2811caaf679351 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 11 Dec 2019 10:47:23 -0600 Subject: [PATCH 2376/2437] MQE-1921: [MTF-To-MFTF] Process PR 711 - Add annotations to new action groups --- .../Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml | 4 ++++ .../Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml | 3 +++ ...sageCustomerCreateAccountPasswordComplexityActionGroup.xml | 3 +++ 3 files changed, 10 insertions(+) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml index 052a5ac6a14e6..48e7ec81ae1c1 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml @@ -8,9 +8,13 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontClickHeaderLinkActionGroup"> + <annotations> + <description>Clicks a link in the storefront header.</description> + </annotations> <arguments> <argument name="linkName" type="string" defaultValue="Create an Account"/> </arguments> + <click stepKey="clickTheLink" selector="{{StorefrontHeaderSection.headerLinkByText(linkName)}}"/> <waitForPageLoad stepKey="wait"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml index f4498e3b6cdd7..ec5c388ee1c6b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml @@ -8,6 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSeeHeaderLinksActionGroup"> + <annotations> + <description>See a link by name in the storefront header.</description> + </annotations> <arguments> <argument name="linkName" type="string" defaultValue="Create an Account"/> </arguments> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml index 95a03042bec3b..b62f5435275a3 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml @@ -9,6 +9,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup"> + <annotations> + <description>Assert is shown an error about their password during new user form filling.</description> + </annotations> <arguments> <argument name="message" type="string"/> </arguments> From 0b7b23616d1a391125cd7cb3230f09ac28572441 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 11 Dec 2019 11:04:02 -0600 Subject: [PATCH 2377/2437] MQE-1857: [MTF-To-MFTF] Process PR 746 --- .../AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml | 9 +++++---- ...nDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml | 8 ++++---- ...nDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml | 8 ++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml index 4c3bd8179578c..b6cd116956812 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -6,16 +6,16 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest"> <annotations> <stories value="Delete CMS Page URL rewrite with No Redirects"/> <title value="Delete CMS Page URL rewrite with No Redirects"/> <description value="Log in to admin and delete CMS Page URL rewrite with No Redirects"/> - <group value="cMSContent"/> + <testCaseId value="MC-14648"/> <group value="mtf_migrated"/> </annotations> - <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> @@ -46,14 +46,15 @@ <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="You deleted the URL rewrite."/> </actionGroup> + <!--Search and verify AssertUrlRewriteNotInGrid--> <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedUrlRewriteInGrid"> <argument name="requestPath" value="newrequestpath"/> </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> <argument name="requestPath" value="newrequestpath"/> </actionGroup> - </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml index 89f663b54e017..1f76493bac89c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -6,16 +6,16 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest"> <annotations> <stories value="Delete CMS Page URL rewrite with Permanent Redirect"/> <title value="Delete CMS Page URL rewrite with Permanent Redirect"/> <description value="Log in to admin and delete CMS Page URL rewrite with Permanent Redirect"/> - <group value="cMSContent"/> + <testCaseId value="MC-14649"/> <group value="mtf_migrated"/> </annotations> - <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> @@ -46,10 +46,10 @@ <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="You deleted the URL rewrite."/> </actionGroup> + <!-- Verify AssertPageByUrlRewriteIsNotFound --> <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> <argument name="requestPath" value="permanentrequestpath.html"/> </actionGroup> - </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml index ad7aec335b7a3..c62f0ece7e780 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -6,16 +6,16 @@ */ --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest"> <annotations> <stories value="Delete CMS Page URL rewrite with Temporary Redirect"/> <title value="Delete CMS Page URL rewrite with Temporary Redirect"/> <description value="Log in to admin and delete CMS Page URL rewrite with Temporary Redirect"/> - <group value="cMSContent"/> + <testCaseId value="MC-14650"/> <group value="mtf_migrated"/> </annotations> - <before> <createData entity="simpleCmsPage" stepKey="createCMSPage"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> @@ -46,10 +46,10 @@ <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> <argument name="message" value="You deleted the URL rewrite."/> </actionGroup> + <!--Verify AssertPageByUrlRewriteIsNotFound--> <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="assertPageByUrlRewriteIsNotFound"> <argument name="requestPath" value="temporaryrequestpath.html"/> </actionGroup> - </test> </tests> From 26e5774754cd2ce52707bd1327345c6a89f12d19 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun <d.cheshun@atwix.com> Date: Wed, 11 Dec 2019 21:06:27 +0200 Subject: [PATCH 2378/2437] Minor code style fixes --- .../js/jasmine/tests/lib/mage/browser.test.js | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/dev/tests/js/jasmine/tests/lib/mage/browser.test.js b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js index ce76f5ec02b64..06f355908daca 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/browser.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/browser.test.js @@ -12,7 +12,6 @@ define([ var obj; beforeEach(function () { - /** * Dummy constructor to use for instantiation * @constructor @@ -27,14 +26,14 @@ define([ describe('"openDialog" method', function () { it('Opens dialog with provided targetElementId', function () { var options = { - 'targetElementId': 1 - }; + 'targetElementId': 1 + }; spyOn($, 'ajax').and.callFake( - function () { + function () { return { /** - * Succes result of ajax request + * Success result of ajax request */ done: function () { obj.targetElementId = 1; @@ -44,22 +43,21 @@ define([ obj.openDialog('instance/url', 100, 100, 'title', options); obj.openDialog('instance/url', 100, 100, 'title', options); expect($.ajax.calls.count()).toBe(1); - }); it('Opens dialog with provided url param', function () { spyOn($, 'ajax').and.callFake( - function () { - return { - /** - * Succes result of ajax request - */ - done: function () { - obj.targetElementId = 'instance/url'; - obj.modalLoaded = true; - } - }; - }); + function () { + return { + /** + * Success result of ajax request + */ + done: function () { + obj.targetElementId = 'instance/url'; + obj.modalLoaded = true; + } + }; + }); obj.openDialog('instance/url', 100, 100, 'title', undefined); obj.openDialog('instance/url', 100, 100, 'title', undefined); expect($.ajax.calls.count()).toBe(1); From 16ab63b347dd4628ed1cc1947929ff44b6bad90c Mon Sep 17 00:00:00 2001 From: Lukasz Lewandowski <luklewluk@gmail.com> Date: Wed, 11 Dec 2019 15:25:39 -0600 Subject: [PATCH 2379/2437] Fix caching Product Metadata getVersion --- lib/internal/Magento/Framework/App/ProductMetadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php index 052119713294b..8f989351743d2 100644 --- a/lib/internal/Magento/Framework/App/ProductMetadata.php +++ b/lib/internal/Magento/Framework/App/ProductMetadata.php @@ -85,8 +85,8 @@ public function getVersion() } else { $this->version = 'UNKNOWN'; } - $this->cache->save($this->version, self::VERSION_CACHE_KEY, [Config::CACHE_TAG]); } + $this->cache->save($this->version, self::VERSION_CACHE_KEY, [Config::CACHE_TAG]); } return $this->version; } From 09136b5ff8f0b4f1f78163ce4ab142b33a99f60d Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 11 Dec 2019 15:55:27 -0600 Subject: [PATCH 2380/2437] MQE-1857: [MTF-To-MFTF] Process PR 746 - Try to fix some flakiness --- .../Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml | 2 ++ .../Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml index 94d488f216b49..eb4f2b5c25281 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml @@ -31,6 +31,8 @@ <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="ProductWithUnicode"/> </actionGroup> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="product" value="ProductWithUnicode"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 418c0e72dc1fc..4b618495f19d5 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -80,6 +80,7 @@ <waitForPageLoad stepKey="waitForResultPage"/> <!-- Perform all assertions: assert refund success create message --> + <waitForElementVisible selector="{{AdminIndexManagementSection.successMessage}}" stepKey="waitForSuccessMessage"/> <see selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the credit memo." stepKey="assertRefundSuccessCreateMessage"/> <!-- Assert Credit Memo button --> From 264e6e6e86f4b0c7e17436ad72e331599a72c4ec Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Wed, 11 Dec 2019 21:27:49 -0600 Subject: [PATCH 2381/2437] Fix static --- .../Sales/view/adminhtml/templates/order/address/form.phtml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml index b794c418de8d9..0cc23056b3c2f 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml @@ -7,7 +7,11 @@ <div class="messages"> <div class="message message-notice"> <div class="message-inner"> - <div class="message-content"><?= $block->escapeHtml(__('Changing address information will not recalculate shipping, tax or other order amount.')) ?></div> + <div class="message-content"> + <?= $block->escapeHtml( + __('Changing address information will not recalculate shipping, tax or other order amount.') + ) ?> + </div> </div> </div> </div> From 32fb4d3b02ebc1529b29a2158e7ccf59cd4384f8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 12 Dec 2019 09:38:43 +0200 Subject: [PATCH 2382/2437] MC-5233: DateTime product attributes support --- .../CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 3b60e4b09de28..1cec03a4c765e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -308,6 +308,10 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> From ae8cb2cc39828276cb60e602a0dd878774beff7b Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 12 Dec 2019 13:15:12 +0200 Subject: [PATCH 2383/2437] MC-25098: Product is not deleted from minicart if not included in shared catalog --- .../testsuite/Magento/Checkout/Model/SessionTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php index c7802f73a9a47..e0e390e89c97c 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php @@ -18,7 +18,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class SessionTest + * Checkout Session model test. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ From b67ab4377e58cb977e1ec95c86607943bad1144e Mon Sep 17 00:00:00 2001 From: Mastiuhin Olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Thu, 12 Dec 2019 14:03:00 +0200 Subject: [PATCH 2384/2437] MC-29578: A lot of identical reviews are created after many times clicking on Submit Review button --- .../Review/view/frontend/templates/form.phtml | 14 ++++++++------ .../view/frontend/web/js/submit-review.js | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Review/view/frontend/web/js/submit-review.js diff --git a/app/code/Magento/Review/view/frontend/templates/form.phtml b/app/code/Magento/Review/view/frontend/templates/form.phtml index f1340945043e9..6b00bf681c1e3 100644 --- a/app/code/Magento/Review/view/frontend/templates/form.phtml +++ b/app/code/Magento/Review/view/frontend/templates/form.phtml @@ -5,28 +5,29 @@ */ /** @var \Magento\Review\Block\Form $block */ +//phpcs:disable Generic.Files.LineLength ?> <div class="block review-add"> <div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div> <div class="block-content"> -<?php if ($block->getAllowWriteReviewFlag()) : ?> +<?php if ($block->getAllowWriteReviewFlag()):?> <form action="<?= $block->escapeUrl($block->getAction()) ?>" class="review-form" method="post" id="review-form" data-role="product-review-form" data-bind="scope: 'review-form'"> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_fields_before') ?> <fieldset class="fieldset review-fieldset" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"> <legend class="legend review-legend"><span><?= $block->escapeHtml(__("You're reviewing:")) ?></span><strong><?= $block->escapeHtml($block->getProductInfo()->getName()) ?></strong></legend><br /> - <?php if ($block->getRatings() && $block->getRatings()->getSize()) : ?> + <?php if ($block->getRatings() && $block->getRatings()->getSize()): ?> <span id="input-message-box"></span> <fieldset class="field required review-field-ratings"> <legend class="label"><span><?= $block->escapeHtml(__('Your Rating')) ?></span></legend><br/> <div class="control"> <div class="nested" id="product-review-table"> - <?php foreach ($block->getRatings() as $_rating) : ?> + <?php foreach ($block->getRatings() as $_rating): ?> <div class="field choice review-field-rating"> <label class="label" id="<?= $block->escapeHtml($_rating->getRatingCode()) ?>_rating_label"><span><?= $block->escapeHtml($_rating->getRatingCode()) ?></span></label> <div class="control review-control-vote"> <?php $options = $_rating->getOptions();?> - <?php $iterator = 1; foreach ($options as $_option) : ?> + <?php $iterator = 1; foreach ($options as $_option): ?> <input type="radio" name="ratings[<?= $block->escapeHtmlAttr($_rating->getId()) ?>]" @@ -84,11 +85,12 @@ }, "#review-form": { "Magento_Review/js/error-placement": {}, - "Magento_Review/js/validate-review": {} + "Magento_Review/js/validate-review": {}, + "Magento_Review/js/submit-review": {} } } </script> -<?php else : ?> +<?php else: ?> <div class="message info notlogged" id="review-form"> <div> <?= $block->escapeHtml(__('Only registered users can write reviews. Please <a href="%1">Sign in</a> or <a href="%2">create an account</a>', $block->getLoginLink(), $block->getRegisterUrl()), ['a']) ?> diff --git a/app/code/Magento/Review/view/frontend/web/js/submit-review.js b/app/code/Magento/Review/view/frontend/web/js/submit-review.js new file mode 100644 index 0000000000000..6399ce22ffe88 --- /dev/null +++ b/app/code/Magento/Review/view/frontend/web/js/submit-review.js @@ -0,0 +1,18 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery' +], function ($) { + 'use strict'; + + return function (config, element) { + $(element).on('submit', function () { + if ($(this).valid()) { + $(this).find('.submit').attr('disabled', true); + } + }); + }; +}); From 3096b56dcebdd96d864ea09059997e4021333403 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Thu, 12 Dec 2019 15:12:50 +0200 Subject: [PATCH 2385/2437] MC-25257: Special from / to date wrong on administrator Dutch locale --- .../Product/Initialization/Helper.php | 55 +++-------- .../Catalog/Model/Product/Filter/DateTime.php | 67 ++++++++++++++ .../Product/Initialization/HelperTest.php | 78 ++++++++++------ .../Model/Product/Filter/DateTimeTest.php | 91 +++++++++++++++++++ 4 files changed, 219 insertions(+), 72 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Filter/DateTime.php create mode 100644 app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index 62bcb1c8cd6d7..2ae97223d6359 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -6,7 +6,6 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Initialization; -use DateTime; use Magento\Backend\Helper\Js; use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory as CustomOptionFactory; use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory as ProductLinkFactory; @@ -15,6 +14,8 @@ use Magento\Catalog\Api\ProductRepositoryInterface\Proxy as ProductRepository; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Authorization as ProductAuthorization; +use Magento\Catalog\Model\Product\Filter\DateTime as DateTimeFilter; use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks; use Magento\Catalog\Model\Product\Link\Resolver as LinkResolver; use Magento\Catalog\Model\Product\LinkTypeProvider; @@ -24,13 +25,13 @@ use Magento\Framework\Stdlib\DateTime\Filter\Date; use Magento\Store\Model\StoreManagerInterface; use Zend_Filter_Input; -use Magento\Catalog\Model\Product\Authorization as ProductAuthorization; /** * Product helper * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) * @since 100.0.2 */ class Helper @@ -89,11 +90,6 @@ class Helper */ private $linkResolver; - /** - * @var \Magento\Framework\Stdlib\DateTime\Filter\DateTime - */ - private $dateTimeFilter; - /** * @var LinkTypeProvider */ @@ -114,6 +110,11 @@ class Helper */ private $localeFormat; + /** + * @var DateTimeFilter + */ + private $dateTimeFilter; + /** * Constructor * @@ -130,6 +131,7 @@ class Helper * @param AttributeFilter|null $attributeFilter * @param FormatInterface|null $localeFormat * @param ProductAuthorization|null $productAuthorization + * @param DateTimeFilter|null $dateTimeFilter * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -145,7 +147,8 @@ public function __construct( LinkTypeProvider $linkTypeProvider = null, AttributeFilter $attributeFilter = null, FormatInterface $localeFormat = null, - ?ProductAuthorization $productAuthorization = null + ?ProductAuthorization $productAuthorization = null, + ?DateTimeFilter $dateTimeFilter = null ) { $this->request = $request; $this->storeManager = $storeManager; @@ -162,6 +165,7 @@ public function __construct( $this->attributeFilter = $attributeFilter ?: $objectManager->get(AttributeFilter::class); $this->localeFormat = $localeFormat ?: $objectManager->get(FormatInterface::class); $this->productAuthorization = $productAuthorization ?? $objectManager->get(ProductAuthorization::class); + $this->dateTimeFilter = $dateTimeFilter ?? $objectManager->get(DateTimeFilter::class); } /** @@ -185,7 +189,6 @@ public function initializeFromData(Product $product, array $productData) } $productData = $this->normalize($productData); - $productData = $this->convertSpecialFromDateStringToObject($productData); if (!empty($productData['is_downloadable'])) { $productData['product_has_weight'] = 0; @@ -209,7 +212,7 @@ public function initializeFromData(Product $product, array $productData) foreach ($attributes as $attrKey => $attribute) { if ($attribute->getBackend()->getType() == 'datetime') { if (array_key_exists($attrKey, $productData) && $productData[$attrKey] != '') { - $dateFieldFilters[$attrKey] = $this->getDateTimeFilter(); + $dateFieldFilters[$attrKey] = $this->dateTimeFilter; } } } @@ -408,22 +411,6 @@ private function getLinkResolver() return $this->linkResolver; } - /** - * Get DateTimeFilter instance - * - * @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime - * @deprecated 101.0.0 - */ - private function getDateTimeFilter() - { - if ($this->dateTimeFilter === null) { - $this->dateTimeFilter = ObjectManager::getInstance() - ->get(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); - } - - return $this->dateTimeFilter; - } - /** * Remove ids of non selected websites from $websiteIds array and return filtered data * @@ -497,20 +484,4 @@ function ($valueData) { return $product->setOptions($customOptions); } - - /** - * Convert string date presentation into object - * - * @param array $productData - * @return array - */ - private function convertSpecialFromDateStringToObject($productData) - { - if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { - $productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']); - $productData['special_from_date'] = new DateTime($productData['special_from_date']); - } - - return $productData; - } } diff --git a/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php b/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php new file mode 100644 index 0000000000000..93b7d55458a9f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Filter; + +use Magento\Framework\Stdlib\DateTime as StdlibDateTime; +use Magento\Framework\Stdlib\DateTime\Filter\DateTime as StdlibDateTimeFilter; + +/** + * Product datetime fields values filter + */ +class DateTime implements \Zend_Filter_Interface +{ + /** + * @var StdlibDateTimeFilter + */ + private $stdlibDateTimeFilter; + + /** + * Initializes dependencies. + * + * @param StdlibDateTimeFilter $stdlibDateTimeFilter + */ + public function __construct(StdlibDateTimeFilter $stdlibDateTimeFilter) + { + $this->stdlibDateTimeFilter = $stdlibDateTimeFilter; + } + + /** + * Convert datetime from locale format to internal format; + * + * Make an additional check for MySql date format which is wrongly parsed by IntlDateFormatter + * + * @param mixed $value + * @return mixed|string + * @throws \Exception + */ + public function filter($value) + { + if (is_string($value)) { + $value = $this->createDateFromMySqlFormat($value) ?? $value; + } + return $this->stdlibDateTimeFilter->filter($value); + } + + /** + * Parse a string in MySql date format into a new DateTime object + * + * @param string $value + * @return \DateTime|null + */ + private function createDateFromMySqlFormat(string $value): ?\DateTime + { + $datetime = date_create_from_format(StdlibDateTime::DATETIME_PHP_FORMAT, $value); + if ($datetime === false) { + $datetime = date_create_from_format(StdlibDateTime::DATE_PHP_FORMAT, $value); + if ($datetime !== false) { + $datetime->setTime(0, 0, 0, 0); + } + } + return $datetime instanceof \DateTime ? $datetime : null; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index 134c6f9edeaf7..2aea34244437d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -165,6 +165,8 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->dateTimeFilterMock = $this->createMock(\Magento\Catalog\Model\Product\Filter\DateTime::class); + $this->helper = $this->objectManager->getObject( Helper::class, [ @@ -178,6 +180,7 @@ protected function setUp() 'linkTypeProvider' => $this->linkTypeProviderMock, 'attributeFilter' => $this->attributeFilterMock, 'localeFormat' => $this->localeFormatMock, + 'dateTimeFilter' => $this->dateTimeFilterMock ] ); @@ -188,11 +191,6 @@ protected function setUp() $resolverProperty = $helperReflection->getProperty('linkResolver'); $resolverProperty->setAccessible(true); $resolverProperty->setValue($this->helper, $this->linkResolverMock); - - $this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); - $dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter'); - $dateTimeFilterProperty->setAccessible(true); - $dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock); } /** @@ -226,6 +224,7 @@ public function testInitialize( ]; $specialFromDate = '2018-03-03 19:30:00'; $productData = [ + 'name' => 'Simple Product', 'stock_data' => ['stock_data'], 'options' => $optionsData, 'website_ids' => $websiteIds, @@ -235,30 +234,23 @@ public function testInitialize( $productData = array_merge($productData, ['tier_price' => $tierPrice]); } - $this->dateTimeFilterMock->expects($this->once()) + $this->dateTimeFilterMock + ->expects($this->once()) ->method('filter') - ->with($specialFromDate) - ->willReturn($specialFromDate); - - $attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - $attributeDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - - $attributeNonDateBackEnd = - $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $attributeDateBackEnd = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class) - ->disableOriginalConstructor() - ->getMock(); + ->willReturnArgument(0); - $attributeNonDate->expects($this->any())->method('getBackend')->willReturn($attributeNonDateBackEnd); - $attributeDate->expects($this->any())->method('getBackend')->willReturn($attributeDateBackEnd); - $attributeNonDateBackEnd->expects($this->any())->method('getType')->willReturn('non-datetime'); - $attributeDateBackEnd->expects($this->any())->method('getType')->willReturn('datetime'); + $this->setProductAttributes( + [ + [ + 'code' => 'name', + 'backend_type' => 'varchar', + ], + [ + 'code' => 'special_from_date', + 'backend_type' => 'datetime', + ] + ] + ); $useDefaults = ['attributeCode1', 'attributeCode2']; @@ -274,8 +266,6 @@ public function testInitialize( $this->productMock->expects($this->once())->method('isLockedAttribute')->with('media')->willReturn(true); $this->productMock->expects($this->once())->method('unlockAttribute')->with('media'); $this->productMock->expects($this->once())->method('lockAttribute')->with('media'); - $this->productMock->expects($this->once())->method('getAttributes') - ->willReturn([$attributeNonDate, $attributeDate]); $this->productMock->expects($this->any())->method('getSku')->willReturn('sku'); $this->productMock->expects($this->any())->method('getOptionsReadOnly')->willReturn(false); @@ -348,7 +338,35 @@ function () { } $this->assertEquals($expectedLinks, $resultLinks); - $this->assertEquals($specialFromDate, $productData['special_from_date']); + $this->assertEquals($specialFromDate, $this->productMock->getSpecialFromDate()); + } + + /** + * Mock product attributes + * + * @param array $attributes + */ + private function setProductAttributes(array $attributes): void + { + $attributesModels = []; + foreach ($attributes as $attribute) { + $attributeModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + $backendModel = $attribute['backend_model'] + ?? $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class); + $attributeModel->expects($this->any()) + ->method('getBackend') + ->willReturn($backendModel); + $attributeModel->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attribute['code']); + $backendModel->expects($this->any()) + ->method('getType') + ->willReturn($attribute['backend_type']); + $attributesModels[$attribute['code']] = $attributeModel; + } + $this->productMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributesModels); } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php new file mode 100644 index 0000000000000..aefa0b1cf106d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Model\Product\Filter; + +use Magento\Framework\Locale\Resolver; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Stdlib\DateTime\Timezone; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test datetime filter + */ +class DateTimeTest extends TestCase +{ + /** + * @var string + */ + private $locale; + /** + * @var \Magento\Catalog\Model\Product\Filter\DateTime + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $objectManager = new ObjectManager($this); + $this->locale = Resolver::DEFAULT_LOCALE; + $localeResolver = $this->getMockForAbstractClass(ResolverInterface::class); + $localeResolver->expects($this->any()) + ->method('getLocale') + ->willReturnCallback( + function () { + return $this->locale; + } + ); + $timezone = $objectManager->getObject( + Timezone::class, + ['localeResolver' => $localeResolver] + ); + $stdlibDateTimeFilter = $objectManager->getObject( + \Magento\Framework\Stdlib\DateTime\Filter\DateTime::class, + ['localeDate' => $timezone] + ); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Filter\DateTime::class, + [ + 'stdlibDateTimeFilter' => $stdlibDateTimeFilter + ] + ); + } + + /** + * Test filter with different dates formats and locales + * + * @dataProvider provideFilter + */ + public function testFilter(string $date, string $expectedDate, string $locale = Resolver::DEFAULT_LOCALE) + { + $this->locale = $locale; + $this->assertEquals($expectedDate, $this->model->filter($date)); + } + + /** + * Provide date formats and locales + * + * @return array + */ + public function provideFilter(): array + { + return [ + ['1999-12-31', '1999-12-31 00:00:00', 'en_US'], + ['12-31-1999', '1999-12-31 00:00:00', 'en_US'], + ['12/31/1999', '1999-12-31 00:00:00', 'en_US'], + ['December 31, 1999', '1999-12-31 00:00:00', 'en_US'], + ['1999-12-31', '1999-12-31 00:00:00', 'fr_FR'], + ['31-12-1999', '1999-12-31 00:00:00', 'fr_FR'], + ['31/12/1999', '1999-12-31 00:00:00', 'fr_FR'], + ['31 Décembre 1999', '1999-12-31 00:00:00', 'fr_FR'], + ]; + } +} From 97d2358bb6b2f9f85dcd1656aca7510d1455ecd0 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 12 Dec 2019 09:22:57 -0600 Subject: [PATCH 2386/2437] MQE-1857: [MTF-To-MFTF] Process PR 746 - Remove unnecessary hardcoded ID reference --- .../AdminFilteringCategoryProductsUsingScopeSelectorTest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index 41b446b474078..501a5bd2282be 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -131,7 +131,6 @@ userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName5"/> - <waitForText userInput="$$createCategory.name$$ (ID: 6) (2)" stepKey="seeCorrectProductCount"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" @@ -151,7 +150,6 @@ userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName7"/> - <waitForText userInput="$$createCategory.name$$ (ID: 6) (2)" stepKey="seeCorrectProductCount2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" From efa362f0b2d4abf6d2f17f64e5e55f6e45288821 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Thu, 12 Dec 2019 23:06:28 +0700 Subject: [PATCH 2387/2437] [DownloadableImportExport] Cover Helper Data by Unit Test --- .../Test/Unit/Helper/DataTest.php | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..56dc11733cec4 --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php @@ -0,0 +1,278 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\DownloadableImportExport\Test\Unit\Helper; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\DownloadableImportExport\Helper\Data as HelperData; +use PHPUnit\Framework\TestCase; + +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManagerHelper($this); + $this->helper = $objectManagerHelper->getObject(HelperData::class); + } + + /** + * Test isRowDownloadableEmptyOptions with dataProvider + * + * @param array $rowData + * @param bool $expected + * @dataProvider isRowDownloadableEmptyOptionsDataProvider + */ + public function testIsRowDownloadableEmptyOptions($rowData, $expected) + { + $this->assertEquals($expected, $this->helper->isRowDownloadableEmptyOptions($rowData)); + } + + /** + * Data Provider to test isRowDownloadableEmptyOptions + * + * @return array + */ + public function isRowDownloadableEmptyOptionsDataProvider() + { + return [ + 'Data set include downloadable link and sample' => [ + [ + 'downloadable_links' => 'https://magento2.com/download_link', + 'downloadable_samples' => 'https://magento2.com/sample_link' + ], + false + ], + 'Data set with empty' => [ + [ + 'downloadable_links' => '', + 'downloadable_samples' => '' + ], + true + ] + ]; + } + + /** + * Test isRowDownloadableNoValid with dataProvider + * + * @param array $rowData + * @param bool $expected + * @dataProvider isRowDownloadableNoValidDataProvider + */ + public function isRowDownloadableNoValid($rowData, $expected) + { + $this->assertEquals($expected, $this->helper->isRowDownloadableNoValid($rowData)); + } + + /** + * Data Provider to test isRowDownloadableEmptyOptions + * + * @return array + */ + public function isRowDownloadableNoValidDataProvider() + { + return [ + 'Data set include downloadable link and sample' => [ + [ + 'downloadable_links' => 'https://magento2.com/download_link', + 'downloadable_samples' => 'https://magento2.com/sample_link' + ], + true + ], + 'Data set with empty' => [ + [ + 'downloadable_links' => '', + 'downloadable_samples' => '' + ], + false + ] + ]; + } + + /** + * Test fillExistOptions with dataProvider + * + * @param array $base + * @param array $option + * @param array $existingOptions + * @param array $expected + * @dataProvider fillExistOptionsDataProvider + */ + public function testFillExistOptions($base, $option, $existingOptions, $expected) + { + $this->assertEquals($expected, $this->helper->fillExistOptions($base, $option, $existingOptions)); + } + + /** + * Data Provider to test fillExistOptions + * + * @return array + */ + public function fillExistOptionsDataProvider() + { + return [ + 'Data set 1' => [ + [], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ] + ], + 'Data set 2' => [ + [], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [], + [] + ] + ]; + } + + /** + * Test prepareDataForSave with dataProvider + * + * @param array $base + * @param array $replacement + * @param array $expected + * @dataProvider prepareDataForSaveDataProvider + */ + public function testPrepareDataForSave($base, $replacement, $expected) + { + $this->assertEquals($expected, $this->helper->prepareDataForSave($base, $replacement)); + } + + /** + * Data Provider to test prepareDataForSave + * + * @return array + */ + public function prepareDataForSaveDataProvider() + { + return [ + 'Data set 1' => [ + [], + [], + [] + ], + + 'Data set 2' => [ + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ], + [ + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ] + ] + ]; + } + + /** + * Test getTypeByValue with dataProvider + * + * @param string $option + * @param string $expected + * @dataProvider getTypeByValueDataProvider + */ + public function testGetTypeByValue($option, $expected) + { + $this->assertEquals($expected, $this->helper->getTypeByValue($option)); + } + + /** + * Data Provider for getTypeByValue + * + * @return array + */ + public function getTypeByValueDataProvider() + { + return [ + 'Case File Option Value' => [ + 'file1', + 'file' + ], + 'Case url Option Value' => [ + 'https://example.com', + 'url' + ] + ]; + } +} From 32d40dec5cbb5817e6a04daf999a9e024aacd2cf Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Thu, 12 Dec 2019 23:09:32 +0700 Subject: [PATCH 2388/2437] [DownloadableImportExport] Cover Helper Data by Unit Test --- .../Test/Unit/Helper/DataTest.php | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php index 56dc11733cec4..ce42514e52263 100644 --- a/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php @@ -10,6 +10,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\DownloadableImportExport\Helper\Data as HelperData; +use Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable; use PHPUnit\Framework\TestCase; class DataTest extends TestCase @@ -50,15 +51,15 @@ public function isRowDownloadableEmptyOptionsDataProvider() return [ 'Data set include downloadable link and sample' => [ [ - 'downloadable_links' => 'https://magento2.com/download_link', - 'downloadable_samples' => 'https://magento2.com/sample_link' + Downloadable::COL_DOWNLOADABLE_LINKS => 'https://magento2.com/download_link', + Downloadable::COL_DOWNLOADABLE_SAMPLES => 'https://magento2.com/sample_link' ], false ], 'Data set with empty' => [ [ - 'downloadable_links' => '', - 'downloadable_samples' => '' + Downloadable::COL_DOWNLOADABLE_LINKS => '', + Downloadable::COL_DOWNLOADABLE_SAMPLES => '' ], true ] @@ -87,15 +88,15 @@ public function isRowDownloadableNoValidDataProvider() return [ 'Data set include downloadable link and sample' => [ [ - 'downloadable_links' => 'https://magento2.com/download_link', - 'downloadable_samples' => 'https://magento2.com/sample_link' + Downloadable::COL_DOWNLOADABLE_LINKS => 'https://magento2.com/download_link', + Downloadable::COL_DOWNLOADABLE_SAMPLES => 'https://magento2.com/sample_link' ], true ], 'Data set with empty' => [ [ - 'downloadable_links' => '', - 'downloadable_samples' => '' + Downloadable::COL_DOWNLOADABLE_LINKS => '', + Downloadable::COL_DOWNLOADABLE_SAMPLES => '' ], false ] @@ -267,11 +268,11 @@ public function getTypeByValueDataProvider() return [ 'Case File Option Value' => [ 'file1', - 'file' + Downloadable::FILE_OPTION_VALUE ], 'Case url Option Value' => [ 'https://example.com', - 'url' + Downloadable::URL_OPTION_VALUE ] ]; } From b7640b056a7b9baa16984643f13ea0d001fd7b68 Mon Sep 17 00:00:00 2001 From: Dan Wallis <mrdanwallis@gmail.com> Date: Thu, 12 Dec 2019 23:01:33 +0000 Subject: [PATCH 2389/2437] Position arrow by item, not centrally in menu --- .../Magento/backend/web/css/source/_actions.less | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index c86e9cdbf0866..852c6c1f3799e 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -451,11 +451,9 @@ button { border-width: .4rem 0 .4rem .5rem; content: ''; height: 0; - margin-top: -.2rem; - position: absolute; - right: 1rem; - top: 50%; - transition: all .2s linear; + position: relative; + right: 1.2rem; + top: 1.4rem; width: 0; } } From ab6cabd682ae18a48aaf1429232047d90e4e881c Mon Sep 17 00:00:00 2001 From: Lukasz Lewandowski <llewandowski@gpmd.co.uk> Date: Fri, 13 Dec 2019 08:44:14 -0600 Subject: [PATCH 2390/2437] Add tests for cached getVersion and update class description --- .../Magento/Framework/App/ProductMetadata.php | 4 +--- .../App/Test/Unit/ProductMetadataTest.php | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/App/ProductMetadata.php b/lib/internal/Magento/Framework/App/ProductMetadata.php index 8f989351743d2..55e98bb085d41 100644 --- a/lib/internal/Magento/Framework/App/ProductMetadata.php +++ b/lib/internal/Magento/Framework/App/ProductMetadata.php @@ -13,9 +13,7 @@ use Magento\Framework\Composer\ComposerInformation; /** - * Class ProductMetadata - * - * @package Magento\Framework\App + * Magento application product metadata */ class ProductMetadata implements ProductMetadataInterface { diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php index 8e1acc89437e2..bc1abb169b4f8 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Framework\App\Test\Unit; +use Magento\Framework\App\CacheInterface; use Magento\Framework\App\ProductMetadata; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -20,16 +21,27 @@ class ProductMetadataTest extends \PHPUnit\Framework\TestCase */ private $composerInformationMock; + /** + * @var CacheInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $cacheMock; + protected function setUp() { $this->composerInformationMock = $this->getMockBuilder(\Magento\Framework\Composer\ComposerInformation::class) ->disableOriginalConstructor()->getMock(); + $this->cacheMock = $this->getMockBuilder(CacheInterface::class)->getMock(); + $objectManager = new ObjectManager($this); $this->productMetadata = $objectManager->getObject(ProductMetadata::class); $reflectionProperty = new \ReflectionProperty($this->productMetadata, 'composerInformation'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->productMetadata, $this->composerInformationMock); + + $reflectionProperty = new \ReflectionProperty($this->productMetadata, 'cache'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->productMetadata, $this->cacheMock); } /** @@ -40,11 +52,21 @@ protected function setUp() public function testGetVersion($packageList, $expectedVersion) { $this->composerInformationMock->expects($this->any())->method('getSystemPackages')->willReturn($packageList); + $this->cacheMock->expects($this->once())->method('save')->with($expectedVersion); $productVersion = $this->productMetadata->getVersion(); $this->assertNotEmpty($productVersion, 'Empty product version'); $this->assertEquals($expectedVersion, $productVersion); } + public function testGetVersionCached() + { + $expectedVersion = '1.2.3'; + $this->composerInformationMock->expects($this->never())->method('getSystemPackages'); + $this->cacheMock->expects($this->once())->method('load')->willReturn($expectedVersion); + $productVersion = $this->productMetadata->getVersion(); + $this->assertEquals($expectedVersion, $productVersion); + } + /** * @return array */ From 16d0cfc692b497e397df08ebe0ce0487827e46ea Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 13 Dec 2019 17:14:17 +0200 Subject: [PATCH 2391/2437] MC-4242: Newsletter subscriptions per website --- app/code/Magento/Newsletter/Model/Subscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index f33b9929435c3..5c573f47aa0bf 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -628,7 +628,7 @@ public function loadByCustomerId($customerId) $customer = $this->customerRepository->getById($customerId); $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); $this->loadByCustomer((int)$customerId, $websiteId); - if ($customer->getId() && !$this->getCustomerId()) { + if ($this->getId() && $customer->getId() && !$this->getCustomerId()) { $this->setCustomerId($customer->getId()); $this->setSubscriberConfirmCode($this->randomSequence()); $this->save(); From f1f7dadd2100a8d6d676510742a38f3672be6363 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 13 Dec 2019 17:23:40 +0200 Subject: [PATCH 2392/2437] MC-4242: Newsletter subscriptions per website --- .../CustomerGraphQl/Model/Resolver/IsSubscribed.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index 4e49891b5870a..3e69723d4e771 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -7,6 +7,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; @@ -45,10 +46,12 @@ public function resolve( if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } - /** @var Customer $customer */ + /** @var CustomerInterface $customer */ $customer = $value['model']; + $customerId = (int)$customer->getId(); + $websiteId = (int)$customer->getWebsiteId(); + $status = $this->subscriberFactory->create()->loadByCustomer($customerId, $websiteId)->isSubscribed(); - $status = $this->subscriberFactory->create()->loadByCustomerId((int)$customer->getId())->isSubscribed(); return (bool)$status; } } From 0b7c34ef79307da52e088b6d3652663e9cd2c3a1 Mon Sep 17 00:00:00 2001 From: Lukasz Lewandowski <llewandowski@gpmd.co.uk> Date: Fri, 13 Dec 2019 09:26:43 -0600 Subject: [PATCH 2393/2437] Update tests --- .../Framework/App/Test/Unit/ProductMetadataTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php b/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php index bc1abb169b4f8..c504c5f480669 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/ProductMetadataTest.php @@ -34,14 +34,10 @@ protected function setUp() $this->cacheMock = $this->getMockBuilder(CacheInterface::class)->getMock(); $objectManager = new ObjectManager($this); - $this->productMetadata = $objectManager->getObject(ProductMetadata::class); + $this->productMetadata = $objectManager->getObject(ProductMetadata::class, ['cache' => $this->cacheMock]); $reflectionProperty = new \ReflectionProperty($this->productMetadata, 'composerInformation'); $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($this->productMetadata, $this->composerInformationMock); - - $reflectionProperty = new \ReflectionProperty($this->productMetadata, 'cache'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->productMetadata, $this->cacheMock); } /** @@ -63,6 +59,7 @@ public function testGetVersionCached() $expectedVersion = '1.2.3'; $this->composerInformationMock->expects($this->never())->method('getSystemPackages'); $this->cacheMock->expects($this->once())->method('load')->willReturn($expectedVersion); + $this->cacheMock->expects($this->never())->method('save'); $productVersion = $this->productMetadata->getVersion(); $this->assertEquals($expectedVersion, $productVersion); } From f59c1ca61e034a7973e0ac08ab765cbe4060ed84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= <pawel.tylek@creativestyle.pl> Date: Fri, 13 Dec 2019 19:24:10 +0100 Subject: [PATCH 2394/2437] MAGETWO-95866 Add horizontal scroll if elements extend menu's width --- .../Magento_Backend/web/css/source/module/_menu.less | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index d0b17b3439d66..4332ac93ffcd2 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -17,7 +17,7 @@ @menu-logo__padding-bottom: 1.7rem; @menu-logo__outer-size: @menu-logo__padding-top + @menu-logo-img__height + @menu-logo__padding-bottom; -@menu-logo__padding-top: 1.7rem; +@menu-logo__padding-top: 1.7rem; @menu-logo-img__height: 4.1rem; @menu-logo-img__width: 3.5rem; @@ -37,6 +37,7 @@ @submenu__padding-horizontal: 1.5rem; @submenu__padding-vertical: 2rem; @submenu__z-index: @menu__z-index - 2; +@submenu__height: 720px; @submenu-column__width: 23.8rem; @submenu-column__width__l: 19.8rem; @submenu-title__color: @color-white; @@ -252,7 +253,6 @@ background-color: @submenu__background-color; box-shadow: 0 0 3px @color-black; left: 100%; // align all submenus with one Y axis line - min-height: ~'calc(@{menu-logo__outer-size} + 2rem + 100%)'; padding: @submenu__padding-vertical 0 0; position: absolute; top: 0; @@ -266,6 +266,13 @@ .ie11 & { height: 100%; } + + > ul[role="menu"] { + max-width: ~'calc(100vw - @{menu__width})'; + min-height: @submenu__height; + overflow-y: hidden; + overflow-x: auto; + } } &._show { From 7a2bc8fe52e570cfec0201fa36c7118eaf26f6a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= <pawel.tylek@creativestyle.pl> Date: Fri, 13 Dec 2019 20:19:48 +0100 Subject: [PATCH 2395/2437] MAGETWO-95866 Fix tests --- .../Magento_Backend/web/css/source/module/_menu.less | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index 4332ac93ffcd2..c84a54efd5028 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -267,11 +267,11 @@ height: 100%; } - > ul[role="menu"] { - max-width: ~'calc(100vw - @{menu__width})'; - min-height: @submenu__height; - overflow-y: hidden; + > ul[role='menu'] { overflow-x: auto; + overflow-y: hidden; + max-width: ~'calc(100vw - @{menu__width})'; + min-height: @submenu__height; } } From 3c0b60a33e1bda64eb694ac278f11d2f750a4eb0 Mon Sep 17 00:00:00 2001 From: Krzysztof Daniel <krzysztof.daniel@creativestyle.pl> Date: Fri, 13 Dec 2019 23:26:51 +0100 Subject: [PATCH 2396/2437] Fixes phpcs errors and warnings for Magento\Framework\View\Element I was working with those classes and spotted a few warnings and errors that prevent from smooth pull requests. Here are most fixes. --- .../Framework/View/Element/ExceptionHandlerBlockFactory.php | 1 + lib/internal/Magento/Framework/View/Element/FormKey.php | 2 ++ lib/internal/Magento/Framework/View/Element/Js/Components.php | 2 ++ lib/internal/Magento/Framework/View/Element/Js/Cookie.php | 4 ++++ lib/internal/Magento/Framework/View/Element/RendererList.php | 2 ++ lib/internal/Magento/Framework/View/Element/Template.php | 2 ++ .../Magento/Framework/View/Element/Template/File/Resolver.php | 1 + .../Magento/Framework/View/Element/UiComponentInterface.php | 2 ++ 8 files changed, 16 insertions(+) diff --git a/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php b/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php index 83f7dda94328f..b1c74777fb102 100644 --- a/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php +++ b/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php @@ -7,6 +7,7 @@ /** * Class ExceptionHandlerBlockFactory + * * @package Magento\Framework\View\Element */ class ExceptionHandlerBlockFactory diff --git a/lib/internal/Magento/Framework/View/Element/FormKey.php b/lib/internal/Magento/Framework/View/Element/FormKey.php index 072ba42b34f1d..e6ecd20435ce8 100644 --- a/lib/internal/Magento/Framework/View/Element/FormKey.php +++ b/lib/internal/Magento/Framework/View/Element/FormKey.php @@ -10,6 +10,8 @@ namespace Magento\Framework\View\Element; /** + * Element with FormKey + * * @api */ class FormKey extends \Magento\Framework\View\Element\AbstractBlock diff --git a/lib/internal/Magento/Framework/View/Element/Js/Components.php b/lib/internal/Magento/Framework/View/Element/Js/Components.php index 3f9be417d62be..8e33ca5581960 100644 --- a/lib/internal/Magento/Framework/View/Element/Js/Components.php +++ b/lib/internal/Magento/Framework/View/Element/Js/Components.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Element\Template; /** + * Block for Components + * * @api */ class Components extends Template diff --git a/lib/internal/Magento/Framework/View/Element/Js/Cookie.php b/lib/internal/Magento/Framework/View/Element/Js/Cookie.php index c44bad026ecac..b37a9387c07f6 100644 --- a/lib/internal/Magento/Framework/View/Element/Js/Cookie.php +++ b/lib/internal/Magento/Framework/View/Element/Js/Cookie.php @@ -10,6 +10,8 @@ use Magento\Framework\View\Element\Template\Context; /** + * Block passes configuration for cookies set by JS + * * @api */ class Cookie extends Template @@ -75,6 +77,8 @@ public function getPath() } /** + * Get configured cookie lifetime + * * @return int */ public function getLifetime() diff --git a/lib/internal/Magento/Framework/View/Element/RendererList.php b/lib/internal/Magento/Framework/View/Element/RendererList.php index 30bc38aed28f3..96707c74dd253 100644 --- a/lib/internal/Magento/Framework/View/Element/RendererList.php +++ b/lib/internal/Magento/Framework/View/Element/RendererList.php @@ -6,6 +6,8 @@ namespace Magento\Framework\View\Element; /** + * Renderer List + * * @api */ class RendererList extends AbstractBlock diff --git a/lib/internal/Magento/Framework/View/Element/Template.php b/lib/internal/Magento/Framework/View/Element/Template.php index a1aa599156d2a..99c876b440665 100644 --- a/lib/internal/Magento/Framework/View/Element/Template.php +++ b/lib/internal/Magento/Framework/View/Element/Template.php @@ -10,6 +10,7 @@ /** * Standard Magento block. + * * Should be used when you declare a block in frontend area layout handle. * * Avoid extending this class. @@ -166,6 +167,7 @@ public function setTemplateContext($templateContext) /** * Internal constructor, that is called from real constructor + * * @return void */ protected function _construct() diff --git a/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php b/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php index abad0eee856c3..b13caa55d3174 100644 --- a/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php +++ b/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php @@ -10,6 +10,7 @@ /** * Class Resolver + * * @package Magento\Framework\View\Element\Template\File */ class Resolver diff --git a/lib/internal/Magento/Framework/View/Element/UiComponentInterface.php b/lib/internal/Magento/Framework/View/Element/UiComponentInterface.php index b5c3546084fcb..3ae2627881101 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponentInterface.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponentInterface.php @@ -52,6 +52,8 @@ public function render(); public function addComponent($name, UiComponentInterface $component); /** + * Get component + * * @param string $name * @return UiComponentInterface */ From 75532257cf22ede587602be75e70f5c0c8a9d490 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 14 Dec 2019 19:05:16 +0700 Subject: [PATCH 2397/2437] [Catalog] Cover Component/FilterFactory by Unit Test --- .../Unit/Ui/Component/FilterFactoryTest.php | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php new file mode 100644 index 0000000000000..545b9e7326ff1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Ui\Component; + +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Ui\Component\FilterFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Eav\Model\Entity\Attribute\Source\SourceInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; + +class FilterFactoryTest extends TestCase +{ + /** + * @var FilterFactory + */ + private $filterFactory; + + /** + * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $componentFactoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + + $this->componentFactoryMock = $this->createMock(UiComponentFactory::class); + + $this->filterFactory = $objectManager->getObject( + FilterFactory::class, + [ + 'componentFactory' => $this->componentFactoryMock + ] + ); + } + + /** + * Test create() with use source attribute + */ + public function testCreateWithUseSourceAttribute() + { + $contextMock = $this->createMock(ContextInterface::class); + $attributeMock = $this->getMockBuilder(ProductAttributeInterface::class) + ->setMethods(['usesSource', 'getSource']) + ->getMockForAbstractClass(); + $attributeMock->method('getAttributeCode')->willReturn('color'); + $attributeMock->method('getDefaultFrontendLabel')->willReturn('Color'); + $attributeMock->method('usesSource')->willReturn(true); + $attributeMock->method('getSourceModel')->willReturn('getSourceModel value'); + $attributeMock->method('getFrontendInput')->willReturn('select'); + $sourceMock = $this->createMock(SourceInterface::class); + $attributeMock->method('getSource')->willReturn($sourceMock); + $sourceMock->method('getAllOptions')->willReturn( + [ + [ + 'value' => 1, + 'label' => 'Black', + ], + [ + 'value' => 2, + 'label' => 'White', + ] + ] + ); + $this->componentFactoryMock->expects($this->once()) + ->method('create') + ->with('color', 'filterSelect', [ + 'data' => [ + 'config' => [ + 'options' => [ + [ + 'value' => 1, + 'label' => 'Black', + ], + [ + 'value' => 2, + 'label' => 'White', + ] + ], + 'caption' => (string)__('Select...'), + 'dataScope' => 'color', + 'label' => (string)__('Color'), + ] + ], + 'context' => $contextMock + ]); + + $this->filterFactory->create($attributeMock, $contextMock); + } +} From ad48079535d018ddab62d7f1313d93a4268f859f Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 14 Dec 2019 20:11:23 +0700 Subject: [PATCH 2398/2437] [Persistent] Cover CustomerData by Unit Test --- .../Test/Unit/CustomerData/PersistentTest.php | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php new file mode 100644 index 0000000000000..381555ed0fe79 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Persistent\Test\Unit\CustomerData; + +use PHPUnit\Framework\TestCase; +use Magento\Persistent\CustomerData\Persistent; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Helper\View; +use Magento\Persistent\Helper\Session; +use Magento\Persistent\Model\Session as PersistentSession; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class PersistentTest extends TestCase +{ + /** + * @var Persistent + */ + private $customerData; + + /** + * @var Session + */ + private $persistentSessionHelperMock; + + /** + * @var View + */ + private $customerViewHelperMock; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepositoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->persistentSessionHelperMock = $this->createMock(Session::class); + $this->customerViewHelperMock = $this->createMock(View::class); + $this->customerRepositoryMock = $this->createMock(CustomerRepositoryInterface::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->customerData = $objectManager->getObject( + Persistent::class, + [ + 'persistentSession' => $this->persistentSessionHelperMock, + 'customerViewHelper' => $this->customerViewHelperMock, + 'customerRepository' => $this->customerRepositoryMock + ] + ); + } + + /** + * Test getSectionData() when disable persistent + */ + public function testGetSectionDataWhenDisablePersistent() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(false); + + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() when customer doesn't login + */ + public function testGetSectionDataWithNotLogin() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); + + $persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['getCustomerId']); + $persistentSessionMock->method('getCustomerId')->willReturn(null); + $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); + + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() when customer login and enable persistent + */ + public function testGetSectionDataCustomerLoginAndEnablePersistent() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); + + $persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['getCustomerId']); + $persistentSessionMock->method('getCustomerId')->willReturn(1); + $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); + + $customerMock = $this->createMock(CustomerInterface::class); + $this->customerRepositoryMock->method('getById')->with(1)->willReturn($customerMock); + $this->customerViewHelperMock->method('getCustomerName')->with($customerMock)->willReturn('Adam John'); + + $this->assertEquals( + [ + 'fullname' => 'Adam John' + ], + $this->customerData->getSectionData() + ); + } +} From 1c699b25db84b0690888ee85f145bd336607d672 Mon Sep 17 00:00:00 2001 From: Eden Duong <quocviet312@gmail.com> Date: Sat, 14 Dec 2019 20:25:24 +0700 Subject: [PATCH 2399/2437] Update phpdocs --- .../Persistent/Test/Unit/CustomerData/PersistentTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php index 381555ed0fe79..16df8181a0e61 100644 --- a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -25,17 +25,17 @@ class PersistentTest extends TestCase private $customerData; /** - * @var Session + * @var Session|\PHPUnit_Framework_MockObject_MockObject */ private $persistentSessionHelperMock; /** - * @var View + * @var View|\PHPUnit_Framework_MockObject_MockObject */ private $customerViewHelperMock; /** - * @var CustomerRepositoryInterface + * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ private $customerRepositoryMock; From fc04d190a098796680070a338d2b8124e117feb9 Mon Sep 17 00:00:00 2001 From: Giancarlo Peris <gperis93@gmail.com> Date: Sat, 14 Dec 2019 13:53:57 +0000 Subject: [PATCH 2400/2437] Initialise value as empty when color picker input is reset so the preview gets updated --- .../view/base/web/js/lib/knockout/bindings/color-picker.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js index c678b85276093..1e8e89894d22f 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js @@ -71,6 +71,11 @@ define([ update: function (element, valueAccessor, allBindings, viewModel) { var config = valueAccessor(); + /** Initialise value as empty if it is undefined when color picker input is reset **/ + if (config.value() === undefined) { + config.value(''); + } + if (tinycolor(config.value()).isValid() || config.value() === '') { $(element).spectrum('set', config.value()); From e704d62ef2af5b2bcbb31574c9b8bee8874c90e1 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sat, 14 Dec 2019 21:06:18 +0700 Subject: [PATCH 2401/2437] [Downloadable] Cover Helper Data by Unit Test --- .../Test/Unit/Helper/DataTest.php | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php b/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..7c59ef7d7ec64 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Helper; + +use Magento\Downloadable\Model\Link; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use Magento\Downloadable\Helper\Data; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class DataTest extends TestCase +{ + /** + * @var Data + */ + private $helper; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->contextMock = $this->createMock(Context::class); + $this->contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $objectManager = new ObjectManagerHelper($this); + $this->helper = $objectManager->getObject( + Data::class, + ['context' => $this->contextMock] + ); + } + + /** + * Test getIsShareable() with data provider + * + * @param int $linkShareable + * @param bool $config + * @param bool $expectedResult + * @dataProvider getIsShareableDataProvider + */ + public function testGetIsShareable($linkShareable, $config, $expectedResult) + { + $this->scopeConfigMock->method('isSetFlag') + ->with(Link::XML_PATH_CONFIG_IS_SHAREABLE, ScopeInterface::SCOPE_STORE) + ->willReturn($config); + + $linkMock = $this->createMock(Link::class); + $linkMock->method('getIsShareable')->willReturn($linkShareable); + + $this->assertEquals($expectedResult, $this->helper->getIsShareable($linkMock)); + } + + /** + * Data provider for getIsShareable() + * + * @return array + */ + public function getIsShareableDataProvider() + { + return [ + 'link shareable yes' => [Link::LINK_SHAREABLE_YES, true, true], + 'link shareable no' => [Link::LINK_SHAREABLE_NO, true, false], + 'link shareable config true' => [Link::LINK_SHAREABLE_CONFIG, true, true], + 'link shareable config false' => [Link::LINK_SHAREABLE_CONFIG, false, false], + ]; + } +} From f075d4e4120dc833867a3ddc047804b8e1ea9070 Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Sun, 15 Dec 2019 01:23:06 +0530 Subject: [PATCH 2402/2437] [Catalog] cover ViewModel AddToCompareAvailability by Unit Test --- .../Checker/AddToCompareAvailabilityTest.php | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php new file mode 100644 index 0000000000000..4295b11a58077 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\ViewModel\Product\Checker; + +use Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Unit test for Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability. + */ +class AddToCompareAvailabilityTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var AddToCompareAvailability + */ + private $viewModel; + + /** + * @var StockConfigurationInterface|MockObject + */ + protected $stockConfiguration; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + + $this->stockConfiguration = + $this->getMockBuilder(StockConfigurationInterface::class) + ->getMock(); + + $this->viewModel = $this->getObjectManager()->getObject( + AddToCompareAvailability::class, + [ + 'stockConfiguration' => $this->stockConfiguration + ] + ); + } + + /** + * Test IsAvailableForCompare() with data provider + * + * @param bool $status + * @param bool $isSalable + * @param array $isInStock + * @param bool $isShowOutOfStock + * @return boolean + * @dataProvider isAvailableForCompareDataProvider + */ + public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isShowOutOfStock): bool + { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $productMock->expects($this->once()) + ->method('getStatus') + ->willReturn($status); + + $productMock->expects($this->any()) + ->method('isSalable') + ->willReturn($isSalable); + + $productMock->expects($this->any()) + ->method('getQuantityAndStockStatus') + ->willReturn($isInStock); + + $this->stockConfiguration->expects($this->any()) + ->method('isShowOutOfStock') + ->willReturn($isShowOutOfStock); + + return $this->viewModel->isAvailableForCompare($productMock); + } + + /** + * Data provider for isAvailableForCompare() + * + * @return array + */ + public function isAvailableForCompareDataProvider(): array + { + return [ + [1, true, ['is_in_stock' => true], false], + [1, true, ['is_in_stock' => false], true], + [1, true, [], false], + [1, false, [], false], + [2, true, ['is_in_stock' => true], false] + ]; + } + + /** + * @return ObjectManager + */ + private function getObjectManager(): ObjectManager + { + if (null === $this->objectManager) { + $this->objectManager = new ObjectManager($this); + } + + return $this->objectManager; + } +} From 0b806f9cf6d4f1cf89a294d18c552cc3eaa9c2ad Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Sun, 15 Dec 2019 02:19:06 +0530 Subject: [PATCH 2403/2437] Fixed the static tests & feedbacks --- .../Checker/AddToCompareAvailabilityTest.php | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php index 4295b11a58077..88dba7ebee1c6 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -26,14 +26,9 @@ class AddToCompareAvailabilityTest extends \PHPUnit\Framework\TestCase private $viewModel; /** - * @var StockConfigurationInterface|MockObject + * @var StockConfigurationInterface|MockObject */ - protected $stockConfiguration; - - /** - * @var ObjectManager - */ - private $objectManager; + protected $stockConfigurationMock; /** * @inheritdoc @@ -41,14 +36,16 @@ class AddToCompareAvailabilityTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $this->stockConfiguration = + $objectManager = new ObjectManager($this); + + $this->stockConfigurationMock = $this->getMockBuilder(StockConfigurationInterface::class) ->getMock(); - $this->viewModel = $this->getObjectManager()->getObject( + $this->viewModel = $objectManager->getObject( AddToCompareAvailability::class, [ - 'stockConfiguration' => $this->stockConfiguration + 'stockConfiguration' => $this->stockConfigurationMock ] ); } @@ -81,7 +78,7 @@ public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isSh ->method('getQuantityAndStockStatus') ->willReturn($isInStock); - $this->stockConfiguration->expects($this->any()) + $this->stockConfigurationMock->expects($this->any()) ->method('isShowOutOfStock') ->willReturn($isShowOutOfStock); @@ -90,7 +87,7 @@ public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isSh /** * Data provider for isAvailableForCompare() - * + * * @return array */ public function isAvailableForCompareDataProvider(): array @@ -103,16 +100,4 @@ public function isAvailableForCompareDataProvider(): array [2, true, ['is_in_stock' => true], false] ]; } - - /** - * @return ObjectManager - */ - private function getObjectManager(): ObjectManager - { - if (null === $this->objectManager) { - $this->objectManager = new ObjectManager($this); - } - - return $this->objectManager; - } } From 97fe3ffe0b0e891284301f7ef749ee96391ea013 Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Sun, 15 Dec 2019 03:08:39 +0530 Subject: [PATCH 2404/2437] Assert added with expected results --- .../Checker/AddToCompareAvailabilityTest.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php index 88dba7ebee1c6..1b743d26a4297 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -57,10 +57,11 @@ protected function setUp(): void * @param bool $isSalable * @param array $isInStock * @param bool $isShowOutOfStock - * @return boolean + * @param bool $expectedBool + * @return void * @dataProvider isAvailableForCompareDataProvider */ - public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isShowOutOfStock): bool + public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isShowOutOfStock, $expectedBool): void { $productMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() @@ -82,7 +83,7 @@ public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isSh ->method('isShowOutOfStock') ->willReturn($isShowOutOfStock); - return $this->viewModel->isAvailableForCompare($productMock); + $this->assertEquals($expectedBool, $this->viewModel->isAvailableForCompare($productMock)); } /** @@ -93,11 +94,11 @@ public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isSh public function isAvailableForCompareDataProvider(): array { return [ - [1, true, ['is_in_stock' => true], false], - [1, true, ['is_in_stock' => false], true], - [1, true, [], false], - [1, false, [], false], - [2, true, ['is_in_stock' => true], false] + [1, true, ['is_in_stock' => true], false, true], + [1, true, ['is_in_stock' => false], true, true], + [1, true, [], false, true], + [1, false, [], false, false], + [2, true, ['is_in_stock' => true], false, false] ]; } } From 5cedaf8e26fe72b7335058be87ab2b1a39977382 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 05:44:47 +0700 Subject: [PATCH 2405/2437] Refactor code --- .../Test/Unit/CustomerData/PersistentTest.php | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php index 381555ed0fe79..15889a74439d7 100644 --- a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -16,26 +16,37 @@ use Magento\Persistent\Model\Session as PersistentSession; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; class PersistentTest extends TestCase { + /** + * Stub customer id + */ + private const STUB_CUSTOMER_ID = 1; + + /** + * Stub customer name + */ + private const STUB_CUSTOMER_NAME = 'Adam John'; + /** * @var Persistent */ private $customerData; /** - * @var Session + * @var Session|MockObject */ private $persistentSessionHelperMock; /** - * @var View + * @var View|MockObject */ private $customerViewHelperMock; /** - * @var CustomerRepositoryInterface + * @var CustomerRepositoryInterface|MockObject */ private $customerRepositoryMock; @@ -73,7 +84,7 @@ public function testGetSectionDataWhenDisablePersistent() /** * Test getSectionData() when customer doesn't login */ - public function testGetSectionDataWithNotLogin() + public function testGetSectionDataWhenCustomerNotLoggedInReturnsEmptyArray() { $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); @@ -92,16 +103,17 @@ public function testGetSectionDataCustomerLoginAndEnablePersistent() $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); $persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['getCustomerId']); - $persistentSessionMock->method('getCustomerId')->willReturn(1); + $persistentSessionMock->method('getCustomerId')->willReturn(self::STUB_CUSTOMER_ID); $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); $customerMock = $this->createMock(CustomerInterface::class); $this->customerRepositoryMock->method('getById')->with(1)->willReturn($customerMock); - $this->customerViewHelperMock->method('getCustomerName')->with($customerMock)->willReturn('Adam John'); + $this->customerViewHelperMock->method('getCustomerName')->with($customerMock) + ->willReturn(self::STUB_CUSTOMER_NAME); $this->assertEquals( [ - 'fullname' => 'Adam John' + 'fullname' => self::STUB_CUSTOMER_NAME ], $this->customerData->getSectionData() ); From bbfdb089bb5cbd7b61882896984ad87bea62deee Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 06:02:50 +0700 Subject: [PATCH 2406/2437] Refactor code --- .../Unit/Ui/Component/FilterFactoryTest.php | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php index 545b9e7326ff1..4b43a41c42fad 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php @@ -7,23 +7,45 @@ namespace Magento\Catalog\Test\Unit\Ui\Component; -use PHPUnit\Framework\TestCase; -use Magento\Catalog\Ui\Component\FilterFactory; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; -use Magento\Framework\View\Element\UiComponentFactory; use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Ui\Component\FilterFactory; use Magento\Eav\Model\Entity\Attribute\Source\SourceInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; class FilterFactoryTest extends TestCase { + /** + * Stub attribute + */ + private const STUB_ATTRIBUTE = [ + 'attribute_code' => 'color', + 'default_frontend_label' => 'Color', + 'uses_source' => 'Color', + 'source_model' => 'getSourceModel value', + 'frontend_input' => 'select', + 'all_options' => [ + [ + 'value' => 1, + 'label' => 'Black', + ], + [ + 'value' => 2, + 'label' => 'White', + ] + ] + ]; + /** * @var FilterFactory */ private $filterFactory; /** - * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UiComponentFactory|MockObject */ private $componentFactoryMock; @@ -32,7 +54,7 @@ class FilterFactoryTest extends TestCase */ protected function setUp() { - $objectManager = new ObjectManagerHelper($this); + $objectManager = new ObjectManager($this); $this->componentFactoryMock = $this->createMock(UiComponentFactory::class); @@ -53,43 +75,24 @@ public function testCreateWithUseSourceAttribute() $attributeMock = $this->getMockBuilder(ProductAttributeInterface::class) ->setMethods(['usesSource', 'getSource']) ->getMockForAbstractClass(); - $attributeMock->method('getAttributeCode')->willReturn('color'); - $attributeMock->method('getDefaultFrontendLabel')->willReturn('Color'); - $attributeMock->method('usesSource')->willReturn(true); - $attributeMock->method('getSourceModel')->willReturn('getSourceModel value'); - $attributeMock->method('getFrontendInput')->willReturn('select'); + $attributeMock->method('getAttributeCode')->willReturn(self::STUB_ATTRIBUTE['attribute_code']); + $attributeMock->method('getDefaultFrontendLabel') + ->willReturn(self::STUB_ATTRIBUTE['default_frontend_label']); + $attributeMock->method('usesSource')->willReturn(self::STUB_ATTRIBUTE['uses_source']); + $attributeMock->method('getSourceModel')->willReturn(self::STUB_ATTRIBUTE['source_model']); + $attributeMock->method('getFrontendInput')->willReturn(self::STUB_ATTRIBUTE['frontend_input']); $sourceMock = $this->createMock(SourceInterface::class); $attributeMock->method('getSource')->willReturn($sourceMock); - $sourceMock->method('getAllOptions')->willReturn( - [ - [ - 'value' => 1, - 'label' => 'Black', - ], - [ - 'value' => 2, - 'label' => 'White', - ] - ] - ); + $sourceMock->method('getAllOptions')->willReturn(self::STUB_ATTRIBUTE['all_options']); $this->componentFactoryMock->expects($this->once()) ->method('create') ->with('color', 'filterSelect', [ 'data' => [ 'config' => [ - 'options' => [ - [ - 'value' => 1, - 'label' => 'Black', - ], - [ - 'value' => 2, - 'label' => 'White', - ] - ], + 'options' => self::STUB_ATTRIBUTE['all_options'], 'caption' => (string)__('Select...'), - 'dataScope' => 'color', - 'label' => (string)__('Color'), + 'dataScope' => self::STUB_ATTRIBUTE['attribute_code'], + 'label' => (string)__(self::STUB_ATTRIBUTE['default_frontend_label']), ] ], 'context' => $contextMock From 78ba90500058250770676f3c60c826a23f1de366 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 06:03:36 +0700 Subject: [PATCH 2407/2437] optimize import --- .../Persistent/Test/Unit/CustomerData/PersistentTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php index 15889a74439d7..7120474ba0eee 100644 --- a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -8,15 +8,15 @@ namespace Magento\Persistent\Test\Unit\CustomerData; -use PHPUnit\Framework\TestCase; -use Magento\Persistent\CustomerData\Persistent; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Helper\View; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Persistent\CustomerData\Persistent; use Magento\Persistent\Helper\Session; use Magento\Persistent\Model\Session as PersistentSession; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; class PersistentTest extends TestCase { From 50b006e1cfbb59d84914e8b7dd9d81afaf22cf01 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 06:35:16 +0700 Subject: [PATCH 2408/2437] refactor code --- .../Persistent/Test/Unit/CustomerData/PersistentTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php index 7120474ba0eee..a95f5530bb800 100644 --- a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -107,7 +107,7 @@ public function testGetSectionDataCustomerLoginAndEnablePersistent() $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); $customerMock = $this->createMock(CustomerInterface::class); - $this->customerRepositoryMock->method('getById')->with(1)->willReturn($customerMock); + $this->customerRepositoryMock->method('getById')->with(self::STUB_CUSTOMER_ID)->willReturn($customerMock); $this->customerViewHelperMock->method('getCustomerName')->with($customerMock) ->willReturn(self::STUB_CUSTOMER_NAME); From 888ad592a5995e189783a20bf855d8b979338324 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 07:12:55 +0700 Subject: [PATCH 2409/2437] refactor code --- .../Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php index 4b43a41c42fad..e19284447c07d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php @@ -92,7 +92,7 @@ public function testCreateWithUseSourceAttribute() 'options' => self::STUB_ATTRIBUTE['all_options'], 'caption' => (string)__('Select...'), 'dataScope' => self::STUB_ATTRIBUTE['attribute_code'], - 'label' => (string)__(self::STUB_ATTRIBUTE['default_frontend_label']), + 'label' => self::STUB_ATTRIBUTE['default_frontend_label'], ] ], 'context' => $contextMock From 9e042b677e1d6e7af4efa2958697a4088b63400e Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Sun, 15 Dec 2019 06:37:41 +0530 Subject: [PATCH 2410/2437] Magic numbers replaced with class --- .../Product/Checker/AddToCompareAvailabilityTest.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php index 1b743d26a4297..682f652339820 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -11,6 +11,7 @@ use Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability; use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Catalog\Model\Product; +use \Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\MockObject\MockObject; @@ -94,11 +95,11 @@ public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isSh public function isAvailableForCompareDataProvider(): array { return [ - [1, true, ['is_in_stock' => true], false, true], - [1, true, ['is_in_stock' => false], true, true], - [1, true, [], false, true], - [1, false, [], false, false], - [2, true, ['is_in_stock' => true], false, false] + [Status::STATUS_ENABLED, true, ['is_in_stock' => true], false, true], + [Status::STATUS_ENABLED, true, ['is_in_stock' => false], true, true], + [Status::STATUS_ENABLED, true, [], false, true], + [Status::STATUS_ENABLED, false, [], false, false], + [Status::STATUS_DISABLED, true, ['is_in_stock' => true], false, false] ]; } } From a308d4add176b1250d19f8dec6abad9b655333e1 Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Sun, 15 Dec 2019 06:40:07 +0530 Subject: [PATCH 2411/2437] Magic numbers replaced with class --- .../ViewModel/Product/Checker/AddToCompareAvailabilityTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php index 682f652339820..55bfbe8b4ec71 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -11,7 +11,7 @@ use Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability; use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\Catalog\Model\Product; -use \Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\MockObject\MockObject; From 0a1ae3b37f3c1b937a2000f6f7f45205a3d32145 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 15 Dec 2019 11:30:51 +0700 Subject: [PATCH 2412/2437] Refactor filter factory test --- .../Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php index e19284447c07d..1e72b7ba35864 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php @@ -86,7 +86,7 @@ public function testCreateWithUseSourceAttribute() $sourceMock->method('getAllOptions')->willReturn(self::STUB_ATTRIBUTE['all_options']); $this->componentFactoryMock->expects($this->once()) ->method('create') - ->with('color', 'filterSelect', [ + ->with(self::STUB_ATTRIBUTE['attribute_code'], 'filterSelect', [ 'data' => [ 'config' => [ 'options' => self::STUB_ATTRIBUTE['all_options'], From 56bf55ed2975af90f2daf646eee540f043831a99 Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Mon, 16 Dec 2019 01:16:57 +0530 Subject: [PATCH 2413/2437] [Contact] covered Model Config by Unit Test --- .../Contact/Test/Unit/Model/ConfigTest.php | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php diff --git a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php new file mode 100644 index 0000000000000..56351bae87698 --- /dev/null +++ b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php @@ -0,0 +1,127 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Contact\Test\Unit\Model; + +use Magento\Contact\Model\Config; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Contact\Model\Config + */ +class ConfigTest extends TestCase +{ + /** + * @var Config + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['getValue', 'isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $objectManager = new ObjectManagerHelper($this); + $this->model = $objectManager->getObject( + Config::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Test isEnabled() + * + * @return void + */ + public function testIsEnabled(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) + ->willReturn(true); + + $this->assertTrue(true, $this->model->isEnabled()); + } + + /** + * Test isNotEnabled() + * + * @return void + */ + public function testIsNotEnabled(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) + ->willReturn(false); + + $this->assertFalse(false, $this->model->isEnabled()); + } + + /** + * Test emailTemplate() + * + * @return void + */ + public function testEmailTemplate(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE) + ->willReturn('contact_email_email_template'); + + $this->assertEquals('contact_email_email_template', $this->model->emailTemplate()); + } + + /** + * Test emailSender() + * + * @return void + */ + public function testEmailSender(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_SENDER, ScopeInterface::SCOPE_STORE) + ->willReturn('custom2'); + + $this->assertEquals('custom2', $this->model->emailSender()); + } + + /** + * Test emailRecipient() + * + * @return void + */ + public function testEmailRecipient(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_RECIPIENT, ScopeInterface::SCOPE_STORE) + ->willReturn('hello@example.com'); + + $this->assertEquals('hello@example.com', $this->model->emailRecipient()); + } +} From 9b1883458be9e7c2481df3b08ca1865798921b1d Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Mon, 16 Dec 2019 02:18:28 +0530 Subject: [PATCH 2414/2437] Assert updated --- app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php index 56351bae87698..a665b6b71c8c9 100644 --- a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php @@ -62,7 +62,7 @@ public function testIsEnabled(): void ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) ->willReturn(true); - $this->assertTrue(true, $this->model->isEnabled()); + $this->assertTrue($this->model->isEnabled()); } /** @@ -77,7 +77,7 @@ public function testIsNotEnabled(): void ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) ->willReturn(false); - $this->assertFalse(false, $this->model->isEnabled()); + $this->assertFalse($this->model->isEnabled()); } /** From ccc399a6920c4f1c9def5536f70094a87bdf1577 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 16 Dec 2019 10:47:25 +0200 Subject: [PATCH 2415/2437] MC-4242: Newsletter subscriptions per website --- ...esAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 52dce4d67f698..716bb60ce68bd 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -29,6 +29,10 @@ <requiredEntity createDataKey="createSecondCategory"/> </createData> + <!-- Reindex and flush the cache --> + <magentoCLI command="indexer:reindex" stepKey="runReindex"/> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + <!-- Log in to backend --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From 6bf903f0521c7ef1f2561af6db71e391ecdc39ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Tylek?= <pawel.tylek@creativestyle.pl> Date: Mon, 16 Dec 2019 10:24:30 +0100 Subject: [PATCH 2416/2437] MAGETWO-95866 Fix tests --- .../backend/Magento_Backend/web/css/source/module/_menu.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index c84a54efd5028..29a7499ec72f4 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -268,10 +268,10 @@ } > ul[role='menu'] { - overflow-x: auto; - overflow-y: hidden; max-width: ~'calc(100vw - @{menu__width})'; min-height: @submenu__height; + overflow-x: auto; + overflow-y: hidden; } } From b394255d6d5c1df661c2c6cd5f6507f603b2d1f9 Mon Sep 17 00:00:00 2001 From: Sathish <srsathish92@gmail.com> Date: Mon, 16 Dec 2019 15:40:13 +0530 Subject: [PATCH 2417/2437] Removed duplicated logic with data provider --- .../Contact/Test/Unit/Model/ConfigTest.php | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php index a665b6b71c8c9..9456ea4b48105 100644 --- a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php +++ b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php @@ -54,30 +54,29 @@ protected function setUp(): void * Test isEnabled() * * @return void + * @dataProvider isEnabledDataProvider */ - public function testIsEnabled(): void + public function testIsEnabled($isSetFlag, $result): void { $this->scopeConfigMock->expects($this->once()) ->method('isSetFlag') ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) - ->willReturn(true); + ->willReturn($isSetFlag); - $this->assertTrue($this->model->isEnabled()); + $this->assertEquals($result, $this->model->isEnabled()); } /** - * Test isNotEnabled() + * Data provider for isEnabled() * - * @return void + * @return array */ - public function testIsNotEnabled(): void + public function isEnabledDataProvider(): array { - $this->scopeConfigMock->expects($this->once()) - ->method('isSetFlag') - ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) - ->willReturn(false); - - $this->assertFalse($this->model->isEnabled()); + return [ + [true, true], + [false, false] + ]; } /** From b8d5c105115e4ed143fe29d99f5f5e0e89ef8521 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 16 Dec 2019 12:21:37 +0200 Subject: [PATCH 2418/2437] MC-4242: Newsletter subscriptions per website --- ...terChangingUrlKeyForStoreViewAndMovingCategoryTest.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 716bb60ce68bd..0986443fd5893 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -29,15 +29,15 @@ <requiredEntity createDataKey="createSecondCategory"/> </createData> - <!-- Reindex and flush the cache --> - <magentoCLI command="indexer:reindex" stepKey="runReindex"/> - <magentoCLI command="cache:flush" stepKey="cleanCache"/> - <!-- Log in to backend --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create additional Store View in Main Website Store --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + + <!-- Reindex and flush the cache --> + <magentoCLI command="indexer:reindex" stepKey="runReindex"/> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> </before> <after> <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> From 18500e1410a96b06a7e59ac19733e84784b14cfc Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 16 Dec 2019 14:41:16 +0200 Subject: [PATCH 2419/2437] MC-4242: Newsletter subscriptions per website --- ...iesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 0986443fd5893..7b901658faa4a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -80,7 +80,6 @@ <!-- Assert category url with custom store view --> <amOnPage url="{{StorefrontHomePage.url}}$$createSecondCategory.name$$/{{SimpleRootSubCategory.url_key}}.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <dontSee userInput="$$createSecondSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="dontSeeProductInCategory"/> - <see selector="{{StorefrontCategoryMainSection.emptyProductMessage}}" userInput="We can't find products matching the selection." stepKey="seeEmptyProductMessage"/> + <see userInput="$$createFirstSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeProductInCategory"/> </test> </tests> From 218c3ff32418c9dacb4da0ed812b226f81e8400a Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 16 Dec 2019 16:15:39 +0200 Subject: [PATCH 2420/2437] Reverting container class --- .../Theme/view/adminhtml/page_layout/admin-2columns-left.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Theme/view/adminhtml/page_layout/admin-2columns-left.xml b/app/code/Magento/Theme/view/adminhtml/page_layout/admin-2columns-left.xml index cee241233a199..9cb89746ad85d 100644 --- a/app/code/Magento/Theme/view/adminhtml/page_layout/admin-2columns-left.xml +++ b/app/code/Magento/Theme/view/adminhtml/page_layout/admin-2columns-left.xml @@ -31,7 +31,7 @@ </container> <container name="page.main.container" as="page_main_container" htmlId="page:main-container" htmlTag="div" htmlClass="page-columns"> <container name="main.col" as="main-col" htmlId="container" htmlTag="div" htmlClass="main-col"> - <container name="admin.scope.col.wrap" as="admin-scope-col-wrap" htmlTag="div" htmlClass="form-inline"> <!-- ToDo UI: remove this wrapper remove with old styles removal --> + <container name="admin.scope.col.wrap" as="admin-scope-col-wrap" htmlTag="div" htmlClass="admin__scope-old"> <!-- ToDo UI: remove this wrapper remove with old styles removal --> <container name="content" as="content"/> </container> </container> From 6ac10980c6322e949b13af43dfea320113586c2f Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Mon, 16 Dec 2019 23:13:53 +0700 Subject: [PATCH 2421/2437] [Msrp] Cover MsrpPriceCalculator by Unit Test --- .../Unit/Pricing/MsrpPriceCalculatorTest.php | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php diff --git a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php new file mode 100644 index 0000000000000..613547943c9a4 --- /dev/null +++ b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Msrp\Test\Unit\Pricing; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type as Type; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedType; +use Magento\Msrp\Pricing\MsrpPriceCalculator; +use Magento\MsrpGroupedProduct\Pricing\MsrpPriceCalculator as MsrpGroupedCalculator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class MsrpPriceCalculatorTest extends TestCase +{ + /** + * Test getMrspPriceValue() with the data provider below + * + * @param array $msrpPriceCalculators + * @param Product $productMock + * @param float $expected + * @dataProvider getMsrpPriceValueDataProvider + */ + public function testGetMsrpPriceValue($msrpPriceCalculators, $productMock, $expected) + { + $objectManager = new ObjectManager($this); + $pricing = $objectManager->getObject(MsrpPriceCalculator::class, + [ + 'msrpPriceCalculators' => $msrpPriceCalculators + ] + ); + + $this->assertEquals($expected, $pricing->getMsrpPriceValue($productMock)); + } + + /** + * Data Provider for test getMrspPriceValue() + * + * @return array + */ + public function getMsrpPriceValueDataProvider() + { + return [ + 'Get Mrsp Price with grouped product and price calculator is also grouped product type' => [ + [ + [ + 'productType' => GroupedType::TYPE_CODE, + 'priceCalculator' => $this->createPriceCalculatorMock( + MsrpGroupedCalculator::class, 23.50) + ] + ], + $this->createProductMock(GroupedType::TYPE_CODE, 0), + 23.50 + ], + 'Get Mrsp Price with simple product and price calculator is grouped product type' => [ + [ + [ + 'productType' => GroupedType::TYPE_CODE, + 'priceCalculator' => $this->createPriceCalculatorMock( + MsrpGroupedCalculator::class, 0) + ] + ], + $this->createProductMock(Type::TYPE_SIMPLE, 24.88), + 24.88 + ] + ]; + } + + /** + * Create Price Calculator Mock + * + * @param string $class + * @param float $msrpPriceValue + * @return MockObject + */ + private function createPriceCalculatorMock($class, $msrpPriceValue) + { + $priceCalculatorMock = $this->createMock($class); + $priceCalculatorMock->expects($this->any())->method('getMsrpPriceValue')->willReturn($msrpPriceValue); + return $priceCalculatorMock; + } + + /** + * Create Product Mock + * + * @param string $typeId + * @param float $msrp + * @return MockObject + */ + private function createProductMock($typeId, $msrp) + { + $productMock = $this->createPartialMock(Product::class, ['getTypeId', 'getMsrp']); + $productMock->expects($this->any())->method('getTypeId')->willReturn($typeId); + $productMock->expects($this->any())->method('getMsrp')->willReturn($msrp); + return $productMock; + } +} From fb7aa9c9c41ec300d697eb2fbb5b3398fbbd7a00 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Mon, 16 Dec 2019 23:37:11 +0700 Subject: [PATCH 2422/2437] fix static test --- .../Test/Unit/Pricing/MsrpPriceCalculatorTest.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php index 613547943c9a4..aac6852b7000c 100644 --- a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php +++ b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php @@ -30,7 +30,8 @@ class MsrpPriceCalculatorTest extends TestCase public function testGetMsrpPriceValue($msrpPriceCalculators, $productMock, $expected) { $objectManager = new ObjectManager($this); - $pricing = $objectManager->getObject(MsrpPriceCalculator::class, + $pricing = $objectManager->getObject( + MsrpPriceCalculator::class, [ 'msrpPriceCalculators' => $msrpPriceCalculators ] @@ -52,7 +53,9 @@ public function getMsrpPriceValueDataProvider() [ 'productType' => GroupedType::TYPE_CODE, 'priceCalculator' => $this->createPriceCalculatorMock( - MsrpGroupedCalculator::class, 23.50) + MsrpGroupedCalculator::class, + 23.50 + ) ] ], $this->createProductMock(GroupedType::TYPE_CODE, 0), @@ -63,7 +66,9 @@ public function getMsrpPriceValueDataProvider() [ 'productType' => GroupedType::TYPE_CODE, 'priceCalculator' => $this->createPriceCalculatorMock( - MsrpGroupedCalculator::class, 0) + MsrpGroupedCalculator::class, + 0 + ) ] ], $this->createProductMock(Type::TYPE_SIMPLE, 24.88), From cf0725a78721e28a60cf68af5c30844424c89d1f Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 16 Dec 2019 19:52:01 +0200 Subject: [PATCH 2423/2437] Covering the UrlBuilder ViewModel by Unit Test --- .../ViewModel/Page/Grid/UrlBuilderTest.php | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php diff --git a/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php new file mode 100644 index 0000000000000..c75f86b7e5ffb --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php @@ -0,0 +1,191 @@ +<?php +/*** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Test\Unit\ViewModel\Page\Grid; + +use Magento\Cms\ViewModel\Page\Grid\UrlBuilder; +use Magento\Framework\Url\EncoderInterface; +use Magento\Framework\UrlInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class UrlBuilderTest + */ +class UrlBuilderTest extends TestCase +{ + /** + * @var UrlBuilder + */ + private $viewModel; + + /** + * @var UrlInterface|MockObject + */ + private $frontendUrlBuilderMock; + + /** + * @var EncoderInterface|MockObject + */ + private $urlEncoderMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * Set Up + */ + public function setUp() + { + $this->frontendUrlBuilderMock = $this->getMockBuilder(UrlInterface::class) + ->setMethods(['getUrl', 'setScope']) + ->getMockForAbstractClass(); + $this->urlEncoderMock = $this->createMock(EncoderInterface::class); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->viewModel = new UrlBuilder( + $this->frontendUrlBuilderMock, + $this->urlEncoderMock, + $this->storeManagerMock + ); + } + + /** + * Testing url builder with no scope provided + * + * @dataProvider nonScopedUrlsDataProvider + * + * @param array $url + * @param string $expected + * @param string $store + * @param null $scope + */ + public function testUrlBuilderWithNoScope(array $url, string $expected, string $store, $scope = null) + { + $this->frontendUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->with($url['path'], $url['params']) + ->willReturn($expected); + + $result = $this->viewModel->getUrl($url['path'], $scope, $store); + + $this->assertSame($expected, $result); + } + + /** + * Providing a non scoped urls + * + * @return array + */ + public function nonScopedUrlsDataProvider(): array + { + return [ + [ + [ + 'path' => 'test/view', + 'params' => [ + '_current' => false, + '_nosid' => true + ] + ], + 'http://domain.com/test/view/', + 'en' + ] + ]; + } + + /** + * Testing url builder with a scope provided + * + * @dataProvider scopedUrlsDataProvider + * + * @param string $storeCode + * @param string $defaultStoreCode + * @param array $urlParams + * @param string $scope + */ + public function testScopedUrlBuilder( + string $storeCode, + string $defaultStoreCode, + array $urlParams, + string $scope = 'store' + ) { + /** @var StoreInterface|MockObject $storeMock */ + $storeMock = $this->createMock(StoreInterface::class); + $storeMock->expects($this->any()) + ->method('getCode') + ->willReturn($defaultStoreCode); + $this->storeManagerMock->expects($this->once()) + ->method('getDefaultStoreView') + ->willReturn($storeMock); + + $this->frontendUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->withConsecutive( + [ + 'test/index', + [ + '_current' => false, + '_nosid' => true, + '_query' => [ + StoreManagerInterface::PARAM_NAME => $storeCode + ] + ] + ], [ + 'stores/store/switch', + $urlParams + ] + ) + ->willReturnOnConsecutiveCalls( + 'http://domain.com/test', + 'http://domain.com/test/index' + ); + + $result = $this->viewModel->getUrl('test/index', $scope, $storeCode); + + $this->assertSame('http://domain.com/test/index', $result); + } + + /** + * Providing a scoped urls + * + * @return array + */ + public function scopedUrlsDataProvider(): array + { + $enStoreCode = 'en'; + $frStoreCode = 'fr'; + $scopedDefaultUrlParams = $defaultUrlParams = [ + '_current' => false, + '_nosid' => true, + '_query' => [ + '___store' => $enStoreCode, + 'uenc' => null, + ] + ]; + $scopedDefaultUrlParams['_query']['___from_store'] = $frStoreCode; + + return [ + [ + $enStoreCode, + $enStoreCode, + $defaultUrlParams, + ], + [ + $enStoreCode, + $frStoreCode, + $scopedDefaultUrlParams + ] + ]; + } +} From 59d6c757bf6a010ebb7fdd12af9650ea937be417 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Mon, 16 Dec 2019 20:36:41 +0200 Subject: [PATCH 2424/2437] Small adjustments --- .../Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php index c75f86b7e5ffb..fbb2fb1eb65c5 100644 --- a/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php +++ b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php @@ -17,6 +17,8 @@ /** * Class UrlBuilderTest + * + * Testing the UrlBuilder */ class UrlBuilderTest extends TestCase { @@ -141,7 +143,8 @@ public function testScopedUrlBuilder( StoreManagerInterface::PARAM_NAME => $storeCode ] ] - ], [ + ], + [ 'stores/store/switch', $urlParams ] From 8258a162a4629d0ae00ed8c36adff846cb99fd89 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Mon, 16 Dec 2019 15:25:24 -0600 Subject: [PATCH 2425/2437] add missed dependency on jquery ui core module --- app/code/Magento/Ui/view/base/web/js/modal/modal.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index cefe79b42e503..bcbb2f3c31dbd 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -15,6 +15,7 @@ define([ 'text!ui/template/modal/modal-custom.html', 'Magento_Ui/js/lib/key-codes', 'jquery-ui-modules/widget', + 'jquery-ui-modules/core', 'mage/translate' ], function ($, _, template, popupTpl, slideTpl, customTpl, keyCodes) { 'use strict'; From b6819dc9a5440e9f75dc980bf9bc1645a10ca3ed Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 16 Dec 2019 15:28:21 -0600 Subject: [PATCH 2426/2437] MC-25203: Introduce PHPStan code analysis tool to the static build --- composer.json | 4 +- composer.lock | 554 +++++++++++------- .../Design/Theme/Edit/Tab/GeneralTest.php | 2 +- .../Formatters/FilteredErrorFormatter.php | 142 +++++ .../framework/Magento/PhpStan/autoload.php | 34 ++ .../CodingStandard/Tool/PhpStan.php | 108 ++++ dev/tests/static/framework/bootstrap.php | 4 +- .../Formatters/FilteredErrorFormatterTest.php | 143 +++++ .../Fixtures/ClassWithIgnoreAnnotation.php | 44 ++ .../Fixtures/ClassWithoutIgnoreAnnotation.php | 33 ++ .../ClassAnnotationStructureSniffTest.php | 9 +- .../MethodAnnotationStructureSniffTest.php | 6 +- .../Magento/Test/Php/LiveCodeTest.php | 36 ++ .../Php/_files/phpstan/blacklist/common.txt | 14 + .../Test/Php/_files/phpstan/phpstan.neon | 38 ++ .../Framework/EntityManager/TypeResolver.php | 6 +- .../Test/Unit/Config/ConfigTest.php | 90 +-- .../Unit/Autoloader/FactoryGenerator.php | 3 + 18 files changed, 988 insertions(+), 282 deletions(-) create mode 100644 dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php create mode 100644 dev/tests/static/framework/Magento/PhpStan/autoload.php create mode 100644 dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/PhpStan.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithIgnoreAnnotation.php create mode 100644 dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithoutIgnoreAnnotation.php create mode 100644 dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt create mode 100644 dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon diff --git a/composer.json b/composer.json index 4e78f54942576..59737c298b720 100644 --- a/composer.json +++ b/composer.json @@ -92,6 +92,7 @@ "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", + "phpstan/phpstan": "^0.12.2", "phpunit/phpunit": "~6.5.0", "sebastian/phpcpd": "~3.0.0", "squizlabs/php_codesniffer": "~3.4.0" @@ -341,7 +342,8 @@ "Magento\\Tools\\": "dev/tools/Magento/Tools/", "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/", "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/", - "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/" + "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/", + "Magento\\PhpStan\\": "dev/tests/static/framework/Magento/PhpStan/" } }, "prefer-stable": true diff --git a/composer.lock b/composer.lock index ba126b3eabefc..8391dd2f48518 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e3ad90186a7742707e4c12cda2580b35", + "content-hash": "7effdb746287f89357497d7bc2ed1bf4", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.4", + "version": "1.2.5", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527" + "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527", - "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/62e8fc2dc550e5d6d8c9360c7721662670f58149", + "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149", "shasum": "" }, "require": { @@ -221,7 +221,7 @@ "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" }, "type": "library", "extra": { @@ -253,7 +253,7 @@ "ssl", "tls" ], - "time": "2019-08-30T08:44:50+00:00" + "time": "2019-12-11T14:44:42+00:00" }, { "name": "composer/composer", @@ -595,16 +595,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.4.1", + "version": "6.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11" + "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", - "reference": "0895c932405407fd3a7368b6910c09a24d26db11", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", + "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", "shasum": "" }, "require": { @@ -619,12 +619,13 @@ "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { @@ -657,7 +658,7 @@ "rest", "web service" ], - "time": "2019-10-23T15:58:00+00:00" + "time": "2019-12-07T18:20:45+00:00" }, { "name": "guzzlehttp/promises", @@ -1065,16 +1066,16 @@ }, { "name": "magento/zendframework1", - "version": "1.14.2", + "version": "1.14.3", "source": { "type": "git", "url": "https://github.com/magento/zf1.git", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f" + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/8221062d42a198e431d183bbe672e5e1a2f98c5f", - "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f", + "url": "https://api.github.com/repos/magento/zf1/zipball/726855dfb080089dc7bc7b016624129f8e7bc4e5", + "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5", "shasum": "" }, "require": { @@ -1108,7 +1109,7 @@ "ZF1", "framework" ], - "time": "2019-07-26T16:43:11+00:00" + "time": "2019-11-26T15:09:40+00:00" }, { "name": "monolog/monolog", @@ -2083,16 +2084,16 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9" + "reference": "64acec7e0d67125e9f4656c68d4a38a42ab5a0b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", - "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/64acec7e0d67125e9f4656c68d4a38a42ab5a0b7", + "reference": "64acec7e0d67125e9f4656c68d4a38a42ab5a0b7", "shasum": "" }, "require": { @@ -2101,7 +2102,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -2132,20 +2133,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-10-02T08:36:26+00:00" + "time": "2019-10-12T00:35:04+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.3.8", + "version": "v4.3.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0df002fd4f500392eabd243c2947061a50937287" + "reference": "87a1ae7480f2020818013605a65776b9033bcc4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", - "reference": "0df002fd4f500392eabd243c2947061a50937287", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a1ae7480f2020818013605a65776b9033bcc4f", + "reference": "87a1ae7480f2020818013605a65776b9033bcc4f", "shasum": "" }, "require": { @@ -2202,7 +2203,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-11-03T09:04:05+00:00" + "time": "2019-11-28T13:25:45+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2264,16 +2265,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" + "reference": "40c2606131d56eff6f193b6e2ceb92414653b591" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", - "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/40c2606131d56eff6f193b6e2ceb92414653b591", + "reference": "40c2606131d56eff6f193b6e2ceb92414653b591", "shasum": "" }, "require": { @@ -2283,7 +2284,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -2310,20 +2311,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2019-08-20T14:07:54+00:00" + "time": "2019-11-26T23:16:41+00:00" }, { "name": "symfony/finder", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" + "reference": "ce8743441da64c41e2a667b8eb66070444ed911e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", - "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "url": "https://api.github.com/repos/symfony/finder/zipball/ce8743441da64c41e2a667b8eb66070444ed911e", + "reference": "ce8743441da64c41e2a667b8eb66070444ed911e", "shasum": "" }, "require": { @@ -2332,7 +2333,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -2359,20 +2360,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-10-30T12:53:54+00:00" + "time": "2019-11-17T21:56:56+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", "shasum": "" }, "require": { @@ -2384,7 +2385,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2417,20 +2418,20 @@ "polyfill", "portable" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" + "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", + "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", "shasum": "" }, "require": { @@ -2442,7 +2443,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -2476,20 +2477,20 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T14:18:11+00:00" }, { "name": "symfony/process", - "version": "v4.3.8", + "version": "v4.3.9", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" + "reference": "207dab1f17d34ad71ea72e9741ab8049a9d8251b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", - "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", + "url": "https://api.github.com/repos/symfony/process/zipball/207dab1f17d34ad71ea72e9741ab8049a9d8251b", + "reference": "207dab1f17d34ad71ea72e9741ab8049a9d8251b", "shasum": "" }, "require": { @@ -2525,7 +2526,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-11-28T10:05:26+00:00" }, { "name": "tedivm/jshrink", @@ -3529,16 +3530,16 @@ }, { "name": "zendframework/zend-http", - "version": "2.10.0", + "version": "2.11.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0" + "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/4b4983178693a8fdda53b0bbee58552e2d2b1ac0", - "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/76000da8490b8685d63ff6f6ff8eefa459f6e9e7", + "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7", "shasum": "" }, "require": { @@ -3559,8 +3560,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { @@ -3580,7 +3581,7 @@ "zend", "zf" ], - "time": "2019-02-19T18:58:14+00:00" + "time": "2019-12-04T23:02:34+00:00" }, { "name": "zendframework/zend-hydrator", @@ -3644,22 +3645,26 @@ }, { "name": "zendframework/zend-i18n", - "version": "2.9.2", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07" + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/e17a54b3aee333ab156958f570cde630acee8b07", - "reference": "e17a54b3aee333ab156958f570cde630acee8b07", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/84038e6a1838b611dcc491b1c40321fa4c3a123c", + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c", "shasum": "" }, "require": { + "ext-intl": "*", "php": "^5.6 || ^7.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, + "conflict": { + "phpspec/prophecy": "<1.9.0" + }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", @@ -3672,7 +3677,6 @@ "zendframework/zend-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", "zendframework/zend-cache": "Zend\\Cache component", "zendframework/zend-config": "Zend\\Config component", "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", @@ -3685,8 +3689,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\I18n", @@ -3708,7 +3712,7 @@ "i18n", "zf" ], - "time": "2019-09-30T12:04:37+00:00" + "time": "2019-12-12T14:08:22+00:00" }, { "name": "zendframework/zend-inputfilter", @@ -4794,16 +4798,16 @@ }, { "name": "zendframework/zend-view", - "version": "2.11.3", + "version": "2.11.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5" + "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e766457bd6ce13c5354e443bb949511b6904d7f5", - "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", + "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", "shasum": "" }, "require": { @@ -4877,7 +4881,7 @@ "view", "zf" ], - "time": "2019-10-11T21:10:04+00:00" + "time": "2019-12-04T08:40:50+00:00" } ], "packages-dev": [ @@ -5280,16 +5284,16 @@ }, { "name": "codeception/phpunit-wrapper", - "version": "6.7.0", + "version": "6.7.1", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "93f59e028826464eac086052fa226e58967f6907" + "reference": "d3b611635b47a583dfaa1a9e98b98fa476d14025" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/93f59e028826464eac086052fa226e58967f6907", - "reference": "93f59e028826464eac086052fa226e58967f6907", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/d3b611635b47a583dfaa1a9e98b98fa476d14025", + "reference": "d3b611635b47a583dfaa1a9e98b98fa476d14025", "shasum": "" }, "require": { @@ -5322,7 +5326,7 @@ } ], "description": "PHPUnit classes used by Codeception", - "time": "2019-08-18T15:43:35+00:00" + "time": "2019-11-23T18:22:38+00:00" }, { "name": "codeception/stub", @@ -6162,16 +6166,16 @@ }, { "name": "doctrine/cache", - "version": "1.9.1", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", - "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", + "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62", + "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62", "shasum": "" }, "require": { @@ -6238,10 +6242,9 @@ "memcached", "php", "redis", - "riak", "xcache" ], - "time": "2019-11-15T14:31:57+00:00" + "time": "2019-11-29T15:36:20+00:00" }, { "name": "doctrine/inflector", @@ -6620,16 +6623,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.9.0", + "version": "v1.9.1", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" + "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", - "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/fc10d778e4b84d5bd315dad194661e091d307c6f", + "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f", "shasum": "" }, "require": { @@ -6642,7 +6645,9 @@ }, "type": "library", "extra": { - "branch-alias": [] + "branch-alias": { + "dev-master": "1.9-dev" + } }, "autoload": { "psr-4": { @@ -6664,7 +6669,7 @@ "faker", "fixtures" ], - "time": "2019-11-14T13:13:06+00:00" + "time": "2019-12-12T13:22:17+00:00" }, { "name": "grasmash/expander", @@ -7030,16 +7035,16 @@ }, { "name": "league/flysystem", - "version": "1.0.57", + "version": "1.0.61", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a" + "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", - "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fb13c01784a6c9f165a351e996871488ca2d8c9", + "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9", "shasum": "" }, "require": { @@ -7110,7 +7115,7 @@ "sftp", "storage" ], - "time": "2019-10-16T21:01:05+00:00" + "time": "2019-12-08T21:46:50+00:00" }, { "name": "lusitanian/oauth", @@ -7344,16 +7349,16 @@ }, { "name": "mustache/mustache", - "version": "v2.12.0", + "version": "v2.13.0", "source": { "type": "git", "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" + "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", - "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e95c5a008c23d3151d59ea72484d4f72049ab7f4", + "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4", "shasum": "" }, "require": { @@ -7386,20 +7391,20 @@ "mustache", "templating" ], - "time": "2017-07-11T12:54:05+00:00" + "time": "2019-11-23T21:40:31+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", + "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", "shasum": "" }, "require": { @@ -7434,7 +7439,59 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" + "time": "2019-12-15T19:12:40+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9a9981c347c5c49d6dfe5cf826bb882b824080dc", + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "0.0.5", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2019-11-08T13:50:10+00:00" }, { "name": "pdepend/pdepend", @@ -7926,20 +7983,20 @@ "authors": [ { "name": "Manuel Pichler", - "role": "Project Founder", "email": "github@manuel-pichler.de", - "homepage": "https://github.com/manuelpichler" + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" }, { "name": "Marc Würth", - "role": "Project Maintainer", "email": "ravage@bluewin.ch", - "homepage": "https://github.com/ravage84" + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" }, { "name": "Other contributors", - "role": "Contributors", - "homepage": "https://github.com/phpmd/phpmd/graphs/contributors" + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" } ], "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", @@ -7955,33 +8012,34 @@ }, { "name": "phpoption/phpoption", - "version": "1.5.2", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793" + "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793", - "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", + "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^5.5.9 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.7|^5.0" + "bamarni/composer-bin-plugin": "^1.3", + "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5-dev" + "dev-master": "1.7-dev" } }, "autoload": { - "psr-0": { - "PhpOption\\": "src/" + "psr-4": { + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", @@ -7992,6 +8050,10 @@ { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" } ], "description": "Option Type for PHP", @@ -8001,7 +8063,7 @@ "php", "type" ], - "time": "2019-11-06T22:27:00+00:00" + "time": "2019-12-15T19:35:24+00:00" }, { "name": "phpspec/prophecy", @@ -8066,6 +8128,46 @@ ], "time": "2019-10-03T11:07:50+00:00" }, + { + "name": "phpstan/phpstan", + "version": "0.12.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c15a6ea55da71d8133399306f560cfe4d30301b7", + "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.3.0", + "php": "^7.1" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "time": "2019-12-14T13:41:17+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "5.3.2", @@ -9254,27 +9356,27 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" + "reference": "e19e465c055137938afd40cfddd687e7511bbbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", - "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e19e465c055137938afd40cfddd687e7511bbbf0", + "reference": "e19e465c055137938afd40cfddd687e7511bbbf0", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/dom-crawler": "~3.4|~4.0" + "symfony/dom-crawler": "^3.4|^4.0|^5.0" }, "require-dev": { - "symfony/css-selector": "~3.4|~4.0", - "symfony/http-client": "^4.3", - "symfony/mime": "^4.3", - "symfony/process": "~3.4|~4.0" + "symfony/css-selector": "^3.4|^4.0|^5.0", + "symfony/http-client": "^4.3|^5.0", + "symfony/mime": "^4.3|^5.0", + "symfony/process": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/process": "" @@ -9282,7 +9384,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9309,36 +9411,36 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-10-28T20:30:34+00:00" }, { "name": "symfony/config", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8267214841c44d315a55242ea867684eb43c42ce" + "reference": "7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8267214841c44d315a55242ea867684eb43c42ce", - "reference": "8267214841c44d315a55242ea867684eb43c42ce", + "url": "https://api.github.com/repos/symfony/config/zipball/7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c", + "reference": "7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/filesystem": "~3.4|~4.0", + "symfony/filesystem": "^3.4|^4.0|^5.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/finder": "<3.4" }, "require-dev": { - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~3.4|~4.0", - "symfony/finder": "~3.4|~4.0", - "symfony/messenger": "~4.1", - "symfony/yaml": "~3.4|~4.0" + "symfony/event-dispatcher": "^3.4|^4.0|^5.0", + "symfony/finder": "^3.4|^4.0|^5.0", + "symfony/messenger": "^4.1|^5.0", + "symfony/service-contracts": "^1.1|^2", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -9346,7 +9448,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9373,29 +9475,29 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-11-08T08:31:27+00:00" + "time": "2019-12-01T10:50:45+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64" + "reference": "ad46a4def1325befab696b49c839dffea3fc92bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/80c6d9e19467dfbba14f830ed478eb592ce51b64", - "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ad46a4def1325befab696b49c839dffea3fc92bd", + "reference": "ad46a4def1325befab696b49c839dffea3fc92bd", "shasum": "" }, "require": { "php": "^7.1.3", "psr/container": "^1.0", - "symfony/service-contracts": "^1.1.6" + "symfony/service-contracts": "^1.1.6|^2" }, "conflict": { - "symfony/config": "<4.3", + "symfony/config": "<4.3|>=5.0", "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" @@ -9406,8 +9508,8 @@ }, "require-dev": { "symfony/config": "^4.3", - "symfony/expression-language": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" + "symfony/expression-language": "^3.4|^4.0|^5.0", + "symfony/yaml": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/config": "", @@ -9419,7 +9521,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9446,20 +9548,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-11-08T16:22:27+00:00" + "time": "2019-12-01T10:19:36+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" + "reference": "36bbcab9369fc2f583220890efd43bf262d563fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", - "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/36bbcab9369fc2f583220890efd43bf262d563fd", + "reference": "36bbcab9369fc2f583220890efd43bf262d563fd", "shasum": "" }, "require": { @@ -9472,7 +9574,7 @@ }, "require-dev": { "masterminds/html5": "^2.6", - "symfony/css-selector": "~3.4|~4.0" + "symfony/css-selector": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/css-selector": "" @@ -9480,7 +9582,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9507,7 +9609,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-10-28T17:07:32+00:00" + "time": "2019-10-29T11:38:30+00:00" }, { "name": "symfony/http-foundation", @@ -9566,16 +9668,16 @@ }, { "name": "symfony/options-resolver", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" + "reference": "2be23e63f33de16b49294ea6581f462932a77e2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", - "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2be23e63f33de16b49294ea6581f462932a77e2f", + "reference": "2be23e63f33de16b49294ea6581f462932a77e2f", "shasum": "" }, "require": { @@ -9584,7 +9686,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9616,20 +9718,20 @@ "configuration", "options" ], - "time": "2019-10-28T20:59:01+00:00" + "time": "2019-10-28T21:57:16+00:00" }, { "name": "symfony/polyfill-php54", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "a043bcced870214922fbb4bf22679d431ec0296a" + "reference": "dd1618047426412036e98d159940d58a81fc392c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/a043bcced870214922fbb4bf22679d431ec0296a", - "reference": "a043bcced870214922fbb4bf22679d431ec0296a", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/dd1618047426412036e98d159940d58a81fc392c", + "reference": "dd1618047426412036e98d159940d58a81fc392c", "shasum": "" }, "require": { @@ -9638,7 +9740,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -9674,20 +9776,20 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-php55", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265" + "reference": "b0d838f225725e2951af1aafc784d2e5ea7b656e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/548bb39407e78e54f785b4e18c7e0d5d9e493265", - "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b0d838f225725e2951af1aafc784d2e5ea7b656e", + "reference": "b0d838f225725e2951af1aafc784d2e5ea7b656e", "shasum": "" }, "require": { @@ -9697,7 +9799,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -9730,20 +9832,20 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "54b4c428a0054e254223797d2713c31e08610831" + "reference": "af23c7bb26a73b850840823662dda371484926c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", - "reference": "54b4c428a0054e254223797d2713c31e08610831", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/af23c7bb26a73b850840823662dda371484926c4", + "reference": "af23c7bb26a73b850840823662dda371484926c4", "shasum": "" }, "require": { @@ -9753,7 +9855,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -9789,20 +9891,20 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.12.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "04ce3335667451138df4307d6a9b61565560199e" + "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", - "reference": "04ce3335667451138df4307d6a9b61565560199e", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038", + "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038", "shasum": "" }, "require": { @@ -9811,7 +9913,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-master": "1.13-dev" } }, "autoload": { @@ -9844,24 +9946,24 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2019-11-27T13:56:44+00:00" }, { "name": "symfony/service-contracts", - "version": "v1.1.8", + "version": "v2.0.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" + "reference": "144c5e51266b281231e947b51223ba14acf1a749" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", + "reference": "144c5e51266b281231e947b51223ba14acf1a749", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^7.2.5", "psr/container": "^1.0" }, "suggest": { @@ -9870,7 +9972,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -9902,30 +10004,30 @@ "interoperability", "standards" ], - "time": "2019-10-14T12:27:06+00:00" + "time": "2019-11-18T17:27:11+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0" + "reference": "5745b514fc56ae1907c6b8ed74f94f90f64694e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e96c259de6abcd0cead71f0bf4d730d53ee464d0", - "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5745b514fc56ae1907c6b8ed74f94f90f64694e9", + "reference": "5745b514fc56ae1907c6b8ed74f94f90f64694e9", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/service-contracts": "^1.0" + "symfony/service-contracts": "^1.0|^2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -9952,20 +10054,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-11-05T14:48:09+00:00" + "time": "2019-11-05T16:11:08+00:00" }, { "name": "symfony/yaml", - "version": "v4.3.8", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "324cf4b19c345465fad14f3602050519e09e361d" + "reference": "76de473358fe802578a415d5bb43c296cf09d211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", - "reference": "324cf4b19c345465fad14f3602050519e09e361d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/76de473358fe802578a415d5bb43c296cf09d211", + "reference": "76de473358fe802578a415d5bb43c296cf09d211", "shasum": "" }, "require": { @@ -9976,7 +10078,7 @@ "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "~3.4|~4.0" + "symfony/console": "^3.4|^4.0|^5.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -9984,7 +10086,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "4.4-dev" } }, "autoload": { @@ -10011,7 +10113,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-10-30T12:58:49+00:00" + "time": "2019-11-12T14:51:11+00:00" }, { "name": "theseer/fdomdocument", @@ -10146,31 +10248,29 @@ }, { "name": "webmozart/assert", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", + "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", + "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "vimeo/psalm": "<3.6.0" + }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -10192,7 +10292,7 @@ "check", "validate" ], - "time": "2019-08-24T08:43:50+00:00" + "time": "2019-11-24T13:36:37+00:00" }, { "name": "weew/helpers-array", diff --git a/dev/tests/integration/testsuite/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/GeneralTest.php b/dev/tests/integration/testsuite/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/GeneralTest.php index 2539011f25a36..2613b71c24202 100644 --- a/dev/tests/integration/testsuite/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/GeneralTest.php +++ b/dev/tests/integration/testsuite/Magento/Theme/Block/Adminhtml/System/Design/Theme/Edit/Tab/GeneralTest.php @@ -16,7 +16,7 @@ class GeneralTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\View\Design\ThemeInterface */ protected $_theme; - /** @var \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab_General */ + /** @var \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\General */ protected $_block; protected function setUp() diff --git a/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php new file mode 100644 index 0000000000000..b3a4bd9ae0791 --- /dev/null +++ b/dev/tests/static/framework/Magento/PhpStan/Formatters/FilteredErrorFormatter.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PhpStan\Formatters; + +use PHPStan\Command\AnalysisResult; +use PHPStan\Command\ErrorFormatter\TableErrorFormatter; +use PHPStan\Command\Output; + +/** + * To mute the PHPStan error message add a comment above the reported error line. + * + * Example of usage: + * + * // phpstan:ignore "Method Magento\TestModule\TestClass::testMethod() invoked with 1 parameter, 0 required." + * $this->testMethod(1); + * + * or replace some part of error message with * + * + * // phpstan:ignore "Method * invoked with 1 parameter, 0 required." + * $this->testMethod(1); + * + * or just + * + * // phpstan:ignore + * $this->testMethod(1); + * + * or + * + * $this->testMethod(1); // phpstan:ignore + * + * The error message will be suppressed. + * + * @see \Magento\PhpStan\Formatters\Fixtures\ClassWithIgnoreAnnotation + */ +class FilteredErrorFormatter extends TableErrorFormatter +{ + private const MUTE_ERROR_ANNOTATION = 'phpstan:ignore'; + + private const NO_ERRORS = 0; + + /** + * @inheritdoc + */ + public function formatErrors(AnalysisResult $analysisResult, Output $output): int + { + if (!$analysisResult->hasErrors()) { + $style = $output->getStyle(); + $style->success('No errors'); + return self::NO_ERRORS; + } + + $fileSpecificErrorsWithoutIgnoredErrors = $this->clearIgnoredErrors( + $analysisResult->getFileSpecificErrors() + ); + + $clearedAnalysisResult = new AnalysisResult( + $fileSpecificErrorsWithoutIgnoredErrors, + $analysisResult->getNotFileSpecificErrors(), + $analysisResult->isDefaultLevelUsed(), + $analysisResult->hasInferrablePropertyTypesFromConstructor(), + $analysisResult->getProjectConfigFile() + ); + + return parent::formatErrors($clearedAnalysisResult, $output); + } + + /** + * Filters error list. + * + * @param array $fileSpecificErrors + * @return array + */ + private function clearIgnoredErrors(array $fileSpecificErrors): array + { + foreach ($fileSpecificErrors as $index => $error) { + $fileName = $error->getFile(); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + if (!file_exists($fileName)) { + continue; + } + + $line = $error->getLine() ? $this->getLineWithMuteErrorAnnotation($error->getLine(), $fileName) : null; + if ($line === null) { + continue; + } + + $extractErrorPattern = '@' . self::MUTE_ERROR_ANNOTATION . '\s+"(.*?)"@'; + $errorPattern = preg_match($extractErrorPattern, $line, $result) ? $this->preparePattern($result[1]) : ''; + if ($errorPattern && !preg_match('@' . $errorPattern . '@i', $error->getMessage())) { + continue; + } + + unset($fileSpecificErrors[$index]); + } + + return $fileSpecificErrors; + } + + /** + * Returns context of the line with mute error annotation. + * + * @param int $errorLine + * @param string $fileName + * @return string|null + */ + private function getLineWithMuteErrorAnnotation(int $errorLine, string $fileName): ?string + { + $file = new \SplFileObject($fileName); + $lineNumbersToCheck = [ + $errorLine - 2, // the line above to the line that caused the error + $errorLine - 1, // the line that caused the error + $errorLine - 3, // the line two lines above to the line that caused the error + ]; + + foreach ($lineNumbersToCheck as $lineNumber) { + $file->seek($lineNumber > 0 ? $lineNumber : 0); + $line = $file->current(); + if (strpos($line, self::MUTE_ERROR_ANNOTATION) !== false) { + return $line; + } + } + + return null; + } + + /** + * Prepares error pattern. + * + * @param string $errorDescription + * @return string + */ + private function preparePattern(string $errorDescription) + { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + return str_replace('*', '(?:.*?)', addcslashes(trim($errorDescription), '\()[]')); + } +} diff --git a/dev/tests/static/framework/Magento/PhpStan/autoload.php b/dev/tests/static/framework/Magento/PhpStan/autoload.php new file mode 100644 index 0000000000000..baf44b2381759 --- /dev/null +++ b/dev/tests/static/framework/Magento/PhpStan/autoload.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// phpcs:disable + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Code\Generator\Io; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\TestFramework\Unit\Autoloader\ExtensionAttributesGenerator; +use Magento\Framework\TestFramework\Unit\Autoloader\ExtensionAttributesInterfaceGenerator; +use Magento\Framework\TestFramework\Unit\Autoloader\FactoryGenerator; +use Magento\Framework\TestFramework\Unit\Autoloader\GeneratedClassesAutoloader; + +if (!defined('TESTS_TEMP_DIR')) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction + define('TESTS_TEMP_DIR', dirname(__DIR__) . '/../../tmp'); +} + +$generatorIo = new Io( + new File(), + TESTS_TEMP_DIR . '/' . DirectoryList::getDefaultConfig()[DirectoryList::GENERATED_CODE][DirectoryList::PATH] +); +$generatedCodeAutoloader = new GeneratedClassesAutoloader( + [ + new ExtensionAttributesGenerator(), + new ExtensionAttributesInterfaceGenerator(), + new FactoryGenerator(), + ], + $generatorIo +); +spl_autoload_register([$generatedCodeAutoloader, 'load']); diff --git a/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/PhpStan.php b/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/PhpStan.php new file mode 100644 index 0000000000000..9048262722d48 --- /dev/null +++ b/dev/tests/static/framework/Magento/TestFramework/CodingStandard/Tool/PhpStan.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\CodingStandard\Tool; + +use Magento\TestFramework\CodingStandard\ToolInterface; + +/** + * PhpStan tool wrapper. + */ +class PhpStan implements ToolInterface +{ + /** + * Rule level to be used. + * + * @see https://github.com/phpstan/phpstan#rule-levels + */ + private const RULE_LEVEL = 0; + + /** + * Memory limit required by PHPStan for full Magento project scan. + */ + private const MEMORY_LIMIT = '4G'; + + /** + * Error formatter to be used. + * + * @see https://github.com/phpstan/phpstan#existing-error-formatters-to-be-used + */ + private const ERROR_FORMAT = 'filtered'; + + /** + * Report file. + * + * @var string + */ + private $reportFile; + + /** + * PHPStan configuration file in neon format. + * + * @var string + */ + private $confFile; + + /** + * @param string $confFile + * @param string $reportFile + */ + public function __construct(string $confFile, string $reportFile) + { + $this->reportFile = $reportFile; + $this->confFile = $confFile; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function canRun(): bool + { + // phpcs:disable Magento2.Security.InsecureFunction + exec($this->getCommand() . ' --version', $output, $exitCode); + return $exitCode === 0; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function run(array $whiteList): int + { + if (empty($whiteList)) { + return 0; + } + + $command = $this->getCommand() . ' analyse' . + ' --level ' . self::RULE_LEVEL . + ' --no-progress' . + ' --error-format=' . self::ERROR_FORMAT . + ' --memory-limit=' . self::MEMORY_LIMIT . + // phpcs:ignore Magento2.Functions.DiscouragedFunction + ' --configuration ' . escapeshellarg($this->confFile) . + ' ' . implode(' ', $whiteList) . + ' > ' . $this->reportFile; + + // phpcs:disable Magento2.Security.InsecureFunction + exec($command, $output, $exitCode); + + return $exitCode; + } + + /** + * Get PHPStan CLI command + * + * @return string + */ + private function getCommand(): string + { + // phpcs:ignore Magento2.Security.IncludeFile + $vendorDir = require BP . '/app/etc/vendor_path.php'; + return 'php ' . BP . '/' . $vendorDir . '/bin/phpstan'; + } +} diff --git a/dev/tests/static/framework/bootstrap.php b/dev/tests/static/framework/bootstrap.php index 5e3336ebc28de..e43337cf06026 100644 --- a/dev/tests/static/framework/bootstrap.php +++ b/dev/tests/static/framework/bootstrap.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +// phpcs:disable + use Magento\Framework\App\Utility\Files; use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\Component\DirSearch; @@ -15,7 +17,7 @@ require __DIR__ . '/autoload.php'; if (!defined('TESTS_TEMP_DIR')) { - define('TESTS_TEMP_DIR', __DIR__ . DIRECTORY_SEPARATOR . 'tmp'); + define('TESTS_TEMP_DIR', dirname(__DIR__) . '/tmp'); } setCustomErrorHandler(); diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php new file mode 100644 index 0000000000000..dfedd625fdb0e --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php @@ -0,0 +1,143 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +namespace Magento\PhpStan\Formatters; + +use PHPStan\Analyser\Error; +use PHPStan\Command\AnalysisResult; +use PHPStan\File\FuzzyRelativePathHelper; +use PHPStan\ShouldNotHappenException; +use PHPStan\Testing\ErrorFormatterTestCase; + +/** + * Tests filter error formatter. + */ +class FilteredErrorFormatterTest extends ErrorFormatterTestCase +{ + protected const DIRECTORY_PATH = __DIR__ . '/Fixtures'; + + /** + * Tests errors filtering. + * + * @param string $message + * @param int $exitCode + * @param array $fileErrors + * @param string $expected + * @throws ShouldNotHappenException + * + * @dataProvider dataFormatterOutputProvider + */ + public function testFormatErrors( + string $message, + int $exitCode + array $fileErrors, + string $expected + ): void { + $formatter = new FilteredErrorFormatter( + new FuzzyRelativePathHelper(self::DIRECTORY_PATH, '/', []), + false, + false, + false, + true + ); + + $analysisResult = new AnalysisResult( + $fileErrors, + [], + false, + false, + null + ); + + $this->assertSame( + $exitCode, + $formatter->formatErrors( + $analysisResult, + $this->getOutput() + ), + sprintf('%s: response code do not match', $message) + ); + $this->assertEquals( + $expected, + $this->getOutputContent(), + sprintf('%s: output do not match', $message) + ); + } + + /** + * @return array + */ + public function dataFormatterOutputProvider(): array + { + // phpcs:disable Generic.Files.LineLength.TooLong + $errorMessage = 'Method Magento\PhpStan\Formatters\Fixtures\ClassWithIgnoreAnnotation::testMethod() invoked with 2 parameters, 1 required.'; + // phpcs:enable Generic.Files.LineLength.TooLong + + return [ + [ + 'No errors', + 0, + [], + "\n [OK] No errors\n\n", + ], + [ + 'All errors are suppressed by ignore annotations', + 0, + [ + new Error( + 'Method level error', + self::DIRECTORY_PATH . '/ClassWithIgnoreAnnotation.php', + 22 + ), + new Error( + $errorMessage, + self::DIRECTORY_PATH . '/ClassWithIgnoreAnnotation.php', + 25 + ), + new Error( + $errorMessage, + self::DIRECTORY_PATH . '/ClassWithIgnoreAnnotation.php', + 28 + ), + new Error( + $errorMessage, + self::DIRECTORY_PATH . '/ClassWithIgnoreAnnotation.php', + 31 + ), + new Error( + $errorMessage, + self::DIRECTORY_PATH . '/ClassWithIgnoreAnnotation.php', + 33 + ), + ], + "\n [OK] No errors\n\n", + ], + [ + 'Errors aren\'t suppressed by ignore annotations', + 1, + [ + new Error( + $errorMessage, + self::DIRECTORY_PATH . '/ClassWithoutIgnoreAnnotation.php', + 21 + ), + ], + // phpcs:disable Generic.Files.LineLength.TooLong + ' ------ --------------------------------------------------------------------------------------------------------------------------- + Line ClassWithoutIgnoreAnnotation.php + ------ --------------------------------------------------------------------------------------------------------------------------- + 21 Method Magento\PhpStan\Formatters\Fixtures\ClassWithIgnoreAnnotation::testMethod() invoked with 2 parameters, 1 required. + ------ --------------------------------------------------------------------------------------------------------------------------- + + [ERROR] Found 1 error + +', + // phpcs:enable Generic.Files.LineLength.TooLong + ] + ]; + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithIgnoreAnnotation.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithIgnoreAnnotation.php new file mode 100644 index 0000000000000..dc0637704ec82 --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithIgnoreAnnotation.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PhpStan\Formatters\Fixtures; + +/** + * Class ClassWithIgnoreAnnotation + * + * phpcs:ignoreFile + */ +class ClassWithIgnoreAnnotation +{ + /** + * Test method. + * phpstan:ignore "Method level error" + */ + public function getProductList() + { + // phpstan:ignore "Method Magento\PhpStan\Formatters\Fixtures\ClassWithIgnoreAnnotation::testMethod() invoked with 2 parameters, 1 required." + $this->testMethod('test1', 'test2'); + + // phpstan:ignore "Method * invoked with 2 parameters, 1 required." + $this->testMethod('test1', 'test2'); + + // phpstan:ignore + $this->testMethod('test1', 'test2'); + + $this->testMethod('test1', 'test2'); // phpstan:ignore + } + + /** + * @param string $arg1 + * @return string + */ + private function testMethod(string $arg1) + { + return $arg1; + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithoutIgnoreAnnotation.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithoutIgnoreAnnotation.php new file mode 100644 index 0000000000000..d46ba7c63d86a --- /dev/null +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/Fixtures/ClassWithoutIgnoreAnnotation.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\PhpStan\Formatters\Fixtures; + +/** + * Class ClassWithoutIgnoreAnnotation + * phpcs:ignoreFile + */ +class ClassWithoutIgnoreAnnotation +{ + /** + * Test method. + */ + public function getProductList() + { + $this->testMethod('test1', 'test2'); + } + + /** + * @param string $arg1 + * @return string + */ + private function testMethod(string $arg1) + { + return $arg1; + } +} diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php index b12cb1fbfcbd3..afe559fdd6759 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/ClassAnnotationStructureSniffTest.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ declare(strict_types=1); + namespace Magento\Sniffs\Annotation; class ClassAnnotationStructureSniffTest extends \PHPUnit\Framework\TestCase @@ -63,8 +64,8 @@ public function testProcess($fileUnderTest, $expectedReportFile) { $reportFile = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'phpcs_report.txt'; $this->copyFile( - __DIR__ . DIRECTORY_SEPARATOR . '_files'. DIRECTORY_SEPARATOR, - TESTS_TEMP_DIR + __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR, + TESTS_TEMP_DIR . DIRECTORY_SEPARATOR ); $codeSniffer = new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer( 'Magento', @@ -72,11 +73,11 @@ public function testProcess($fileUnderTest, $expectedReportFile) new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer\Wrapper() ); $result = $codeSniffer->run( - [TESTS_TEMP_DIR . $fileUnderTest] + [TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $fileUnderTest] ); $actual = file_get_contents($reportFile); $expected = file_get_contents( - TESTS_TEMP_DIR . $expectedReportFile + TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $expectedReportFile ); unlink($reportFile); $this->assertEquals(2, $result); diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/MethodAnnotationStructureSniffTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/MethodAnnotationStructureSniffTest.php index b56239f4df8a5..d471597565077 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/MethodAnnotationStructureSniffTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/Sniffs/Annotation/MethodAnnotationStructureSniffTest.php @@ -54,7 +54,7 @@ public function testProcess($fileUnderTest, $expectedReportFile) $reportFile = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'phpcs_report.txt'; $this->copyFile( __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR, - TESTS_TEMP_DIR + TESTS_TEMP_DIR . DIRECTORY_SEPARATOR ); $codeSniffer = new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer( 'Magento', @@ -62,11 +62,11 @@ public function testProcess($fileUnderTest, $expectedReportFile) new \Magento\TestFramework\CodingStandard\Tool\CodeSniffer\Wrapper() ); $result = $codeSniffer->run( - [TESTS_TEMP_DIR . $fileUnderTest] + [TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $fileUnderTest] ); $actual = file_get_contents($reportFile); $expected = file_get_contents( - TESTS_TEMP_DIR . $expectedReportFile + TESTS_TEMP_DIR . DIRECTORY_SEPARATOR . $expectedReportFile ); unlink($reportFile); $this->assertEquals(2, $result); diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index a00c09ceadcef..8ccda77a25191 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -13,6 +13,7 @@ use Magento\TestFramework\CodingStandard\Tool\CodeSniffer\Wrapper; use Magento\TestFramework\CodingStandard\Tool\CopyPasteDetector; use Magento\TestFramework\CodingStandard\Tool\PhpCompatibility; +use Magento\TestFramework\CodingStandard\Tool\PhpStan; use PHPMD\TextUI\Command; /** @@ -465,4 +466,39 @@ public function testPhpCompatibility() 'PHP Compatibility detected violation(s):' . PHP_EOL . $report ); } + + /** + * Test code quality using PHPStan + * + * @throws \Exception + */ + public function testPhpStan() + { + $reportFile = self::$reportDir . '/phpstan_report.txt'; + $confFile = __DIR__ . '/_files/phpstan/phpstan.neon'; + + if (!file_exists($reportFile)) { + touch($reportFile); + } + + $fileList = self::getWhitelist(['php']); + $blackList = Files::init()->readLists(__DIR__ . '/_files/phpstan/blacklist/*.txt'); + if ($blackList) { + $blackListPattern = sprintf('#(%s)#i', implode('|', $blackList)); + $fileList = array_filter( + $fileList, + function ($path) use ($blackListPattern) { + return !preg_match($blackListPattern, $path); + } + ); + } + + $phpStan = new PhpStan($confFile, $reportFile); + $exitCode = $phpStan->run($fileList); + $report = file_get_contents($reportFile); + + $errorMessage = empty($report) ? + 'PHPStan command run failed.' : 'PHPStan detected violation(s):' . PHP_EOL . $report; + $this->assertEquals(0, $exitCode, $errorMessage); + } } diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt new file mode 100644 index 0000000000000..34965bda0dba6 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -0,0 +1,14 @@ +# Format: path to directory or path to file +# +# Example: +# app/code/Magento/Catalog +# dev/tests/static/framework/bootstrap.php +lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php +lib/internal/Magento/Framework/Cache/Backend/Eaccelerator.php +dev/tests/integration/framework/deployTestModules.php +dev/tests/integration/testsuite/Magento/Framework/Session/ConfigTest.php +dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php +dev/tests/api-functional/testsuite/Magento/Customer/Api/AddressRepositoryTest.php +dev/tests/api-functional/testsuite/Magento/Framework/Model/Entity/HydratorTest.php +dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php +dev/tests/api-functional/testsuite/Magento/Integration/Model/CustomerTokenServiceTest.php diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon new file mode 100644 index 0000000000000..f4f6ff87f8175 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon @@ -0,0 +1,38 @@ +parameters: + checkExplicitMixedMissingReturn: true + checkPhpDocMissingReturn: true + reportUnmatchedIgnoredErrors: false + excludes_analyse: + - %rootDir%/../../../lib/internal/Magento/Framework/ObjectManager/Test/Unit/* + - %rootDir%/../../../*/_files/* + - %rootDir%/../../../dev/tests/functional/* + - %rootDir%/../../../dev/tests/*/Fixtures/* + - %rootDir%/../../../dev/tests/*/tmp/* + - %rootDir%/../../../dev/tests/*/_generated/* + - %rootDir%/../../../pub/* + autoload_directories: + - %rootDir%/../../../dev/tests/static/framework/tests/unit/testsuite/Magento + - %rootDir%/../../../dev/tests/integration/framework/tests/unit/testsuite/Magento + - %rootDir%/../../../dev/tests/api-functional/_files/Magento + autoload_files: + - %rootDir%/../../../dev/tests/static/framework/autoload.php + - %rootDir%/../../../dev/tests/integration/framework/autoload.php + - %rootDir%/../../../dev/tests/api-functional/framework/autoload.php + - %rootDir%/../../../dev/tests/setup-integration/framework/autoload.php + - %rootDir%/../../../dev/tests/static/framework/Magento/PhpStan/autoload.php + ignoreErrors: + # Ignore PHPStan\Rules\Classes\UnusedConstructorParametersRule + - '#Constructor of class [a-zA-Z0-9\\_]+ has an unused parameter#' + # Ignore setCustomErrorHandler function not found in bootstrap files + - '#Function setCustomErrorHandler not found#' + # Ignore 'return statement is missing' error when 'void' is present in return type list + - '#Method (?:.*?) should return (?:.*?)void(?:.*?) but return statement is missing.#' + +services: + errorFormatter.filtered: + class: Magento\PhpStan\Formatters\FilteredErrorFormatter + arguments: + showTipsOfTheDay: false + checkThisOnly: false + inferPrivatePropertyTypeFromConstructor: true + checkMissingTypehints: %checkMissingTypehints% diff --git a/lib/internal/Magento/Framework/EntityManager/TypeResolver.php b/lib/internal/Magento/Framework/EntityManager/TypeResolver.php index 79cdbae126d7a..1f559c01f11b6 100644 --- a/lib/internal/Magento/Framework/EntityManager/TypeResolver.php +++ b/lib/internal/Magento/Framework/EntityManager/TypeResolver.php @@ -6,7 +6,7 @@ namespace Magento\Framework\EntityManager; /** - * Class TypeResolver + * Resolves types. */ class TypeResolver { @@ -20,7 +20,9 @@ class TypeResolver */ private $typeMapping = [ \Magento\SalesRule\Model\Rule::class => \Magento\SalesRule\Api\Data\RuleInterface::class, + // phpstan:ignore "Class Magento\SalesRule\Model\Rule\Interceptor not found." \Magento\SalesRule\Model\Rule\Interceptor::class => \Magento\SalesRule\Api\Data\RuleInterface::class, + // phpstan:ignore "Class Magento\SalesRule\Model\Rule\Proxy not found." \Magento\SalesRule\Model\Rule\Proxy::class => \Magento\SalesRule\Api\Data\RuleInterface::class ]; @@ -34,6 +36,8 @@ public function __construct(MetadataPool $metadataPool) } /** + * Resolves type. + * * @param object $type * @return string * @throws \Exception diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php b/lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php index 61eb2e62091ea..98bac47e8e1b0 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php @@ -6,8 +6,6 @@ namespace Magento\Framework\Interception\Test\Unit\Config; -use Magento\Framework\Serialize\SerializerInterface; - require_once __DIR__ . '/../Custom/Module/Model/Item.php'; require_once __DIR__ . '/../Custom/Module/Model/Item/Enhanced.php'; require_once __DIR__ . '/../Custom/Module/Model/ItemContainer.php'; @@ -89,48 +87,52 @@ public function testHasPluginsWhenDataIsNotCached($expectedResult, $type, $entit ->will($this->returnValue(null)); $this->omConfigMock->expects($this->any()) ->method('getOriginalInstanceType') - ->will($this->returnValueMap( - [ - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, - ], - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, - ], - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Enhanced::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Enhanced::class, - ], - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Enhanced::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Enhanced::class, - ], - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Proxy::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Proxy::class, - ], + ->will( + $this->returnValueMap( [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemProxy::class, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemProxy::class, - ], - [ - \Magento\Framework\Interception\Custom\Module\Model\Backslash\ItemProxy::class, - \Magento\Framework\Interception\Custom\Module\Model\Backslash\ItemProxy::class - ], - [ - 'virtual_custom_item', - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class - ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class, + ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class, + ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Enhanced::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Enhanced::class, + ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Enhanced::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Enhanced::class, + ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Proxy::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Proxy::class, + ], + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Proxy::class, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Proxy::class, + ], + [ + \Magento\Framework\Interception\Custom\Module\Model\Backslash\Item\Proxy::class, + \Magento\Framework\Interception\Custom\Module\Model\Backslash\Item\Proxy::class + ], + [ + 'virtual_custom_item', + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item::class + ], + ] + ) + ); + $this->definitionMock->expects($this->any())->method('getClasses')->will( + $this->returnValue( + [ + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Proxy::class, + \Magento\Framework\Interception\Custom\Module\Model\Backslash\Item\Proxy::class ] - )); - $this->definitionMock->expects($this->any())->method('getClasses')->will($this->returnValue( - [ - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemProxy::class, - \Magento\Framework\Interception\Custom\Module\Model\Backslash\ItemProxy::class - ] - )); + ) + ); $this->relationsMock->expects($this->any())->method('has')->will($this->returnValue($expectedResult)); $this->relationsMock->expects($this->any())->method('getParents')->will($this->returnValue($entityParents)); @@ -163,7 +165,7 @@ public function testHasPluginsWhenDataIsCached($expectedResult, $type) \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Enhanced::class => true, \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Enhanced::class => true, \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer\Proxy::class => true, - \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemProxy::class => false, + \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Proxy::class => false, 'virtual_custom_item' => true ]; $this->readerMock->expects($this->never())->method('read'); @@ -221,7 +223,7 @@ public function hasPluginsDataProvider() [\Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemContainer::class] ], [ - false, \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\ItemProxy::class, + false, \Magento\Framework\Interception\Test\Unit\Custom\Module\Model\Item\Proxy::class, [] ], [ diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/FactoryGenerator.php b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/FactoryGenerator.php index df5b0b0e0ad0c..1a669ace443ec 100644 --- a/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/FactoryGenerator.php +++ b/lib/internal/Magento/Framework/TestFramework/Unit/Autoloader/FactoryGenerator.php @@ -43,6 +43,9 @@ public function generate($className) */ private function isFactory($className) { + if (!preg_match('/[\\\A-Z]/', substr(ltrim($className), 0, 1))) { + return false; + } $sourceName = rtrim(substr($className, 0, -strlen('Factory')), '\\'); return $sourceName . 'Factory' == $className; } From 40f35ee7ad5b5c3390e9cb297a7871019269c807 Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Mon, 16 Dec 2019 16:11:43 -0600 Subject: [PATCH 2427/2437] Fix static --- .../view/frontend/templates/form.phtml | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/templates/form.phtml b/app/code/Magento/Contact/view/frontend/templates/form.phtml index 053cf419e4225..2982a0a99f74c 100644 --- a/app/code/Magento/Contact/view/frontend/templates/form.phtml +++ b/app/code/Magento/Contact/view/frontend/templates/form.phtml @@ -17,29 +17,55 @@ $viewModel = $block->getViewModel(); data-mage-init='{"validation":{}}'> <fieldset class="fieldset"> <legend class="legend"><span><?= $block->escapeHtml(__('Write Us')) ?></span></legend><br /> - <div class="field note no-label"><?= $block->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?></div> + <div class="field note no-label"> + <?= $block->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?> + </div> <div class="field name required"> <label class="label" for="name"><span><?= $block->escapeHtml(__('Name')) ?></span></label> <div class="control"> - <input name="name" id="name" title="<?= $block->escapeHtmlAttr(__('Name')) ?>" value="<?= $block->escapeHtmlAttr($viewModel->getUserName()) ?>" class="input-text" type="text" data-validate="{required:true}"/> + <input name="name" + id="name" + title="<?= $block->escapeHtmlAttr(__('Name')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserName()) ?>" + class="input-text" + type="text" + data-validate="{required:true}"/> </div> </div> <div class="field email required"> <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input name="email" id="email" title="<?= $block->escapeHtmlAttr(__('Email')) ?>" value="<?= $block->escapeHtmlAttr($viewModel->getUserEmail()) ?>" class="input-text" type="email" data-validate="{required:true, 'validate-email':true}"/> + <input name="email" + id="email" + title="<?= $block->escapeHtmlAttr(__('Email')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserEmail()) ?>" + class="input-text" + type="email" + data-validate="{required:true, 'validate-email':true}"/> </div> </div> <div class="field telephone"> <label class="label" for="telephone"><span><?= $block->escapeHtml(__('Phone Number')) ?></span></label> <div class="control"> - <input name="telephone" id="telephone" title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" value="<?= $block->escapeHtmlAttr($viewModel->getUserTelephone()) ?>" class="input-text" type="text" /> + <input name="telephone" + id="telephone" + title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserTelephone()) ?>" + class="input-text" + type="text" /> </div> </div> <div class="field comment required"> <label class="label" for="comment"><span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span></label> <div class="control"> - <textarea name="comment" id="comment" title="<?= $block->escapeHtmlAttr(__('What’s on your mind?')) ?>" class="input-text" cols="5" rows="3" data-validate="{required:true}"><?= $block->escapeHtml($viewModel->getUserComment()) ?></textarea> + <textarea name="comment" + id="comment" + title="<?= $block->escapeHtmlAttr(__('What’s on your mind?')) ?>" + class="input-text" + cols="5" + rows="3" + data-validate="{required:true}"><?= $block->escapeHtml($viewModel->getUserComment()) ?> + </textarea> </div> </div> <?= $block->getChildHtml('form.additional.info') ?> From a4984838127857816256e4d0a0a4ed82f1d43ccb Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 16 Dec 2019 16:31:33 -0600 Subject: [PATCH 2428/2437] MC-25203: Introduce PHPStan code analysis tool to the static build - Fix test --- .../Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php index dfedd625fdb0e..8512f311f15a2 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/PhpStan/Formatters/FilteredErrorFormatterTest.php @@ -33,7 +33,7 @@ class FilteredErrorFormatterTest extends ErrorFormatterTestCase */ public function testFormatErrors( string $message, - int $exitCode + int $exitCode, array $fileErrors, string $expected ): void { From aa419de0b281718352112a7e6540388efccd395f Mon Sep 17 00:00:00 2001 From: Volodymyr Zaets <vzaets@magento.com> Date: Mon, 16 Dec 2019 16:44:36 -0600 Subject: [PATCH 2429/2437] Fix codestyle --- app/code/Magento/Contact/view/frontend/templates/form.phtml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Contact/view/frontend/templates/form.phtml b/app/code/Magento/Contact/view/frontend/templates/form.phtml index 2982a0a99f74c..3a7c4c8b6d865 100644 --- a/app/code/Magento/Contact/view/frontend/templates/form.phtml +++ b/app/code/Magento/Contact/view/frontend/templates/form.phtml @@ -56,7 +56,9 @@ $viewModel = $block->getViewModel(); </div> </div> <div class="field comment required"> - <label class="label" for="comment"><span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span></label> + <label class="label" for="comment"> + <span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span> + </label> <div class="control"> <textarea name="comment" id="comment" From 9c51c71ad2f686a51026a014a984c2e22a481cf5 Mon Sep 17 00:00:00 2001 From: Viktor Tymchynskyi <vtymchynskyi@magento.com> Date: Mon, 16 Dec 2019 16:53:54 -0600 Subject: [PATCH 2430/2437] MC-25203: Introduce PHPStan code analysis tool to the static build - update composer --- composer.json | 2 +- composer.lock | 450 +++++++++++++++++++++++++------------------------- 2 files changed, 222 insertions(+), 230 deletions(-) diff --git a/composer.json b/composer.json index 59737c298b720..7e01980e32f58 100644 --- a/composer.json +++ b/composer.json @@ -92,7 +92,7 @@ "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", - "phpstan/phpstan": "^0.12.2", + "phpstan/phpstan": "^0.12.3", "phpunit/phpunit": "~6.5.0", "sebastian/phpcpd": "~3.0.0", "squizlabs/php_codesniffer": "~3.4.0" diff --git a/composer.lock b/composer.lock index 8391dd2f48518..a63cdcdf4a1e1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7effdb746287f89357497d7bc2ed1bf4", + "content-hash": "38446350ff1ac4609f77d5d74213880d", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.2.5", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149" + "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/62e8fc2dc550e5d6d8c9360c7721662670f58149", - "reference": "62e8fc2dc550e5d6d8c9360c7721662670f58149", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527", + "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527", "shasum": "" }, "require": { @@ -221,7 +221,7 @@ "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", "psr/log": "^1.0", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0" }, "type": "library", "extra": { @@ -253,7 +253,7 @@ "ssl", "tls" ], - "time": "2019-12-11T14:44:42+00:00" + "time": "2019-08-30T08:44:50+00:00" }, { "name": "composer/composer", @@ -595,16 +595,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.5.0", + "version": "6.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5" + "reference": "0895c932405407fd3a7368b6910c09a24d26db11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", - "reference": "dbc2bc3a293ed6b1ae08a3651e2bfd213d19b6a5", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11", "shasum": "" }, "require": { @@ -619,13 +619,12 @@ "psr/log": "^1.1" }, "suggest": { - "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5-dev" + "dev-master": "6.3-dev" } }, "autoload": { @@ -658,7 +657,7 @@ "rest", "web service" ], - "time": "2019-12-07T18:20:45+00:00" + "time": "2019-10-23T15:58:00+00:00" }, { "name": "guzzlehttp/promises", @@ -1066,16 +1065,16 @@ }, { "name": "magento/zendframework1", - "version": "1.14.3", + "version": "1.14.2", "source": { "type": "git", "url": "https://github.com/magento/zf1.git", - "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5" + "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/zf1/zipball/726855dfb080089dc7bc7b016624129f8e7bc4e5", - "reference": "726855dfb080089dc7bc7b016624129f8e7bc4e5", + "url": "https://api.github.com/repos/magento/zf1/zipball/8221062d42a198e431d183bbe672e5e1a2f98c5f", + "reference": "8221062d42a198e431d183bbe672e5e1a2f98c5f", "shasum": "" }, "require": { @@ -1109,7 +1108,7 @@ "ZF1", "framework" ], - "time": "2019-11-26T15:09:40+00:00" + "time": "2019-07-26T16:43:11+00:00" }, { "name": "monolog/monolog", @@ -2084,16 +2083,16 @@ }, { "name": "symfony/css-selector", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "64acec7e0d67125e9f4656c68d4a38a42ab5a0b7" + "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/64acec7e0d67125e9f4656c68d4a38a42ab5a0b7", - "reference": "64acec7e0d67125e9f4656c68d4a38a42ab5a0b7", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", + "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9", "shasum": "" }, "require": { @@ -2102,7 +2101,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -2133,20 +2132,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-10-12T00:35:04+00:00" + "time": "2019-10-02T08:36:26+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.3.9", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87a1ae7480f2020818013605a65776b9033bcc4f" + "reference": "0df002fd4f500392eabd243c2947061a50937287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a1ae7480f2020818013605a65776b9033bcc4f", - "reference": "87a1ae7480f2020818013605a65776b9033bcc4f", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", + "reference": "0df002fd4f500392eabd243c2947061a50937287", "shasum": "" }, "require": { @@ -2203,7 +2202,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-11-28T13:25:45+00:00" + "time": "2019-11-03T09:04:05+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2265,16 +2264,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "40c2606131d56eff6f193b6e2ceb92414653b591" + "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/40c2606131d56eff6f193b6e2ceb92414653b591", - "reference": "40c2606131d56eff6f193b6e2ceb92414653b591", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9abbb7ef96a51f4d7e69627bc6f63307994e4263", + "reference": "9abbb7ef96a51f4d7e69627bc6f63307994e4263", "shasum": "" }, "require": { @@ -2284,7 +2283,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -2311,20 +2310,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2019-11-26T23:16:41+00:00" + "time": "2019-08-20T14:07:54+00:00" }, { "name": "symfony/finder", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ce8743441da64c41e2a667b8eb66070444ed911e" + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ce8743441da64c41e2a667b8eb66070444ed911e", - "reference": "ce8743441da64c41e2a667b8eb66070444ed911e", + "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", "shasum": "" }, "require": { @@ -2333,7 +2332,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -2360,20 +2359,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-11-17T21:56:56+00:00" + "time": "2019-10-30T12:53:54+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { @@ -2385,7 +2384,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2418,20 +2417,20 @@ "polyfill", "portable" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -2443,7 +2442,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2477,20 +2476,20 @@ "portable", "shim" ], - "time": "2019-11-27T14:18:11+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/process", - "version": "v4.3.9", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "207dab1f17d34ad71ea72e9741ab8049a9d8251b" + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/207dab1f17d34ad71ea72e9741ab8049a9d8251b", - "reference": "207dab1f17d34ad71ea72e9741ab8049a9d8251b", + "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", "shasum": "" }, "require": { @@ -2526,7 +2525,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-11-28T10:05:26+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "tedivm/jshrink", @@ -3530,16 +3529,16 @@ }, { "name": "zendframework/zend-http", - "version": "2.11.1", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7" + "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/76000da8490b8685d63ff6f6ff8eefa459f6e9e7", - "reference": "76000da8490b8685d63ff6f6ff8eefa459f6e9e7", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/4b4983178693a8fdda53b0bbee58552e2d2b1ac0", + "reference": "4b4983178693a8fdda53b0bbee58552e2d2b1ac0", "shasum": "" }, "require": { @@ -3560,8 +3559,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" } }, "autoload": { @@ -3581,7 +3580,7 @@ "zend", "zf" ], - "time": "2019-12-04T23:02:34+00:00" + "time": "2019-02-19T18:58:14+00:00" }, { "name": "zendframework/zend-hydrator", @@ -3645,26 +3644,22 @@ }, { "name": "zendframework/zend-i18n", - "version": "2.10.1", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c" + "reference": "e17a54b3aee333ab156958f570cde630acee8b07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/84038e6a1838b611dcc491b1c40321fa4c3a123c", - "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/e17a54b3aee333ab156958f570cde630acee8b07", + "reference": "e17a54b3aee333ab156958f570cde630acee8b07", "shasum": "" }, "require": { - "ext-intl": "*", "php": "^5.6 || ^7.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, - "conflict": { - "phpspec/prophecy": "<1.9.0" - }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", @@ -3677,6 +3672,7 @@ "zendframework/zend-view": "^2.6.3" }, "suggest": { + "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", "zendframework/zend-cache": "Zend\\Cache component", "zendframework/zend-config": "Zend\\Config component", "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", @@ -3689,8 +3685,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\I18n", @@ -3712,7 +3708,7 @@ "i18n", "zf" ], - "time": "2019-12-12T14:08:22+00:00" + "time": "2019-09-30T12:04:37+00:00" }, { "name": "zendframework/zend-inputfilter", @@ -4798,16 +4794,16 @@ }, { "name": "zendframework/zend-view", - "version": "2.11.4", + "version": "2.11.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d" + "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", - "reference": "a8b1b2d9b52e191539be861a6529f8c8a0c06b9d", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e766457bd6ce13c5354e443bb949511b6904d7f5", + "reference": "e766457bd6ce13c5354e443bb949511b6904d7f5", "shasum": "" }, "require": { @@ -4881,7 +4877,7 @@ "view", "zf" ], - "time": "2019-12-04T08:40:50+00:00" + "time": "2019-10-11T21:10:04+00:00" } ], "packages-dev": [ @@ -5284,16 +5280,16 @@ }, { "name": "codeception/phpunit-wrapper", - "version": "6.7.1", + "version": "6.7.0", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "d3b611635b47a583dfaa1a9e98b98fa476d14025" + "reference": "93f59e028826464eac086052fa226e58967f6907" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/d3b611635b47a583dfaa1a9e98b98fa476d14025", - "reference": "d3b611635b47a583dfaa1a9e98b98fa476d14025", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/93f59e028826464eac086052fa226e58967f6907", + "reference": "93f59e028826464eac086052fa226e58967f6907", "shasum": "" }, "require": { @@ -5326,7 +5322,7 @@ } ], "description": "PHPUnit classes used by Codeception", - "time": "2019-11-23T18:22:38+00:00" + "time": "2019-08-18T15:43:35+00:00" }, { "name": "codeception/stub", @@ -6166,16 +6162,16 @@ }, { "name": "doctrine/cache", - "version": "1.10.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62" + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62", - "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62", + "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", "shasum": "" }, "require": { @@ -6242,9 +6238,10 @@ "memcached", "php", "redis", + "riak", "xcache" ], - "time": "2019-11-29T15:36:20+00:00" + "time": "2019-11-15T14:31:57+00:00" }, { "name": "doctrine/inflector", @@ -6623,16 +6620,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.9.1", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f" + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/fc10d778e4b84d5bd315dad194661e091d307c6f", - "reference": "fc10d778e4b84d5bd315dad194661e091d307c6f", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", "shasum": "" }, "require": { @@ -6645,9 +6642,7 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.9-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -6669,7 +6664,7 @@ "faker", "fixtures" ], - "time": "2019-12-12T13:22:17+00:00" + "time": "2019-11-14T13:13:06+00:00" }, { "name": "grasmash/expander", @@ -7035,16 +7030,16 @@ }, { "name": "league/flysystem", - "version": "1.0.61", + "version": "1.0.57", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9" + "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fb13c01784a6c9f165a351e996871488ca2d8c9", - "reference": "4fb13c01784a6c9f165a351e996871488ca2d8c9", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", + "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a", "shasum": "" }, "require": { @@ -7115,7 +7110,7 @@ "sftp", "storage" ], - "time": "2019-12-08T21:46:50+00:00" + "time": "2019-10-16T21:01:05+00:00" }, { "name": "lusitanian/oauth", @@ -7349,16 +7344,16 @@ }, { "name": "mustache/mustache", - "version": "v2.13.0", + "version": "v2.12.0", "source": { "type": "git", "url": "https://github.com/bobthecow/mustache.php.git", - "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4" + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/e95c5a008c23d3151d59ea72484d4f72049ab7f4", - "reference": "e95c5a008c23d3151d59ea72484d4f72049ab7f4", + "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e", + "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e", "shasum": "" }, "require": { @@ -7391,20 +7386,20 @@ "mustache", "templating" ], - "time": "2019-11-23T21:40:31+00:00" + "time": "2017-07-11T12:54:05+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.9.4", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" + "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", - "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", + "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", "shasum": "" }, "require": { @@ -7439,7 +7434,7 @@ "object", "object graph" ], - "time": "2019-12-15T19:12:40+00:00" + "time": "2019-08-09T12:45:53+00:00" }, { "name": "nikic/php-parser", @@ -8012,34 +8007,33 @@ }, { "name": "phpoption/phpoption", - "version": "1.7.2", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959" + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", - "reference": "77f7c4d2e65413aff5b5a8cc8b3caf7a28d81959", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793", + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0" + "php": ">=5.3.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.3", - "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.7|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.5-dev" } }, "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" + "psr-0": { + "PhpOption\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -8050,10 +8044,6 @@ { "name": "Johannes M. Schmitt", "email": "schmittjoh@gmail.com" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com" } ], "description": "Option Type for PHP", @@ -8063,7 +8053,7 @@ "php", "type" ], - "time": "2019-12-15T19:35:24+00:00" + "time": "2019-11-06T22:27:00+00:00" }, { "name": "phpspec/prophecy", @@ -9356,27 +9346,27 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "e19e465c055137938afd40cfddd687e7511bbbf0" + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e19e465c055137938afd40cfddd687e7511bbbf0", - "reference": "e19e465c055137938afd40cfddd687e7511bbbf0", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/dom-crawler": "^3.4|^4.0|^5.0" + "symfony/dom-crawler": "~3.4|~4.0" }, "require-dev": { - "symfony/css-selector": "^3.4|^4.0|^5.0", - "symfony/http-client": "^4.3|^5.0", - "symfony/mime": "^4.3|^5.0", - "symfony/process": "^3.4|^4.0|^5.0" + "symfony/css-selector": "~3.4|~4.0", + "symfony/http-client": "^4.3", + "symfony/mime": "^4.3", + "symfony/process": "~3.4|~4.0" }, "suggest": { "symfony/process": "" @@ -9384,7 +9374,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -9411,36 +9401,36 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-10-28T20:30:34+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/config", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c" + "reference": "8267214841c44d315a55242ea867684eb43c42ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c", - "reference": "7aa5817f1b7a8ed377752b90fcc47dfb3c67b40c", + "url": "https://api.github.com/repos/symfony/config/zipball/8267214841c44d315a55242ea867684eb43c42ce", + "reference": "8267214841c44d315a55242ea867684eb43c42ce", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/filesystem": "^3.4|^4.0|^5.0", + "symfony/filesystem": "~3.4|~4.0", "symfony/polyfill-ctype": "~1.8" }, "conflict": { "symfony/finder": "<3.4" }, "require-dev": { - "symfony/event-dispatcher": "^3.4|^4.0|^5.0", - "symfony/finder": "^3.4|^4.0|^5.0", - "symfony/messenger": "^4.1|^5.0", - "symfony/service-contracts": "^1.1|^2", - "symfony/yaml": "^3.4|^4.0|^5.0" + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/messenger": "~4.1", + "symfony/yaml": "~3.4|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -9448,7 +9438,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -9475,29 +9465,29 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-12-01T10:50:45+00:00" + "time": "2019-11-08T08:31:27+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ad46a4def1325befab696b49c839dffea3fc92bd" + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ad46a4def1325befab696b49c839dffea3fc92bd", - "reference": "ad46a4def1325befab696b49c839dffea3fc92bd", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/80c6d9e19467dfbba14f830ed478eb592ce51b64", + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64", "shasum": "" }, "require": { "php": "^7.1.3", "psr/container": "^1.0", - "symfony/service-contracts": "^1.1.6|^2" + "symfony/service-contracts": "^1.1.6" }, "conflict": { - "symfony/config": "<4.3|>=5.0", + "symfony/config": "<4.3", "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" @@ -9508,8 +9498,8 @@ }, "require-dev": { "symfony/config": "^4.3", - "symfony/expression-language": "^3.4|^4.0|^5.0", - "symfony/yaml": "^3.4|^4.0|^5.0" + "symfony/expression-language": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" }, "suggest": { "symfony/config": "", @@ -9521,7 +9511,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -9548,20 +9538,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-12-01T10:19:36+00:00" + "time": "2019-11-08T16:22:27+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "36bbcab9369fc2f583220890efd43bf262d563fd" + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/36bbcab9369fc2f583220890efd43bf262d563fd", - "reference": "36bbcab9369fc2f583220890efd43bf262d563fd", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", "shasum": "" }, "require": { @@ -9574,7 +9564,7 @@ }, "require-dev": { "masterminds/html5": "^2.6", - "symfony/css-selector": "^3.4|^4.0|^5.0" + "symfony/css-selector": "~3.4|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -9582,7 +9572,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -9609,7 +9599,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-10-29T11:38:30+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/http-foundation", @@ -9668,16 +9658,16 @@ }, { "name": "symfony/options-resolver", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "2be23e63f33de16b49294ea6581f462932a77e2f" + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2be23e63f33de16b49294ea6581f462932a77e2f", - "reference": "2be23e63f33de16b49294ea6581f462932a77e2f", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", "shasum": "" }, "require": { @@ -9686,7 +9676,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -9718,20 +9708,20 @@ "configuration", "options" ], - "time": "2019-10-28T21:57:16+00:00" + "time": "2019-10-28T20:59:01+00:00" }, { "name": "symfony/polyfill-php54", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "dd1618047426412036e98d159940d58a81fc392c" + "reference": "a043bcced870214922fbb4bf22679d431ec0296a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/dd1618047426412036e98d159940d58a81fc392c", - "reference": "dd1618047426412036e98d159940d58a81fc392c", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/a043bcced870214922fbb4bf22679d431ec0296a", + "reference": "a043bcced870214922fbb4bf22679d431ec0296a", "shasum": "" }, "require": { @@ -9740,7 +9730,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -9776,20 +9766,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php55", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php55.git", - "reference": "b0d838f225725e2951af1aafc784d2e5ea7b656e" + "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b0d838f225725e2951af1aafc784d2e5ea7b656e", - "reference": "b0d838f225725e2951af1aafc784d2e5ea7b656e", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/548bb39407e78e54f785b4e18c7e0d5d9e493265", + "reference": "548bb39407e78e54f785b4e18c7e0d5d9e493265", "shasum": "" }, "require": { @@ -9799,7 +9789,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -9832,20 +9822,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "af23c7bb26a73b850840823662dda371484926c4" + "reference": "54b4c428a0054e254223797d2713c31e08610831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/af23c7bb26a73b850840823662dda371484926c4", - "reference": "af23c7bb26a73b850840823662dda371484926c4", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/54b4c428a0054e254223797d2713c31e08610831", + "reference": "54b4c428a0054e254223797d2713c31e08610831", "shasum": "" }, "require": { @@ -9855,7 +9845,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -9891,20 +9881,20 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.13.1", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038" + "reference": "04ce3335667451138df4307d6a9b61565560199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038", - "reference": "66fea50f6cb37a35eea048d75a7d99a45b586038", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", + "reference": "04ce3335667451138df4307d6a9b61565560199e", "shasum": "" }, "require": { @@ -9913,7 +9903,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -9946,24 +9936,24 @@ "portable", "shim" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.0.1", + "version": "v1.1.8", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "144c5e51266b281231e947b51223ba14acf1a749" + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/144c5e51266b281231e947b51223ba14acf1a749", - "reference": "144c5e51266b281231e947b51223ba14acf1a749", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": "^7.1.3", "psr/container": "^1.0" }, "suggest": { @@ -9972,7 +9962,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -10004,30 +9994,30 @@ "interoperability", "standards" ], - "time": "2019-11-18T17:27:11+00:00" + "time": "2019-10-14T12:27:06+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5745b514fc56ae1907c6b8ed74f94f90f64694e9" + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5745b514fc56ae1907c6b8ed74f94f90f64694e9", - "reference": "5745b514fc56ae1907c6b8ed74f94f90f64694e9", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e96c259de6abcd0cead71f0bf4d730d53ee464d0", + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0", "shasum": "" }, "require": { "php": "^7.1.3", - "symfony/service-contracts": "^1.0|^2" + "symfony/service-contracts": "^1.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -10054,20 +10044,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-11-05T16:11:08+00:00" + "time": "2019-11-05T14:48:09+00:00" }, { "name": "symfony/yaml", - "version": "v4.4.1", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "76de473358fe802578a415d5bb43c296cf09d211" + "reference": "324cf4b19c345465fad14f3602050519e09e361d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/76de473358fe802578a415d5bb43c296cf09d211", - "reference": "76de473358fe802578a415d5bb43c296cf09d211", + "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", + "reference": "324cf4b19c345465fad14f3602050519e09e361d", "shasum": "" }, "require": { @@ -10078,7 +10068,7 @@ "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "^3.4|^4.0|^5.0" + "symfony/console": "~3.4|~4.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -10086,7 +10076,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.3-dev" } }, "autoload": { @@ -10113,7 +10103,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-11-12T14:51:11+00:00" + "time": "2019-10-30T12:58:49+00:00" }, { "name": "theseer/fdomdocument", @@ -10248,29 +10238,31 @@ }, { "name": "webmozart/assert", - "version": "1.6.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" + "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", + "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", + "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", "symfony/polyfill-ctype": "^1.8" }, - "conflict": { - "vimeo/psalm": "<3.6.0" - }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -10292,7 +10284,7 @@ "check", "validate" ], - "time": "2019-11-24T13:36:37+00:00" + "time": "2019-08-24T08:43:50+00:00" }, { "name": "weew/helpers-array", From 2be8374fe0262eccb232ff9614211c126d496a4b Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 17 Dec 2019 07:42:22 +0700 Subject: [PATCH 2431/2437] Refactor code --- .../Unit/Pricing/MsrpPriceCalculatorTest.php | 85 +++++++++---------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php index aac6852b7000c..24e10207ff14c 100644 --- a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php +++ b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php @@ -9,7 +9,7 @@ namespace Magento\Msrp\Test\Unit\Pricing; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\Product\Type as Type; +use Magento\Catalog\Model\Product\Type as ProductType; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedType; use Magento\Msrp\Pricing\MsrpPriceCalculator; @@ -19,6 +19,36 @@ class MsrpPriceCalculatorTest extends TestCase { + /** + * @var MsrpPriceCalculator + */ + private $pricing; + + /** + * @var MsrpGroupedCalculator|MockObject + */ + private $msrpGroupedCalculatorMock; + + /** + * Prepare environment to test + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->msrpGroupedCalculatorMock = $this->createMock(MsrpGroupedCalculator::class); + $this->pricing = $objectManager->getObject( + MsrpPriceCalculator::class, + [ + 'msrpPriceCalculators' => [ + [ + 'productType' => GroupedType::TYPE_CODE, + 'priceCalculator' => $this->msrpGroupedCalculatorMock + ] + ] + ] + ); + } + /** * Test getMrspPriceValue() with the data provider below * @@ -27,17 +57,12 @@ class MsrpPriceCalculatorTest extends TestCase * @param float $expected * @dataProvider getMsrpPriceValueDataProvider */ - public function testGetMsrpPriceValue($msrpPriceCalculators, $productMock, $expected) + public function testGetMsrpPriceValue($msrpPriceCalculatorPrice, $productMock, $expected) { - $objectManager = new ObjectManager($this); - $pricing = $objectManager->getObject( - MsrpPriceCalculator::class, - [ - 'msrpPriceCalculators' => $msrpPriceCalculators - ] - ); + $this->msrpGroupedCalculatorMock->expects($this->any()) + ->method('getMsrpPriceValue')->willReturn($msrpPriceCalculatorPrice); - $this->assertEquals($expected, $pricing->getMsrpPriceValue($productMock)); + $this->assertEquals($expected, $this->pricing->getMsrpPriceValue($productMock)); } /** @@ -48,49 +73,19 @@ public function testGetMsrpPriceValue($msrpPriceCalculators, $productMock, $expe public function getMsrpPriceValueDataProvider() { return [ - 'Get Mrsp Price with grouped product and price calculator is also grouped product type' => [ - [ - [ - 'productType' => GroupedType::TYPE_CODE, - 'priceCalculator' => $this->createPriceCalculatorMock( - MsrpGroupedCalculator::class, - 23.50 - ) - ] - ], + 'Get Mrsp Price with product and msrp calculator and the same product type' => [ + 23.50, $this->createProductMock(GroupedType::TYPE_CODE, 0), 23.50 ], - 'Get Mrsp Price with simple product and price calculator is grouped product type' => [ - [ - [ - 'productType' => GroupedType::TYPE_CODE, - 'priceCalculator' => $this->createPriceCalculatorMock( - MsrpGroupedCalculator::class, - 0 - ) - ] - ], - $this->createProductMock(Type::TYPE_SIMPLE, 24.88), + 'Get Mrsp Price with product and msrp calculator and the different product type' => [ + 24.88, + $this->createProductMock(ProductType::TYPE_SIMPLE, 24.88), 24.88 ] ]; } - /** - * Create Price Calculator Mock - * - * @param string $class - * @param float $msrpPriceValue - * @return MockObject - */ - private function createPriceCalculatorMock($class, $msrpPriceValue) - { - $priceCalculatorMock = $this->createMock($class); - $priceCalculatorMock->expects($this->any())->method('getMsrpPriceValue')->willReturn($msrpPriceValue); - return $priceCalculatorMock; - } - /** * Create Product Mock * From 9b6d9d0436d5dd03960e08de1748eec76dfe07c8 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 17 Dec 2019 10:14:33 +0700 Subject: [PATCH 2432/2437] [Directory] Cover action directory/json/countryRegion by Integration Test --- .../Adminhtml/Json/CountryRegionTest.php | 50 +++++++++++++++++++ .../_files/example_region_in_country.php | 37 ++++++++++++++ .../example_region_in_country_rollback.php | 26 ++++++++++ 3 files changed, 113 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php create mode 100644 dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php b/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php new file mode 100644 index 0000000000000..8d3b12aa34f1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Controller\Adminhtml\Json; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * @magentoAppArea adminhtml + */ +class CountryRegionTest extends AbstractBackendController +{ + /** + * Test Execute without param + */ + public function testExecuteWithNoCountryParam() + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue([]); + $this->dispatch('backend/directory/json/countryRegion'); + + $actual = $this->getResponse()->getBody(); + + $this->assertEquals('[]', $actual); + } + + /** + * Test Execute with region in the fixture + * + * @magentoDataFixture Magento/Directory/_files/example_region_in_country.php + */ + public function testExecute() + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue([ + 'parent' => 'WW' + ]); + $this->dispatch('backend/directory/json/countryRegion'); + + $actual = $this->getResponse()->getBody(); + + $this->assertContains('Example Region 1', $actual); + $this->assertContains('Example Region 2', $actual); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php new file mode 100644 index 0000000000000..680c0079a795c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Directory\Model\Region as RegionModel; +use Magento\Directory\Model\ResourceModel\Region as RegionResource; + +$objectManager = Bootstrap::getObjectManager(); + +$regionData = [ + [ + 'country_id' => 'WW', + 'code' => 'ER1', + 'default_name' => 'Example Region 1' + ], + [ + 'country_id' => 'WW', + 'code' => 'ER2', + 'default_name' => 'Example Region 2' + ] +]; + +/** @var RegionModel $region */ +$region = $objectManager->create(RegionModel::class); +/** @var RegionResource $regionResource */ +$regionResource = $objectManager->get(RegionResource::class); + +foreach ($regionData as $data) { + /** @var RegionModel $region */ + $region = $objectManager->create(RegionModel::class); + $regionResource->save($region); +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php new file mode 100644 index 0000000000000..4f78873aa380f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Directory\Model\Region as RegionModel; +use Magento\Directory\Model\ResourceModel\Region as RegionResource; +use Magento\Directory\Model\ResourceModel\Region\Collection as RegionResourceCollection; + +$objectManager = Bootstrap::getObjectManager(); +$regionCode = ['ER1', 'ER2']; + +/** @var RegionResource $regionResource */ +$regionResource = $objectManager->get(RegionResource::class); + +$regionCollection = $objectManager->create(RegionResourceCollection::class) + ->addFieldToFilter('code', ['in' => $regionCode]); + +/** @var RegionModel $region */ +foreach ($regionCollection as $region) { + $regionResource->delete($region); +} From e3ce8c9f5bf263b12614af349a38be5e7168020b Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 17 Dec 2019 11:20:18 +0700 Subject: [PATCH 2433/2437] [Directory] Cover action directory/json/countryRegion by Integration Test --- .../Magento/Directory/_files/example_region_in_country.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php index 680c0079a795c..ba33b09a1f948 100644 --- a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php @@ -33,5 +33,6 @@ foreach ($regionData as $data) { /** @var RegionModel $region */ $region = $objectManager->create(RegionModel::class); + $region->setData($data); $regionResource->save($region); } From 8451e0f6d4754d29c9458ab21ec60aafa674f8b9 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <51681547+engcom-Golf@users.noreply.github.com> Date: Tue, 17 Dec 2019 10:33:14 +0200 Subject: [PATCH 2434/2437] Fix static test --- .../Magento/Framework/View/Element/Template/File/Resolver.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php b/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php index b13caa55d3174..cd7ba1dfaecda 100644 --- a/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php +++ b/lib/internal/Magento/Framework/View/Element/Template/File/Resolver.php @@ -9,9 +9,7 @@ use Magento\Framework\Serialize\Serializer\Json; /** - * Class Resolver - * - * @package Magento\Framework\View\Element\Template\File + * Resolver, returns template file name by template. */ class Resolver { From 2e7f74bc940752ff4faa682300cdac667b872695 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <51681547+engcom-Golf@users.noreply.github.com> Date: Tue, 17 Dec 2019 10:34:01 +0200 Subject: [PATCH 2435/2437] fix static test --- lib/internal/Magento/Framework/View/Element/RendererList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/View/Element/RendererList.php b/lib/internal/Magento/Framework/View/Element/RendererList.php index 96707c74dd253..eba78867d7725 100644 --- a/lib/internal/Magento/Framework/View/Element/RendererList.php +++ b/lib/internal/Magento/Framework/View/Element/RendererList.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\Element; /** - * Renderer List + * Get renderer by code * * @api */ From 78dc52b87d6a4517799bc0f359b8016e0d40b21a Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <51681547+engcom-Golf@users.noreply.github.com> Date: Tue, 17 Dec 2019 10:34:58 +0200 Subject: [PATCH 2436/2437] fix static test fix --- .../Framework/View/Element/ExceptionHandlerBlockFactory.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php b/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php index b1c74777fb102..b13ac2531ebc8 100644 --- a/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php +++ b/lib/internal/Magento/Framework/View/Element/ExceptionHandlerBlockFactory.php @@ -6,9 +6,7 @@ namespace Magento\Framework\View\Element; /** - * Class ExceptionHandlerBlockFactory - * - * @package Magento\Framework\View\Element + * Factory for BlockInterface */ class ExceptionHandlerBlockFactory { From b0d2f6535389c43e17d71380febd55bd12567887 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Tue, 17 Dec 2019 14:33:41 +0200 Subject: [PATCH 2437/2437] Cover changes with jasmine test --- .../base/js/lib/ko/bind/color-picker.test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/color-picker.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/color-picker.test.js index 9b756a2f2a49a..afbd7e8e94c2d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/color-picker.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/lib/ko/bind/color-picker.test.js @@ -85,5 +85,28 @@ define([ expect($.fn.init).toHaveBeenCalledWith($input, undefined); }); + + it('Verify config value is empty when reset colorpicker intput', function () { + var value = { + configStuffInHere: true, + value: jasmine.createSpy().and.returnValue(undefined) + }, + valueAccessor = jasmine.createSpy().and.returnValue(value), + viewModel = { + disabled: jasmine.createSpy().and.returnValue(false) + }; + + $.fn.spectrum = jasmine.createSpy(); + $input = jasmine.createSpy(); + + ko.bindingHandlers.colorPicker.update($input, valueAccessor, null, viewModel); + expect($.fn.spectrum).toHaveBeenCalledTimes(1); + expect(valueAccessor().value).toHaveBeenCalledTimes(4); + + value.value = jasmine.createSpy().and.returnValue(''); + ko.bindingHandlers.colorPicker.update($input, valueAccessor, null, viewModel); + expect($.fn.spectrum).toHaveBeenCalledTimes(3); + expect(valueAccessor().value).toHaveBeenCalledTimes(5); + }); }); });