+ getHtmlId() . rand(); ?>
+
= $block->getFormHtml() ?>
= /* @noEscape */ $secureRenderer->renderStyleAsTag(
'display:none',
- 'div#new_video_' . /* @noEscape */ $block->getNewVideoBlockName()
+ 'div#' . $videoBlockId
) ?>
= $block->getChildHtml('new-video') ?>
diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php
index 3a81341e2b02a..b0aef022dcd25 100644
--- a/app/code/Magento/Quote/Model/QuoteManagement.php
+++ b/app/code/Magento/Quote/Model/QuoteManagement.php
@@ -24,7 +24,7 @@
use Magento\Store\Model\StoreManagerInterface;
/**
- * Class QuoteManagement
+ * Class for managing quote
*
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -250,6 +250,7 @@ public function createEmptyCart()
$quote->setBillingAddress($this->quoteAddressFactory->create());
$quote->setShippingAddress($this->quoteAddressFactory->create());
+ $quote->setCustomerIsGuest(1);
try {
$quote->getShippingAddress()->setCollectShippingRates(true);
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 c19dbc2c429ae..d938ad7d638f1 100644
--- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php
+++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php
@@ -5,7 +5,16 @@
*/
namespace Magento\Quote\Observer\Frontend\Quote\Address;
+use Magento\Customer\Api\AddressRepositoryInterface;
+use Magento\Customer\Api\Data\CustomerInterfaceFactory;
+use Magento\Customer\Api\GroupManagementInterface;
+use Magento\Customer\Helper\Address;
+use Magento\Customer\Model\Session;
+use Magento\Customer\Model\Vat;
+use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
+use Magento\Quote\Api\Data\ShippingAssignmentInterface;
+use Magento\Quote\Model\Quote;
/**
* Handle customer VAT number on collect_totals_before event of quote address.
@@ -15,22 +24,22 @@
class CollectTotalsObserver implements ObserverInterface
{
/**
- * @var \Magento\Customer\Api\AddressRepositoryInterface
+ * @var AddressRepositoryInterface
*/
private $addressRepository;
/**
- * @var \Magento\Customer\Model\Session
+ * @var Session
*/
private $customerSession;
/**
- * @var \Magento\Customer\Helper\Address
+ * @var Address
*/
protected $customerAddressHelper;
/**
- * @var \Magento\Customer\Model\Vat
+ * @var Vat
*/
protected $customerVat;
@@ -40,36 +49,36 @@ class CollectTotalsObserver implements ObserverInterface
protected $vatValidator;
/**
- * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory
+ * @var CustomerInterfaceFactory
*/
protected $customerDataFactory;
/**
* Group Management
*
- * @var \Magento\Customer\Api\GroupManagementInterface
+ * @var GroupManagementInterface
*/
protected $groupManagement;
/**
* Initialize dependencies.
*
- * @param \Magento\Customer\Helper\Address $customerAddressHelper
- * @param \Magento\Customer\Model\Vat $customerVat
+ * @param Address $customerAddressHelper
+ * @param Vat $customerVat
* @param VatValidator $vatValidator
- * @param \Magento\Customer\Api\Data\CustomerInterfaceFactory $customerDataFactory
- * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement
- * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository
- * @param \Magento\Customer\Model\Session $customerSession
+ * @param CustomerInterfaceFactory $customerDataFactory
+ * @param GroupManagementInterface $groupManagement
+ * @param AddressRepositoryInterface $addressRepository
+ * @param Session $customerSession
*/
public function __construct(
- \Magento\Customer\Helper\Address $customerAddressHelper,
- \Magento\Customer\Model\Vat $customerVat,
+ Address $customerAddressHelper,
+ Vat $customerVat,
VatValidator $vatValidator,
- \Magento\Customer\Api\Data\CustomerInterfaceFactory $customerDataFactory,
- \Magento\Customer\Api\GroupManagementInterface $groupManagement,
- \Magento\Customer\Api\AddressRepositoryInterface $addressRepository,
- \Magento\Customer\Model\Session $customerSession
+ CustomerInterfaceFactory $customerDataFactory,
+ GroupManagementInterface $groupManagement,
+ AddressRepositoryInterface $addressRepository,
+ Session $customerSession
) {
$this->customerVat = $customerVat;
$this->customerAddressHelper = $customerAddressHelper;
@@ -83,25 +92,23 @@ public function __construct(
/**
* Handle customer VAT number if needed on collect_totals_before event of quote address
*
- * @param \Magento\Framework\Event\Observer $observer
+ * @param Observer $observer
* @return void
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
- public function execute(\Magento\Framework\Event\Observer $observer)
+ public function execute(Observer $observer)
{
- /** @var \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment */
+ /** @var ShippingAssignmentInterface $shippingAssignment */
$shippingAssignment = $observer->getShippingAssignment();
- /** @var \Magento\Quote\Model\Quote $quote */
+ /** @var Quote $quote */
$quote = $observer->getQuote();
- /** @var \Magento\Quote\Model\Quote\Address $address */
+ /** @var Quote\Address $address */
$address = $shippingAssignment->getShipping()->getAddress();
$customer = $quote->getCustomer();
$storeId = $customer->getStoreId();
- if ($customer->getDisableAutoGroupChange()
- || false == $this->vatValidator->isEnabled($address, $storeId)
- ) {
+ if ($customer->getDisableAutoGroupChange() || !$this->vatValidator->isEnabled($address, $storeId)) {
return;
}
$customerCountryCode = $address->getCountryId();
@@ -135,6 +142,7 @@ public function execute(\Magento\Framework\Event\Observer $observer)
$quote->setCustomerGroupId($groupId);
$this->customerSession->setCustomerGroupId($groupId);
$customer->setGroupId($groupId);
+ $customer->setEmail($customer->getEmail() ?: $quote->getCustomerEmail());
$quote->setCustomer($customer);
}
}
diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml
index 3e79eb044b5cb..6e9e8e800e076 100644
--- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml
+++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml
@@ -47,7 +47,7 @@
-
+
diff --git a/app/code/Magento/Sales/Model/OrderRepository.php b/app/code/Magento/Sales/Model/OrderRepository.php
index ecd4e7babb1e3..a600d1489857c 100644
--- a/app/code/Magento/Sales/Model/OrderRepository.php
+++ b/app/code/Magento/Sales/Model/OrderRepository.php
@@ -157,6 +157,9 @@ public function get($id)
private function setOrderTaxDetails(OrderInterface $order)
{
$extensionAttributes = $order->getExtensionAttributes();
+ if ($extensionAttributes === null) {
+ $extensionAttributes = $this->orderExtensionFactory->create();
+ }
$orderTaxDetails = $this->orderTaxManagement->getOrderTaxDetails($order->getEntityId());
$appliedTaxes = $orderTaxDetails->getAppliedTaxes();
@@ -180,6 +183,9 @@ private function setOrderTaxDetails(OrderInterface $order)
private function setPaymentAdditionalInfo(OrderInterface $order): void
{
$extensionAttributes = $order->getExtensionAttributes();
+ if ($extensionAttributes === null) {
+ $extensionAttributes = $this->orderExtensionFactory->create();
+ }
$paymentAdditionalInformation = $order->getPayment()->getAdditionalInformation();
$objects = [];
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickInvoiceButtonOrderViewActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickInvoiceButtonOrderViewActionGroup.xml
new file mode 100644
index 0000000000000..4617437595c9c
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminClickInvoiceButtonOrderViewActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Click 'Invoice' button on the order view page.
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml
index b301864212c8b..877f7946d7609 100644
--- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml
@@ -16,5 +16,6 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminFreePaymentMethodExistsOnCreateOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminFreePaymentMethodExistsOnCreateOrderPageActionGroup.xml
new file mode 100644
index 0000000000000..75146e891a02a
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminFreePaymentMethodExistsOnCreateOrderPageActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Checks the free payment on the Admin Create Order page.
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontClickRefundTabCustomerOrderViewActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontClickRefundTabCustomerOrderViewActionGroup.xml
new file mode 100644
index 0000000000000..fda22395f359c
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StorefrontClickRefundTabCustomerOrderViewActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Click "Refund" tab for customer order view.
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
index a478d79d8553f..72fe45465c67b 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml
@@ -15,7 +15,7 @@
-
+
@@ -28,5 +28,6 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml
index 701b7ebe4a958..24e6c5eddf7db 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml
@@ -19,23 +19,22 @@
+
-
+
+
-
-
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml
index c4656e394d349..6ed8510db777c 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml
@@ -129,13 +129,11 @@
-
-
+
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml
index eb3d4ad991915..68301187d3d31 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml
@@ -123,13 +123,11 @@
-
-
+
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml
index 4383820ba6bee..a1027a9987b1f 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml
@@ -116,13 +116,11 @@
-
-
+
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml
index 56399401b205e..141fa2a9e5d06 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml
@@ -115,13 +115,11 @@
-
-
+
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
index 30f8386b3bb91..91a8f95880fbc 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml
@@ -68,8 +68,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml
index 188002f3938a6..b337af3753db3 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml
@@ -65,7 +65,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml
index 055388570479e..6eb4195524224 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml
@@ -65,7 +65,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml
index 9b2ded574b737..41964cbf605da 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml
@@ -52,7 +52,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml
index 1a89c5656b2f1..2a4ad174abae0 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml
@@ -62,7 +62,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml
index 2252c0a813bcf..27ed62fee35e2 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml
@@ -49,7 +49,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml
index d4004c519b7df..163da4917b50a 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml
@@ -48,7 +48,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml
index 28ce9661e259b..d2ded1cc73d2b 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml
@@ -54,7 +54,6 @@
-
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml
index 885f019b864de..a5d210a9765ad 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml
@@ -99,8 +99,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml
index d0c1b51008684..de6e7ff22b7af 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml
@@ -84,7 +84,7 @@
-
+
@@ -133,8 +133,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml
index e5cfd5dc4afa0..57e42e9b190e3 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml
@@ -84,7 +84,7 @@
-
+
@@ -100,8 +100,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml
index 12b956be22cfb..10c6be60f5ba1 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml
@@ -78,7 +78,7 @@
-
+
@@ -124,8 +124,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml
index 780bffd359ba7..cd36547a877ec 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml
@@ -93,7 +93,7 @@
-
+
@@ -107,8 +107,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
index e41e3acbae380..20dcb262b5831 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml
@@ -102,8 +102,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
index e99ffa95495ff..6b6b0b2ef4a16 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml
@@ -213,8 +213,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
index cba141e2ab271..9fba25688702d 100644
--- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml
@@ -199,8 +199,7 @@
-
-
+
diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php
index 84c66d12c10d8..2e51bd8d75b89 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php
@@ -18,12 +18,13 @@
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderPaymentInterface;
use Magento\Sales\Api\Data\OrderSearchResultInterfaceFactory as SearchResultFactory;
+use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Shipping;
use Magento\Sales\Model\Order\ShippingAssignment;
use Magento\Sales\Model\Order\ShippingAssignmentBuilder;
use Magento\Sales\Model\OrderRepository;
use Magento\Sales\Model\ResourceModel\Metadata;
-use Magento\Sales\Model\ResourceModel\Order;
+use Magento\Sales\Model\ResourceModel\Order as OrderResource;
use Magento\Sales\Model\ResourceModel\Order\Collection;
use Magento\Tax\Api\Data\OrderTaxDetailsInterface;
use Magento\Tax\Api\OrderTaxManagementInterface;
@@ -70,6 +71,11 @@ class OrderRepositoryTest extends TestCase
*/
private $paymentAdditionalInfoFactory;
+ /**
+ * @var OrderExtensionFactory|\MockObject
+ */
+ private $orderExtensionFactoryMock;
+
/**
* Setup the test
*
@@ -88,7 +94,7 @@ protected function setUp(): void
$this->collectionProcessor = $this->createMock(
CollectionProcessorInterface::class
);
- $orderExtensionFactoryMock = $this->getMockBuilder(OrderExtensionFactory::class)
+ $this->orderExtensionFactoryMock = $this->getMockBuilder(OrderExtensionFactory::class)
->disableOriginalConstructor()
->getMock();
$this->orderTaxManagementMock = $this->getMockBuilder(OrderTaxManagementInterface::class)
@@ -103,7 +109,7 @@ protected function setUp(): void
'metadata' => $this->metadata,
'searchResultFactory' => $this->searchResultFactory,
'collectionProcessor' => $this->collectionProcessor,
- 'orderExtensionFactory' => $orderExtensionFactoryMock,
+ 'orderExtensionFactory' => $this->orderExtensionFactoryMock,
'orderTaxManagement' => $this->orderTaxManagementMock,
'paymentAdditionalInfoFactory' => $this->paymentAdditionalInfoFactory
]
@@ -178,10 +184,10 @@ public function testGetList()
*/
public function testSave()
{
- $mapperMock = $this->getMockBuilder(Order::class)
+ $mapperMock = $this->getMockBuilder(OrderResource::class)
->disableOriginalConstructor()
->getMock();
- $orderEntity = $this->createMock(\Magento\Sales\Model\Order::class);
+ $orderEntity = $this->createMock(Order::class);
$extensionAttributes = $this->getMockBuilder(OrderExtension::class)
->addMethods(['getShippingAssignments'])
->getMock();
@@ -207,4 +213,57 @@ public function testSave()
$orderEntity->expects($this->any())->method('getEntityId')->willReturn(1);
$this->orderRepository->save($orderEntity);
}
+
+ /**
+ * Test for method get.
+ *
+ * @return void
+ */
+ public function testGet()
+ {
+ $orderId = 1;
+ $appliedTaxes = 'applied_taxes';
+ $items = 'items';
+ $paymentInfo = [];
+
+ $orderEntity = $this->createMock(Order::class);
+ $paymentMock = $this->getMockBuilder(OrderPaymentInterface::class)
+ ->disableOriginalConstructor()->getMockForAbstractClass();
+ $paymentMock->expects($this->once())->method('getAdditionalInformation')->willReturn($paymentInfo);
+ $orderExtension = $this->getMockBuilder(OrderExtension::class)
+ ->setMethods(
+ [
+ 'getShippingAssignments',
+ 'setAppliedTaxes',
+ 'setConvertingFromQuote',
+ 'setItemAppliedTaxes',
+ 'setPaymentAdditionalInfo'
+ ]
+ )
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $orderExtension->expects($this->once())->method('getShippingAssignments')->willReturn(true);
+ $orderExtension->expects($this->once())->method('setAppliedTaxes')->with($appliedTaxes);
+ $orderExtension->expects($this->once())->method('setConvertingFromQuote')->with(true);
+ $orderExtension->expects($this->once())->method('setItemAppliedTaxes')->with($items);
+ $orderExtension->expects($this->once())->method('setPaymentAdditionalInfo')->with($paymentInfo);
+ $this->orderExtensionFactoryMock->expects($this->once())->method('create')->willReturn($orderExtension);
+ $orderEntity->expects($this->once())->method('load')->with($orderId)->willReturn($orderEntity);
+ $orderEntity->expects($this->exactly(2))->method('getEntityId')->willReturn($orderId);
+ $orderEntity->expects($this->once())->method('getPayment')->willReturn($paymentMock);
+ $orderEntity->expects($this->exactly(2))->method('setExtensionAttributes')->with($orderExtension);
+ $orderEntity->expects($this->exactly(3))
+ ->method('getExtensionAttributes')
+ ->willReturnOnConsecutiveCalls(null, $orderExtension, $orderExtension);
+ $this->metadata->expects($this->once())->method('getNewInstance')->willReturn($orderEntity);
+ $orderTaxDetailsMock = $this->getMockBuilder(OrderTaxDetailsInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setAppliedTaxes'])->getMockForAbstractClass();
+ $orderTaxDetailsMock->expects($this->once())->method('getAppliedTaxes')->willReturn($appliedTaxes);
+ $orderTaxDetailsMock->expects($this->once())->method('getItems')->willReturn($items);
+ $this->orderTaxManagementMock->expects($this->atLeastOnce())->method('getOrderTaxDetails')
+ ->willReturn($orderTaxDetailsMock);
+
+ $this->orderRepository->get($orderId);
+ }
}
diff --git a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_grid.xml b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_grid.xml
index e1f047b372c95..f6b1240402477 100644
--- a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_grid.xml
+++ b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_grid.xml
@@ -58,7 +58,7 @@
-
+
diff --git a/app/code/Magento/Sales/view/adminhtml/web/js/grid/tree-massactions.js b/app/code/Magento/Sales/view/adminhtml/web/js/grid/tree-massactions.js
new file mode 100644
index 0000000000000..a2783222afc28
--- /dev/null
+++ b/app/code/Magento/Sales/view/adminhtml/web/js/grid/tree-massactions.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'underscore',
+ 'mageUtils',
+ 'Magento_Ui/js/grid/tree-massactions'
+], function (_, utils, Massactions) {
+ 'use strict';
+
+ return Massactions.extend({
+ /**
+ * Overwrite Default action callback.
+ * Sends selections data with ids
+ * via POST request.
+ *
+ * @param {Object} action - Action data.
+ * @param {Object} data - Selections data.
+ */
+ defaultCallback: function (action, data) {
+ var itemsType = 'selected',
+ selections = {};
+
+ selections[itemsType] = data[itemsType];
+ _.extend(selections, data.params || {});
+ utils.submit({
+ url: action.url,
+ data: selections
+ });
+ }
+ });
+});
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 bbdd6f8fe8437..a329524c58d41 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
@@ -481,6 +481,13 @@ define([
},
switchPaymentMethod: function(method){
+ if (this.paymentMethod !== method) {
+ jQuery('#edit_form')
+ .off('submitOrder')
+ .on('submitOrder', function(){
+ jQuery(this).trigger('realOrder');
+ });
+ }
jQuery('#edit_form').trigger('changePaymentMethod', [method]);
this.setPaymentMethod(method);
var data = {};
@@ -1308,11 +1315,16 @@ define([
},
submit: function () {
- var $editForm = jQuery('#edit_form');
+ var $editForm = jQuery('#edit_form'),
+ beforeSubmitOrderEvent;
if ($editForm.valid()) {
$editForm.trigger('processStart');
- $editForm.trigger('submitOrder');
+ beforeSubmitOrderEvent = jQuery.Event('beforeSubmitOrder');
+ $editForm.trigger(beforeSubmitOrderEvent);
+ if (beforeSubmitOrderEvent.result !== false) {
+ $editForm.trigger('submitOrder');
+ }
}
},
diff --git a/app/code/Magento/Sitemap/Model/Observer.php b/app/code/Magento/Sitemap/Model/Observer.php
index ce74d738c4bc3..4333c71c7497f 100644
--- a/app/code/Magento/Sitemap/Model/Observer.php
+++ b/app/code/Magento/Sitemap/Model/Observer.php
@@ -3,11 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sitemap\Model;
-use Magento\Sitemap\Model\EmailNotification as SitemapEmail;
+use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Sitemap\Model\EmailNotification as SitemapEmail;
use Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory;
+use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
/**
@@ -61,20 +65,28 @@ class Observer
*/
private $emailNotification;
+ /**
+ * @var Emulation
+ */
+ private $appEmulation;
+
/**
* Observer constructor.
* @param ScopeConfigInterface $scopeConfig
* @param CollectionFactory $collectionFactory
* @param EmailNotification $emailNotification
+ * @param Emulation $appEmulation
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
CollectionFactory $collectionFactory,
- SitemapEmail $emailNotification
+ SitemapEmail $emailNotification,
+ Emulation $appEmulation
) {
$this->scopeConfig = $scopeConfig;
$this->collectionFactory = $collectionFactory;
$this->emailNotification = $emailNotification;
+ $this->appEmulation = $appEmulation;
}
/**
@@ -105,9 +117,16 @@ public function scheduledGenerateSitemaps()
foreach ($collection as $sitemap) {
/* @var $sitemap \Magento\Sitemap\Model\Sitemap */
try {
+ $this->appEmulation->startEnvironmentEmulation(
+ $sitemap->getStoreId(),
+ Area::AREA_FRONTEND,
+ true
+ );
$sitemap->generateXml();
} catch (\Exception $e) {
$errors[] = $e->getMessage();
+ } finally {
+ $this->appEmulation->stopEnvironmentEmulation();
}
}
if ($errors && $recipient) {
diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
index 23ebe4f85f79e..70520862faee1 100644
--- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
+++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
@@ -142,4 +142,39 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail()
$this->observer->scheduledGenerateSitemaps();
}
+
+ /**
+ * Test if cron scheduled XML sitemap generation will start and stop the store environment emulation
+ *
+ * @throws \Exception
+ */
+ public function testCronGenerateSitemapEnvironmentEmulation()
+ {
+ $storeId = 1;
+
+ $this->scopeConfigMock->expects($this->once())->method('isSetFlag')->willReturn(true);
+
+ $this->collectionFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->sitemapCollectionMock);
+
+ $this->sitemapCollectionMock->expects($this->any())
+ ->method('getIterator')
+ ->willReturn(new \ArrayIterator([$this->sitemapMock]));
+
+ $this->sitemapMock->expects($this->at(0))
+ ->method('getStoreId')
+ ->willReturn($storeId);
+
+ $this->sitemapMock->expects($this->once())
+ ->method('generateXml');
+
+ $this->appEmulationMock->expects($this->once())
+ ->method('startEnvironmentEmulation');
+
+ $this->appEmulationMock->expects($this->once())
+ ->method('stopEnvironmentEmulation');
+
+ $this->observer->scheduledGenerateSitemaps();
+ }
}
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml
new file mode 100644
index 0000000000000..1aab6ea2c4eec
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Delete Tax Rate.
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml
new file mode 100644
index 0000000000000..2b110e969b113
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Filter Tax Rates by tax rate code.
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
index be7185a5166a2..ceb04a9c42e66 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
index 89cfdd0eb9943..7497b950a8c0e 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
@@ -23,12 +23,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
index a3386cada436f..da89ad3e9337c 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
index 6ceeae953139c..da30157d94182 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
index 4f9e876fed696..93e0f6514e83b 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
index eb774297b8322..751989497d10e 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
@@ -25,14 +25,13 @@
-
-
-
-
+
+
+
+
+
+
-
-
-
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml
new file mode 100644
index 0000000000000..9db9ea7becfc8
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ Massive action for all rows on Admin Grid page.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml
new file mode 100644
index 0000000000000..6440cc01bcafe
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Shows new column on Admin Grid page.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml
new file mode 100644
index 0000000000000..bbfb7e46d89ec
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ Click on select all option on the grid
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
index fcee31c0bd80c..c5b000259e265 100644
--- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
+++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
index ba0f4d25c25a4..828bbccee0478 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
@@ -52,6 +52,7 @@ define([
listens: {
'${ $.provider }:params.filters': 'onFilter',
+ '${ $.provider }:params.search': 'onSearch',
selected: 'onSelectedChange',
rows: 'onRowsChange'
},
@@ -235,7 +236,7 @@ define([
* @returns {Multiselect} Chainable.
*/
togglePage: function () {
- return this.isPageSelected() ? this.deselectPage() : this.selectPage();
+ return this.isPageSelected() && !this.excluded().length ? this.deselectPage() : this.selectPage();
},
/**
@@ -496,6 +497,13 @@ define([
if (!this.preserveSelectionsOnFilter) {
this.deselectAll();
}
+ },
+
+ /**
+ * Is invoked when search is applied or removed
+ */
+ onSearch: function () {
+ this.onFilter();
}
});
});
diff --git a/app/code/Magento/User/i18n/en_US.csv b/app/code/Magento/User/i18n/en_US.csv
index 064b6428387fe..cd550015401d0 100644
--- a/app/code/Magento/User/i18n/en_US.csv
+++ b/app/code/Magento/User/i18n/en_US.csv
@@ -106,8 +106,8 @@ username,username
Custom,Custom
All,All
Resources,Resources
-"Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?"
-"Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?"
+"Warning!
This action will remove this user from already assigned role.
Are you sure?","Warning!
This action will remove this user from already assigned role.
Are you sure?"
+"Warning!
This action will remove those users from already assigned roles.
Are you sure?","Warning!
This action will remove those users from already assigned roles.
Are you sure?"
"Password Reset Confirmation for %name","Password Reset Confirmation for %name"
"%name,","%name,"
"There was recently a request to change the password for your account.","There was recently a request to change the password for your account."
diff --git a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
index 2042479832898..b0107a53593d3 100644
--- a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
+++ b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
@@ -51,8 +51,8 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
if (checked) {
confirm({
- content: "{$myBlock->escapeJs(__('Warning!\r\nThis action will remove this user from already ' .
- 'assigned role\r\nAre you sure?'))}",
+ content: "{$myBlock->escapeJs(__('Warning!
This action will remove this user from already ' .
+ 'assigned role.
Are you sure?'))}",
actions: {
confirm: function () {
checkbox[0].checked = false;
@@ -102,7 +102,7 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
allCheckbox.checked = true;
confirm({
content: "{$myBlock->escapeJs(
- __('Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?')
+ __('Warning!
This action will remove those users from already assigned roles.
Are you sure?')
)}",
actions: {
confirm: function () {
diff --git a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
index 71a866f945693..7455c26334c02 100644
--- a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
+++ b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
@@ -45,7 +45,7 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
var checked = isInput ? checkbox[0].checked : !checkbox[0].checked;
if (checked && warning && radioBoxes.size() > 0) {
if ( !confirm("{$myBlock->escapeJs(
- __('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?')
+ __('Warning!
This action will remove this user from already assigned role.
Are you sure?')
)}") ) {
checkbox[0].checked = false;
for(i in radioBoxes) {
diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
index 7d30d958b5228..ce1a1cdc2942d 100644
--- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
+++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
@@ -7,7 +7,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
-use Magento\CatalogInventory\Model\Stock;
+use Magento\CatalogInventory\Model\ResourceModel\StockStatusFilterInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Sales\Model\ConfigInterface;
@@ -162,6 +162,17 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
* @var CollectionBuilderInterface
*/
private $productCollectionBuilder;
+ /**
+ * @var StockStatusFilterInterface
+ */
+ private $stockStatusFilter;
+
+ /**
+ * Whether product table is joined in select
+ *
+ * @var bool
+ */
+ private $isProductTableJoined = false;
/**
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
@@ -181,10 +192,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
* @param \Magento\Catalog\Model\Entity\AttributeFactory $catalogAttrFactory
* @param \Magento\Wishlist\Model\ResourceModel\Item $resource
* @param \Magento\Framework\App\State $appState
- * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
* @param TableMaintainer|null $tableMaintainer
* @param ConfigInterface|null $salesConfig
* @param CollectionBuilderInterface|null $productCollectionBuilder
+ * @param StockStatusFilterInterface|null $stockStatusFilter
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -208,7 +220,8 @@ public function __construct(
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
TableMaintainer $tableMaintainer = null,
ConfigInterface $salesConfig = null,
- ?CollectionBuilderInterface $productCollectionBuilder = null
+ ?CollectionBuilderInterface $productCollectionBuilder = null,
+ ?StockStatusFilterInterface $stockStatusFilter = null
) {
$this->stockConfiguration = $stockConfiguration;
$this->_adminhtmlSales = $adminhtmlSales;
@@ -227,6 +240,8 @@ public function __construct(
$this->salesConfig = $salesConfig ?: ObjectManager::getInstance()->get(ConfigInterface::class);
$this->productCollectionBuilder = $productCollectionBuilder
?: ObjectManager::getInstance()->get(CollectionBuilderInterface::class);
+ $this->stockStatusFilter = $stockStatusFilter
+ ?: ObjectManager::getInstance()->get(StockStatusFilterInterface::class);
}
/**
@@ -368,15 +383,8 @@ protected function _renderFiltersBefore()
$connection = $this->getConnection();
if ($this->_productInStock && !$this->stockConfiguration->isShowOutOfStock()) {
- $inStockConditions = [
- "stockItem.product_id = {$mainTableName}.product_id",
- $connection->quoteInto('stockItem.stock_status = ?', Stock::STOCK_IN_STOCK),
- ];
- $this->getSelect()->join(
- ['stockItem' => $this->getTable('cataloginventory_stock_status')],
- join(' AND ', $inStockConditions),
- []
- );
+ $this->joinProductTable();
+ $this->stockStatusFilter->execute($this->getSelect(), 'product_entity', 'stockItem');
}
if ($this->_productVisible) {
@@ -587,12 +595,9 @@ protected function _joinProductNameTable()
$entityMetadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
$linkField = $entityMetadata->getLinkField();
+ $this->joinProductTable();
$this->getSelect()->join(
- ['product_entity' => $this->getTable('catalog_product_entity')],
- 'product_entity.entity_id = main_table.product_id',
- []
- )->join(
['product_name_table' => $attribute->getBackendTable()],
'product_name_table.' . $linkField . ' = product_entity.' . $linkField .
' AND product_name_table.store_id = ' .
@@ -677,4 +682,21 @@ protected function _afterLoadData()
return $this;
}
+
+ /**
+ * Join product table to select if not already joined
+ *
+ * @return void
+ */
+ private function joinProductTable(): void
+ {
+ if (!$this->isProductTableJoined) {
+ $this->getSelect()->join(
+ ['product_entity' => $this->getTable('catalog_product_entity')],
+ 'product_entity.entity_id = main_table.product_id',
+ []
+ );
+ $this->isProductTableJoined = true;
+ }
+ }
}
diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php
index 437b3c757f9cf..f544dd374d734 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist.php
@@ -12,9 +12,8 @@
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductFactory;
-use Magento\CatalogInventory\Api\Data\StockItemInterface;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
-use Magento\CatalogInventory\Model\Configuration;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
@@ -27,7 +26,6 @@
use Magento\Framework\Registry;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\Stdlib\DateTime;
-use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Wishlist\Helper\Data;
@@ -150,14 +148,9 @@ class Wishlist extends AbstractModel implements IdentityInterface
private $serializer;
/**
- * @var ScopeConfigInterface
+ * @var StockConfigurationInterface
*/
- private $scopeConfig;
-
- /**
- * @var StockRegistryInterface|null
- */
- private $stockRegistry;
+ private $stockConfiguration;
/**
* Constructor
@@ -181,8 +174,9 @@ class Wishlist extends AbstractModel implements IdentityInterface
* @param Json|null $serializer
* @param StockRegistryInterface|null $stockRegistry
* @param ScopeConfigInterface|null $scopeConfig
- *
+ * @param StockConfigurationInterface|null $stockConfiguration
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
Context $context,
@@ -203,7 +197,8 @@ public function __construct(
array $data = [],
Json $serializer = null,
StockRegistryInterface $stockRegistry = null,
- ScopeConfigInterface $scopeConfig = null
+ ScopeConfigInterface $scopeConfig = null,
+ ?StockConfigurationInterface $stockConfiguration = null
) {
$this->_useCurrentWebsite = $useCurrentWebsite;
$this->_catalogProduct = $catalogProduct;
@@ -218,8 +213,8 @@ public function __construct(
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->productRepository = $productRepository;
- $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
- $this->stockRegistry = $stockRegistry ?: ObjectManager::getInstance()->get(StockRegistryInterface::class);
+ $this->stockConfiguration = $stockConfiguration
+ ?: ObjectManager::getInstance()->get(StockConfigurationInterface::class);
}
/**
@@ -467,7 +462,7 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
throw new LocalizedException(__('Cannot specify product.'));
}
- if ($this->isInStock($productId)) {
+ if (!$this->stockConfiguration->isShowOutOfStock($storeId) && !$product->getIsSalable()) {
throw new LocalizedException(__('Cannot add product without stock to wishlist.'));
}
@@ -672,25 +667,6 @@ public function isSalable()
return false;
}
- /**
- * Retrieve if product has stock or config is set for showing out of stock products
- *
- * @param int $productId
- *
- * @return bool
- */
- private function isInStock($productId)
- {
- /** @var StockItemInterface $stockItem */
- $stockItem = $this->stockRegistry->getStockItem($productId);
- $showOutOfStock = $this->scopeConfig->isSetFlag(
- Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
- ScopeInterface::SCOPE_STORE
- );
- $isInStock = $stockItem ? $stockItem->getIsInStock() : false;
- return !$isInStock && !$showOutOfStock;
- }
-
/**
* Check customer is owner this wishlist
*
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
index e09491813877b..369f77e527287 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
@@ -13,6 +13,7 @@
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Type\AbstractType;
use Magento\Catalog\Model\ProductFactory;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Stock\Item as StockItem;
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
@@ -132,6 +133,10 @@ class WishlistTest extends TestCase
* @var StockRegistryInterface|MockObject
*/
private $stockRegistry;
+ /**
+ * @var StockConfigurationInterface|MockObject
+ */
+ private $stockConfiguration;
protected function setUp(): void
{
@@ -194,6 +199,8 @@ protected function setUp(): void
->method('getEventDispatcher')
->willReturn($this->eventDispatcher);
+ $this->stockConfiguration = $this->createMock(StockConfigurationInterface::class);
+
$this->wishlist = new Wishlist(
$context,
$this->registry,
@@ -213,7 +220,8 @@ protected function setUp(): void
[],
$this->serializer,
$this->stockRegistry,
- $this->scopeConfig
+ $this->scopeConfig,
+ $this->stockConfiguration
);
}
@@ -300,6 +308,7 @@ public function testUpdateItem($itemId, $buyRequest, $param): void
$newProduct->expects($this->once())
->method('getTypeInstance')
->willReturn($instanceType);
+ $newProduct->expects($this->any())->method('getIsSalable')->willReturn(true);
$item = $this->getMockBuilder(Item::class)
->disableOriginalConstructor()
@@ -388,8 +397,19 @@ public function updateItemDataProvider(): array
];
}
- public function testAddNewItem()
+ /**
+ * @param bool $getIsSalable
+ * @param bool $isShowOutOfStock
+ * @param string $throwException
+ *
+ * @dataProvider addNewItemDataProvider
+ */
+ public function testAddNewItem(bool $getIsSalable, bool $isShowOutOfStock, string $throwException): void
{
+ if ($throwException) {
+ $this->expectExceptionMessage($throwException);
+ }
+ $this->stockConfiguration->method('isShowOutOfStock')->willReturn($isShowOutOfStock);
$productId = 1;
$storeId = 1;
$buyRequest = json_encode(
@@ -407,34 +427,31 @@ public function testAddNewItem()
$instanceType = $this->getMockBuilder(AbstractType::class)
->disableOriginalConstructor()
->getMock();
- $instanceType->expects($this->once())
- ->method('processConfiguration')
+ $instanceType->method('processConfiguration')
->willReturn('product');
$productMock = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
- ->setMethods(['getId', 'hasWishlistStoreId', 'getStoreId', 'getTypeInstance'])
+ ->setMethods(['getId', 'hasWishlistStoreId', 'getStoreId', 'getTypeInstance', 'getIsSalable'])
->getMock();
- $productMock->expects($this->once())
- ->method('getId')
+ $productMock->method('getId')
->willReturn($productId);
- $productMock->expects($this->once())
- ->method('hasWishlistStoreId')
+ $productMock->method('hasWishlistStoreId')
->willReturn(false);
- $productMock->expects($this->once())
- ->method('getStoreId')
+ $productMock->method('getStoreId')
->willReturn($storeId);
- $productMock->expects($this->once())
- ->method('getTypeInstance')
+ $productMock->method('getTypeInstance')
->willReturn($instanceType);
+ $productMock->expects($this->any())
+ ->method('getIsSalable')
+ ->willReturn($getIsSalable);
$this->productRepository->expects($this->once())
->method('getById')
->with($productId, false, $storeId)
->willReturn($productMock);
- $this->serializer->expects($this->once())
- ->method('unserialize')
+ $this->serializer->method('unserialize')
->willReturnCallback(
function ($value) {
return json_decode($value, true);
@@ -453,4 +470,17 @@ function ($value) {
$this->assertEquals($result, $this->wishlist->addNewItem($productMock, $buyRequest));
}
+
+ /**
+ * @return array[]
+ */
+ public function addNewItemDataProvider(): array
+ {
+ return [
+ [false, false, 'Cannot add product without stock to wishlist'],
+ [false, true, ''],
+ [true, false, ''],
+ [true, true, ''],
+ ];
+ }
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
index be183fe93815a..67012e75bf272 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
@@ -39,6 +39,11 @@ class CreateEmptyCartTest extends GraphQlAbstract
*/
private $quoteIdMaskFactory;
+ /**
+ * @var string
+ */
+ private $maskedQuoteId;
+
protected function setUp(): void
{
$objectManager = Bootstrap::getObjectManager();
@@ -61,6 +66,7 @@ public function testCreateEmptyCart()
self::assertNotNull($guestCart->getId());
self::assertNull($guestCart->getCustomer()->getId());
self::assertEquals('default', $guestCart->getStore()->getCode());
+ self::assertEquals('1', $guestCart->getCustomerIsGuest());
}
/**
@@ -81,6 +87,7 @@ public function testCreateEmptyCartWithNotDefaultStore()
self::assertNotNull($guestCart->getId());
self::assertNull($guestCart->getCustomer()->getId());
self::assertSame('fixture_second_store', $guestCart->getStore()->getCode());
+ self::assertEquals('1', $guestCart->getCustomerIsGuest());
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
index f9d235493297f..2659f14c07c7a 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
@@ -20,6 +20,7 @@
use Magento\Framework\ObjectManagerInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
/**
@@ -79,6 +80,15 @@ class UpdateHandlerTest extends \PHPUnit\Framework\TestCase
*/
private $mediaAttributeId;
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+ /**
+ * @var int
+ */
+ private $currentStoreId;
+
/**
* @inheritdoc
*/
@@ -93,6 +103,8 @@ protected function setUp(): void
$this->productResource = $this->objectManager->create(ProductResource::class);
$this->mediaAttributeId = (int)$this->productResource->getAttribute('media_gallery')->getAttributeId();
$this->config = $this->objectManager->get(Config::class);
+ $this->storeManager = $this->objectManager->create(StoreManagerInterface::class);
+ $this->currentStoreId = $this->storeManager->getStore()->getId();
$this->mediaDirectory = $this->objectManager->get(Filesystem::class)
->getDirectoryWrite(DirectoryList::MEDIA);
$this->mediaDirectory->writeFile($this->fileName, 'Test');
@@ -274,7 +286,7 @@ public function testExecuteWithImageToDelete(): void
$this->updateHandler->execute($product);
$productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId);
$this->assertCount(0, $productImages);
- $this->assertFileNotExists(
+ $this->assertFileDoesNotExist(
$this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image)
);
$defaultImages = $this->productResource->getAttributeRawValue(
@@ -344,6 +356,7 @@ public function testExecuteWithTwoImagesOnStoreView(): void
*/
protected function tearDown(): void
{
+ $this->storeManager->setCurrentStore($this->currentStoreId);
parent::tearDown();
$this->mediaDirectory->getDriver()->deleteFile($this->mediaDirectory->getAbsolutePath($this->fileName));
$this->galleryResource->getConnection()
@@ -377,4 +390,91 @@ private function updateProductGalleryImages(ProductInterface $product, array $im
$product->setData('store_id', Store::DEFAULT_STORE_ID);
$product->setData('media_gallery', ['images' => ['image' => array_merge($image, $imageData)]]);
}
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDbIsolation disabled
+ * @return void
+ */
+ public function testDeleteWithMultiWebsites(): void
+ {
+ $defaultWebsiteId = (int) $this->storeManager->getWebsite('base')->getId();
+ $secondWebsiteId = (int) $this->storeManager->getWebsite('test')->getId();
+ $defaultStoreId = (int) $this->storeManager->getStore('default')->getId();
+ $secondStoreId = (int) $this->storeManager->getStore('fixture_second_store')->getId();
+ $imageRoles = ['image', 'small_image', 'thumbnail'];
+ $globalScopeId = Store::DEFAULT_STORE_ID;
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that product has images
+ $this->assertNotEmpty($product->getMediaGalleryEntries());
+ $image = $product->getImage();
+ $path = $this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image);
+ $this->assertFileExists($path);
+ // Assign product to default and second website and save changes
+ $product->setWebsiteIds([$defaultWebsiteId, $secondWebsiteId]);
+ $this->productRepository->save($product);
+ // Assert that product image has roles in global scope only
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertArrayNotHasKey($secondStoreId, $imageRolesPerStore);
+ // Assign roles to product image on second store and save changes
+ $this->storeManager->setCurrentStore($secondStoreId);
+ $product = $this->getProduct($secondStoreId);
+ $product->addData(array_fill_keys($imageRoles, $image));
+ $this->productRepository->save($product);
+ // Assert that roles are assigned to product image for second store
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['thumbnail']);
+ // Delete existing images and save changes
+ $this->storeManager->setCurrentStore($globalScopeId);
+ $product = $this->getProduct($globalScopeId);
+ $product->setMediaGalleryEntries([]);
+ $this->productRepository->save($product);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that image was not deleted as it has roles in second store
+ $this->assertNotEmpty($product->getMediaGalleryEntries());
+ $this->assertFileExists($path);
+ // Unlink second website, delete existing images and save changes
+ $product->setWebsiteIds([$defaultWebsiteId]);
+ $product->setMediaGalleryEntries([]);
+ $this->productRepository->save($product);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that image was deleted and product has no images
+ $this->assertEmpty($product->getMediaGalleryEntries());
+ $this->assertFileDoesNotExist($path);
+ // Load image roles
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ // Assert that image roles are reset on global scope and removed on second store
+ // as the product is no longer assigned to second website
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertArrayNotHasKey($secondStoreId, $imageRolesPerStore);
+ }
+
+ /**
+ * @param Product $product
+ * @return array
+ */
+ private function getProductStoreImageRoles(Product $product): array
+ {
+ $imageRolesPerStore = [];
+ $stores = array_keys($this->storeManager->getStores(true));
+ foreach ($this->galleryResource->getProductImages($product, $stores) as $role) {
+ $imageRolesPerStore[$role['store_id']][$role['attribute_code']] = $role['filepath'];
+ }
+ return $imageRolesPerStore;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
index 8908561702dd0..f1d1352bcb05b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
@@ -33,6 +33,12 @@
*/
class ProductRepositoryTest extends TestCase
{
+ private const STUB_STORE_ID = 1;
+ private const STUB_STORE_ID_GLOBAL = 0;
+ private const STUB_PRODUCT_NAME = 'Simple Product';
+ private const STUB_UPDATED_PRODUCT_NAME = 'updated';
+ private const STUB_PRODUCT_SKU = 'simple';
+
/**
* @var ObjectManagerInterface
*/
@@ -273,4 +279,55 @@ private function assertProductNotExist(string $sku): void
));
$this->productRepository->get($sku);
}
+
+ /**
+ * Tests product repository update
+ *
+ * @dataProvider productUpdateDataProvider
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @param int $storeId
+ * @param int $checkStoreId
+ * @param string $expectedNameStore
+ * @param string $expectedNameCheckedStore
+ */
+ public function testProductUpdate(
+ int $storeId,
+ int $checkStoreId,
+ string $expectedNameStore,
+ string $expectedNameCheckedStore
+ ): void {
+ $sku = self::STUB_PRODUCT_SKU;
+
+ $product = $this->productRepository->get($sku, false, $storeId);
+ $product->setName(self::STUB_UPDATED_PRODUCT_NAME);
+ $this->productRepository->save($product);
+ $productNameStoreId = $this->productRepository->get($sku, false, $storeId)->getName();
+ $productNameCheckedStoreId = $this->productRepository->get($sku, false, $checkStoreId)->getName();
+
+ $this->assertEquals($expectedNameStore, $productNameStoreId);
+ $this->assertEquals($expectedNameCheckedStore, $productNameCheckedStoreId);
+ }
+
+ /**
+ * Product update data provider
+ *
+ * @return array
+ */
+ public function productUpdateDataProvider(): array
+ {
+ return [
+ 'Updating for global store' => [
+ self::STUB_STORE_ID_GLOBAL,
+ self::STUB_STORE_ID,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ ],
+ 'Updating for store' => [
+ self::STUB_STORE_ID,
+ self::STUB_STORE_ID_GLOBAL,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ self::STUB_PRODUCT_NAME,
+ ],
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
index 32968572b4ac8..44d900a8f2aca 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
@@ -199,6 +199,11 @@ public function testLoadCustomerQuoteCustomerWithoutQuote(): void
$this->quote->getCustomerEmail(),
'Precondition failed: Customer data must not be set to quote'
);
+ self::assertEquals(
+ '1',
+ $this->quote->getCustomerIsGuest(),
+ 'Precondition failed: Customer must be as guest in quote'
+ );
$customer = $this->customerRepository->getById(1);
$this->customerSession->setCustomerDataObject($customer);
$this->quote = $this->checkoutSession->getQuote();
@@ -244,6 +249,17 @@ public function testGetQuoteWithProductWithTierPrice(): void
$this->assertEquals($tierPriceValue, $quoteProduct->getTierPrice(1));
}
+ /**
+ * Test covers case when quote is not yet initialized and customer is guest
+ *
+ * Expected result - quote object should be loaded with customer as guest
+ */
+ public function testGetQuoteNotInitializedGuest()
+ {
+ $quote = $this->checkoutSession->getQuote();
+ self::assertEquals('1', $quote->getCustomerIsGuest());
+ }
+
/**
* @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
* @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
@@ -288,5 +304,10 @@ private function validateCustomerDataInQuote(CartInterface $quote): void
$quote->getCustomerFirstname(),
'Customer first name was not set to Quote correctly.'
);
+ self::assertEquals(
+ '0',
+ $quote->getCustomerIsGuest(),
+ 'Customer should not be as guest in Quote.'
+ );
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
index a68a546c20bc6..cb96ca2a14cac 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
@@ -6,7 +6,13 @@
*/
namespace Magento\Cms\Model\Wysiwyg\Images;
+use Magento\Cms\Model\Wysiwyg\Images\Storage\Collection;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\DataObject;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Filesystem\Driver\File;
+use Magento\Framework\Filesystem\DriverInterface;
+use Magento\TestFramework\Helper\Bootstrap;
/**
* Test methods of class Storage
@@ -29,22 +35,27 @@ class StorageTest extends \PHPUnit\Framework\TestCase
private $objectManager;
/**
- * @var \Magento\Framework\Filesystem
+ * @var Filesystem
*/
private $filesystem;
/**
- * @var \Magento\Cms\Model\Wysiwyg\Images\Storage
+ * @var Storage
*/
private $storage;
+ /**
+ * @var DriverInterface
+ */
+ private $driver;
+
/**
* @inheritdoc
*/
// phpcs:disable
public static function setUpBeforeClass(): void
{
- self::$_baseDir = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ self::$_baseDir = Bootstrap::getObjectManager()->get(
\Magento\Cms\Helper\Wysiwyg\Images::class
)->getCurrentPath() . 'MagentoCmsModelWysiwygImagesStorageTest';
if (!file_exists(self::$_baseDir)) {
@@ -60,8 +71,8 @@ public static function setUpBeforeClass(): void
// phpcs:ignore
public static function tearDownAfterClass(): void
{
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Framework\Filesystem\Driver\File::class
+ Bootstrap::getObjectManager()->create(
+ File::class
)->deleteDirectory(
self::$_baseDir
);
@@ -72,9 +83,10 @@ public static function tearDownAfterClass(): void
*/
protected function setUp(): void
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class);
- $this->storage = $this->objectManager->create(\Magento\Cms\Model\Wysiwyg\Images\Storage::class);
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->filesystem = $this->objectManager->get(Filesystem::class);
+ $this->storage = $this->objectManager->create(Storage::class);
+ $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class);
}
/**
@@ -83,16 +95,31 @@ protected function setUp(): void
*/
public function testGetFilesCollection(): void
{
- \Magento\TestFramework\Helper\Bootstrap::getInstance()
+ Bootstrap::getInstance()
->loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE);
- $collection = $this->storage->getFilesCollection(self::$_baseDir, 'media');
- $this->assertInstanceOf(\Magento\Cms\Model\Wysiwyg\Images\Storage\Collection::class, $collection);
+ $fileName = 'magento_image.jpg';
+ $imagePath = realpath(__DIR__ . '/../../../../Catalog/_files/' . $fileName);
+ $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
+ $modifiableFilePath = $mediaDirectory->getAbsolutePath('MagentoCmsModelWysiwygImagesStorageTest/' . $fileName);
+ $this->driver->copy(
+ $imagePath,
+ $modifiableFilePath
+ );
+ $this->storage->resizeFile($modifiableFilePath);
+ $collection = $this->storage->getFilesCollection(self::$_baseDir, 'image');
+ $this->assertInstanceOf(Collection::class, $collection);
foreach ($collection as $item) {
- $this->assertInstanceOf(\Magento\Framework\DataObject::class, $item);
- $this->assertStringEndsWith('/1.swf', $item->getUrl());
- $this->assertStringMatchesFormat(
- 'http://%s/static/%s/adminhtml/%s/%s/Magento_Cms/images/placeholder_thumbnail.jpg',
- $item->getThumbUrl()
+ $this->assertInstanceOf(DataObject::class, $item);
+ $this->assertStringEndsWith('/' . $fileName, $item->getUrl());
+ $this->assertEquals(
+ '/pub/media/.thumbsMagentoCmsModelWysiwygImagesStorageTest/magento_image.jpg',
+ parse_url($item->getThumbUrl(), PHP_URL_PATH),
+ "Check if Thumbnail URL is equal to the generated URL"
+ );
+ $this->assertEquals(
+ 'image/jpeg',
+ $item->getMimeType(),
+ "Check if Mime Type is equal to the image in the file system"
);
return;
}
@@ -121,7 +148,7 @@ public function testDeleteDirectory(): void
$this->storage->createDirectory($dir, $path);
$this->assertFileExists($fullPath);
$this->storage->deleteDirectory($fullPath);
- $this->assertFileNotExists($fullPath);
+ $this->assertFileDoesNotExist($fullPath);
}
/**
@@ -142,7 +169,7 @@ public function testDeleteDirectoryWithExcludedDirPath(): void
public function testUploadFile(): void
{
$fileName = 'magento_small_image.jpg';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../../Catalog/_files');
@@ -172,7 +199,7 @@ public function testUploadFileWithExcludedDirPath(): void
);
$fileName = 'magento_small_image.jpg';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../../Catalog/_files');
@@ -204,7 +231,7 @@ public function testUploadFileWithWrongExtension(string $fileName, string $fileT
$this->expectException(\Magento\Framework\Exception\LocalizedException::class);
$this->expectExceptionMessage('File validation failed.');
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../_files');
@@ -251,7 +278,7 @@ public function testUploadFileWithWrongFile(): void
$this->expectExceptionMessage('File validation failed.');
$fileName = 'file.gif';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$file = fopen($filePath, "wb");
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
index 3a6052da3964f..cd206ec8ec273 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
@@ -184,7 +184,10 @@ private function prepareConfigurableProduct(string $sku, string $storeCode): voi
{
$product = $this->productRepository->get($sku, false, null, true);
$productToUpdate = $product->getTypeInstance()->getUsedProductCollection($product)
- ->setPageSize(1)->getFirstItem();
+ ->addStoreFilter($storeCode)
+ ->setPageSize(1)
+ ->getFirstItem();
+
$this->assertNotEmpty($productToUpdate->getData(), 'Configurable product does not have a child');
$this->executeInStoreContext->execute($storeCode, [$this, 'setProductDisabled'], $productToUpdate);
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
index 61c2bf7b5fa72..f6e6261c75662 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
@@ -44,7 +44,6 @@
$product = $objectManager->create(Product::class);
$productId = array_shift($productIds);
$product->setTypeId(Type::TYPE_SIMPLE)
- ->setId($productId)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Option' . $option->getLabel())
@@ -84,7 +83,6 @@
$product->setExtensionAttributes($extensionConfigurableAttributes);
$product->setTypeId(Configurable::TYPE_CODE)
- ->setId(1)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Product')
@@ -110,7 +108,6 @@
$product = $objectManager->create(Product::class);
$productId = array_shift($productIds);
$product->setTypeId(Type::TYPE_SIMPLE)
- ->setId($productId)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Option' . $option->getLabel())
@@ -155,7 +152,6 @@
$product->setExtensionAttributes($extensionConfigurableAttributes);
$product->setTypeId(Configurable::TYPE_CODE)
- ->setId(11)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Product 12345')
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
index 072c0cd8f9118..1f0dee32ce4a2 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
@@ -14,11 +14,11 @@
$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
/** @var ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
-$product = $productRepository->getById(10);
+$product = $productRepository->get('simple_10');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 1, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
$productRepository->save($product);
-$product = $productRepository->getById(20);
+$product = $productRepository->get('simple_20');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 0, 'is_qty_decimal' => 0, 'is_in_stock' => 0]);
$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
index 8d4ebc40128d1..aad47165a470a 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
@@ -3,20 +3,33 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Framework\Filter\Template\Tokenizer;
-class ParameterTest extends \PHPUnit\Framework\TestCase
+use Magento\Catalog\Block\Product\Widget\NewWidget;
+use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Test for \Magento\Framework\Filter\Template\Tokenizer\Parameter.
+ */
+class ParameterTest extends TestCase
{
/**
+ * Test for getValue
+ *
+ * @dataProvider getValueDataProvider
+ *
* @param string $string
* @param array $values
- * @dataProvider getValueDataProvider
+ * @return void
*/
- public function testGetValue($string, $values)
+ public function testGetValue($string, $values): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var \Magento\Framework\Filter\Template\Tokenizer\Parameter $parameter */
- $parameter = $objectManager->create(\Magento\Framework\Filter\Template\Tokenizer\Parameter::class);
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var Parameter $parameter */
+ $parameter = $objectManager->create(Parameter::class);
$parameter->setString($string);
foreach ($values as $value) {
@@ -25,30 +38,36 @@ public function testGetValue($string, $values)
}
/**
+ * Test for tokenize
+ *
* @dataProvider tokenizeDataProvider
+ *
* @param string $string
* @param array $params
+ * @return void
*/
- public function testTokenize($string, $params)
+ public function testTokenize(string $string, array $params): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var \Magento\Framework\Filter\Template\Tokenizer\Parameter $parameter */
- $parameter = $objectManager->create(\Magento\Framework\Filter\Template\Tokenizer\Parameter::class);
+ $objectManager = Bootstrap::getObjectManager();
+ $parameter = $objectManager->create(Parameter::class);
$parameter->setString($string);
+
$this->assertEquals($params, $parameter->tokenize());
}
/**
+ * DataProvider for testTokenize
+ *
* @return array
*/
- public function tokenizeDataProvider()
+ public function tokenizeDataProvider(): array
{
return [
[
' type="Magento\\Catalog\\Block\\Product\\Widget\\NewWidget" display_type="all_products"'
. ' products_count="10" template="product/widget/new/content/new_grid.phtml"',
[
- 'type' => \Magento\Catalog\Block\Product\Widget\NewWidget::class,
+ 'type' => NewWidget::class,
'display_type' => 'all_products',
'products_count' => 10,
'template' => 'product/widget/new/content/new_grid.phtml'
@@ -58,12 +77,24 @@ public function tokenizeDataProvider()
' type="Magento\Catalog\Block\Product\Widget\NewWidget" display_type="all_products"'
. ' products_count="10" template="product/widget/new/content/new_grid.phtml"',
[
- 'type' => \Magento\Catalog\Block\Product\Widget\NewWidget::class,
+ 'type' => NewWidget::class,
'display_type' => 'all_products',
'products_count' => 10,
'template' => 'product/widget/new/content/new_grid.phtml'
]
- ]
+ ],
+ [
+ sprintf(
+ 'type="%s" display_type="all_products" products_count="1" template="content/new_grid.phtml"',
+ NewWidget::class
+ ),
+ [
+ 'type' => NewWidget::class,
+ 'display_type' => 'all_products',
+ 'products_count' => 1,
+ 'template' => 'content/new_grid.phtml'
+ ],
+ ],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
index 31b0ac84266e7..3b70659f80a42 100644
--- a/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
+++ b/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
@@ -24,7 +24,7 @@
$objectManager = Bootstrap::getObjectManager();
/** @var ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
-$product = $productRepository->getById(10);
+$product = $productRepository->get('simple_10');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 4, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
index c1ac7cf1ef723..13fcd53d78186 100644
--- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
+++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
@@ -17,11 +17,13 @@
use Magento\Quote\Model\Quote\Address;
use Magento\Quote\Model\ResourceModel\Quote\Collection;
use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class CheckoutTest extends \PHPUnit\Framework\TestCase
+class CheckoutTest extends TestCase
{
/**
* @var ObjectManagerInterface
@@ -29,22 +31,22 @@ class CheckoutTest extends \PHPUnit\Framework\TestCase
private $objectManager;
/**
- * @var Info|\PHPUnit\Framework\MockObject\MockObject
+ * @var Info|MockObject
*/
private $paypalInfo;
/**
- * @var Config|\PHPUnit\Framework\MockObject\MockObject
+ * @var Config|MockObject
*/
private $paypalConfig;
/**
- * @var Factory|\PHPUnit\Framework\MockObject\MockObject
+ * @var Factory|MockObject
*/
private $apiTypeFactory;
/**
- * @var Nvp|\PHPUnit\Framework\MockObject\MockObject
+ * @var Nvp|MockObject
*/
private $api;
@@ -215,6 +217,28 @@ public function testPlaceGuestQuote()
$this->assertNotEmpty($order->getShippingAddress());
}
+ /**
+ * Place the order as guest when `Automatic Assignment to Customer Group` is enabled.
+ *
+ * @magentoDataFixture Magento/Paypal/_files/quote_express.php
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ *
+ * @return void
+ */
+ public function testPlaceGuestQuoteAutomaticAssignmentEnabled(): void
+ {
+ $quote = $this->getFixtureQuote();
+ $quote->setCheckoutMethod(Onepage::METHOD_GUEST);
+ $quote->getShippingAddress()->setSameAsBilling(0);
+ $quote->setReservedOrderId(null);
+
+ $checkout = $this->getCheckout($quote);
+ $checkout->place('token');
+
+ $order = $checkout->getOrder();
+ $this->assertNotEmpty($order->getRealOrderId());
+ }
+
/**
* @param Quote $quote
* @return Checkout
@@ -721,11 +745,11 @@ private function getFixtureQuote(): Quote
/**
* Adds countryFactory to a mock.
*
- * @param \PHPUnit\Framework\MockObject\MockObject $api
+ * @param MockObject $api
* @return void
* @throws \ReflectionException
*/
- private function addCountryFactory(\PHPUnit\Framework\MockObject\MockObject $api): void
+ private function addCountryFactory(MockObject $api): void
{
$reflection = new \ReflectionClass($api);
$property = $reflection->getProperty('_countryFactory');
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
index 391be01b17f45..f2ae33ee85093 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
@@ -5,28 +5,43 @@
*/
namespace Magento\Quote\Observer\Frontend\Quote\Address;
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Customer\Model\Customer;
+use Magento\Customer\Model\CustomerRegistry;
+use Magento\Customer\Model\Group;
+use Magento\Framework\Event\Observer;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\Quote\Address\Total;
+use Magento\Quote\Model\Shipping;
+use Magento\Quote\Model\ShippingAssignment;
use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
-class CollectTotalsObserverTest extends \PHPUnit\Framework\TestCase
+/**
+ * Test for \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver.
+ */
+class CollectTotalsObserverTest extends TestCase
{
+ private const STUB_CUSTOMER_EMAIL = 'customer@example.com';
+
/**
- * @var \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver
+ * @var CollectTotalsObserver
*/
- protected $model;
+ private $model;
/**
- * Object Manager
- *
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var ObjectManagerInterface
*/
private $objectManager;
+ /**
+ * @inheridoc
+ */
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
- $this->model = $this->objectManager->create(
- \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::class
- );
+ $this->model = $this->objectManager->create(CollectTotalsObserver::class);
}
/**
@@ -37,37 +52,37 @@ protected function setUp(): void
*
* @covers \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::execute
*/
- public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGroupChange()
+ public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGroupChange(): void
{
- /** @var \Magento\Framework\ObjectManagerInterface $objectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var ObjectManagerInterface $objectManager */
+ $objectManager = Bootstrap::getObjectManager();
- /** @var $customer \Magento\Customer\Model\Customer */
- $customer = $objectManager->create(\Magento\Customer\Model\Customer::class);
+ /** @var $customer Customer */
+ $customer = $objectManager->create(Customer::class);
$customer->load(1);
$customer->setDisableAutoGroupChange(1);
$customer->setGroupId(2);
$customer->save();
- /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */
- $customerRepository = $objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
+ /** @var CustomerRepositoryInterface $customerRepository */
+ $customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
$customerData = $customerRepository->getById($customer->getId());
- /** @var $quote \Magento\Quote\Model\Quote */
- $quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var $quote Quote */
+ $quote = $objectManager->create(Quote::class);
$quote->load('test01', 'reserved_order_id');
$quote->setCustomer($customerData);
$quoteAddress = $quote->getBillingAddress();
- $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class);
- $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class);
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
$shipping->setAddress($quoteAddress);
$shippingAssignment->setShipping($shipping);
- /** @var \Magento\Quote\Model\Quote\Address\Total $total */
- $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
$eventObserver = $objectManager->create(
- \Magento\Framework\Event\Observer::class,
+ Observer::class,
['data' => [
'quote' => $quote,
'shipping_assignment' => $shippingAssignment,
@@ -88,51 +103,80 @@ public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGr
*
* @covers \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::execute
*/
- public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGroupChange()
+ public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGroupChange(): void
{
- /** @var \Magento\Framework\ObjectManagerInterface $objectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var ObjectManagerInterface $objectManager */
+ $objectManager = Bootstrap::getObjectManager();
- /** @var $customer \Magento\Customer\Model\Customer */
- $customer = $objectManager->create(\Magento\Customer\Model\Customer::class);
+ /** @var $customer Customer */
+ $customer = $objectManager->create(Customer::class);
$customer->load(1);
$customer->setDisableAutoGroupChange(0);
$customer->setGroupId(2);
$customer->save();
- /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */
- $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class);
+ /** @var CustomerRegistry $customerRegistry */
+ $customerRegistry = $objectManager->get(CustomerRegistry::class);
$customerRegistry->remove($customer->getId());
- /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */
- $customerRepository = $objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
+ /** @var CustomerRepositoryInterface $customerRepository */
+ $customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
$customerData = $customerRepository->getById($customer->getId());
- /** @var $quote \Magento\Quote\Model\Quote */
- $quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var $quote Quote */
+ $quote = $objectManager->create(Quote::class);
$quote->load('test01', 'reserved_order_id');
$quote->setCustomer($customerData);
$quoteAddress = $quote->getBillingAddress();
- $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class);
- $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class);
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
$shipping->setAddress($quoteAddress);
$shippingAssignment->setShipping($shipping);
- /** @var \Magento\Quote\Model\Quote\Address\Total $total */
- $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
$eventObserver = $objectManager->create(
- \Magento\Framework\Event\Observer::class,
- ['data' => [
- 'quote' => $quote,
- 'shipping_assignment' => $shippingAssignment,
- 'total' => $total
- ]
- ]
+ Observer::class,
+ ['data' => ['quote' => $quote, 'shipping_assignment' => $shippingAssignment, 'total' => $total]]
);
$this->model->execute($eventObserver);
$this->assertEquals(2, $quote->getCustomer()->getGroupId());
}
+
+ /**
+ * Dispatch event with guest quote and check that email will not be override to null when auto group assign enabled
+ *
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ *
+ * @return void
+ */
+ public function testQuoteCustomerEmailNotChanged(): void
+ {
+ // prepare quote for guest
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->setCustomerId(null)
+ ->setCustomerEmail(self::STUB_CUSTOMER_EMAIL)
+ ->setCustomerIsGuest(true)
+ ->setCustomerGroupId(Group::NOT_LOGGED_IN_ID);
+
+ $quoteAddress = $quote->getBillingAddress();
+
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
+ $shipping->setAddress($quoteAddress);
+ $shippingAssignment->setShipping($shipping);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
+
+ $eventObserver = $this->objectManager->create(
+ Observer::class,
+ ['data' => ['quote' => $quote, 'shipping_assignment' => $shippingAssignment, 'total' => $total]]
+ );
+ $this->model->execute($eventObserver);
+
+ $this->assertEquals(self::STUB_CUSTOMER_EMAIL, $quote->getCustomerEmail());
+ }
}
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 13003e40dc0a3..d00b4c784110c 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
@@ -9,7 +9,9 @@
use Magento\Framework\Escaper;
use Magento\Sales\Api\Data\InvoiceInterface;
+use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Model\Order;
+use Magento\Sales\Model\ResourceModel\Order\Item;
use PHPUnit\Framework\Constraint\StringContains;
/**
@@ -28,6 +30,9 @@ class SaveTest extends AbstractInvoiceControllerTest
/** @var Escaper */
private $escaper;
+ /** @var Item */
+ private $orderItemResource;
+
/**
* @inheritdoc
*/
@@ -36,6 +41,7 @@ protected function setUp(): void
parent::setUp();
$this->escaper = $this->_objectManager->get(Escaper::class);
+ $this->orderItemResource = $this->_objectManager->get(Item::class);
}
/**
@@ -173,6 +179,40 @@ public function testInvoiceWithoutQty(): void
$this->assertErrorResponse($this->escaper->escapeHtml($expectedMessage));
}
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_configurable_product.php
+ *
+ * @return void
+ */
+ public function testPartialInvoiceWitConfigurableProduct(): void
+ {
+ $order = $this->getOrder('100000001');
+ $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => '1']);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch($this->uri);
+ $this->assertSessionMessages($this->containsEqual((string)__('The invoice has been created.')));
+ $orderItems = $this->getOrderItemsQtyInvoiced((int)$order->getEntityId());
+ $this->assertCount(2, $orderItems);
+ $this->assertEquals(1, (int)$orderItems[0]);
+ $this->assertEquals($orderItems[0], $orderItems[1]);
+ }
+
+ /**
+ * Get order items qty invoiced
+ *
+ * @param int $orderId
+ * @return array
+ */
+ private function getOrderItemsQtyInvoiced(int $orderId): array
+ {
+ $connection = $this->orderItemResource->getConnection();
+ $select = $connection->select()
+ ->from($this->orderItemResource->getMainTable(), OrderItemInterface::QTY_INVOICED)
+ ->where(OrderItemInterface::ORDER_ID . ' = ?', $orderId);
+
+ return $connection->fetchCol($select);
+ }
+
/**
* @inheritdoc
*/
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
index 5a912c2960ab6..d5f252ffdad53 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
@@ -7,22 +7,71 @@
namespace Magento\Sales\Controller\Guest;
+use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Sales\Api\Data\OrderInterfaceFactory;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Helper\Guest;
use Magento\TestFramework\Request;
use Magento\TestFramework\TestCase\AbstractController;
/**
- * Test for \Magento\Sales\Controller\Guest\View class.
+ * Test for orders and returns controller.
+ *
+ * @see \Magento\Sales\Controller\Guest\View
*/
class ViewTest extends AbstractController
{
+ /** @var CookieManagerInterface */
+ private $cookieManager;
+
+ /** @var OrderInterfaceFactory */
+ private $orderFactory;
+
+ /** @var OrderRepositoryInterface */
+ private $orderRepository;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
+ $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class);
+ }
+
/**
* Check that controller applied GET requests.
+ *
+ * @return void
*/
- public function testExecuteWithGetRequest()
+ public function testExecuteWithGetRequest(): void
{
$this->getRequest()->setMethod(Request::METHOD_GET);
$this->dispatch('sales/guest/view/');
$this->assertRedirect($this->stringContains('sales/guest/form'));
}
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testExecuteWithWrongCookie(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $order->setProtectCode('0e6640');
+ $this->orderRepository->save($order);
+ $cookieValue = base64_encode('0' . ':' . $order->getIncrementId());
+ $this->cookieManager->setPublicCookie(Guest::COOKIE_NAME, $cookieValue);
+ $this->getRequest()->setMethod(Request::METHOD_GET);
+ $this->dispatch('sales/guest/view/');
+ $this->assertRedirect($this->stringContains('sales/guest/form/'));
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__('You entered incorrect data. Please try again.'))
+ );
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
index 22583483ddf69..7fe1983d3192f 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
@@ -5,7 +5,6 @@
*/
declare(strict_types=1);
-
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\TestFramework\Helper\Bootstrap;
@@ -30,6 +29,11 @@
$productRepository->cleanCache();
$product = $productRepository->get('product_disabled');
$wishlist->loadByCustomerId($customer->getId(), true);
+/** @var \Magento\Catalog\Helper\Product $productHelper */
+$productHelper = $objectManager->get(\Magento\Catalog\Helper\Product::class);
+$isSkipSaleableCheck = $productHelper->getSkipSaleableCheck();
+$productHelper->setSkipSaleableCheck(true);
$item = $wishlist->addNewItem($product);
+$productHelper->setSkipSaleableCheck($isSkipSaleableCheck);
$wishlist->setSharingCode('wishlist_disabled_item');
$wishListResource->save($wishlist);
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
index 4961d2403672c..54c26b73c70ba 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
@@ -25,4 +25,9 @@
$wishlistFactory = $objectManager->get(WishlistFactory::class);
$wishlist = $wishlistFactory->create();
$wishlist->loadByCustomerId($customer->getId(), true);
+/** @var \Magento\Catalog\Helper\Product $productHelper */
+$productHelper = $objectManager->get(\Magento\Catalog\Helper\Product::class);
+$isSkipSaleableCheck = $productHelper->getSkipSaleableCheck();
+$productHelper->setSkipSaleableCheck(true);
$wishlist->addNewItem($product);
+$productHelper->setSkipSaleableCheck($isSkipSaleableCheck);
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js
new file mode 100644
index 0000000000000..2d6b6cc88fe78
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'Magento_Catalog/js/product-gallery'
+], function ($) {
+ 'use strict';
+
+ var galleryEl,
+ defaultConfig = {
+ images: [
+ {
+ disabled: 0,
+ file: '/e/a/earth.jpg',
+ position: 2,
+ url: 'http://localhost/media/catalog/product/e/a/earth.jpg',
+ size: 2048,
+ 'value_id': 2
+ },
+ {
+ disabled: 0,
+ file: '/m/a/mars.jpg',
+ position: 3,
+ url: 'http://localhost/media/catalog/product/m/a/mars.jpg',
+ size: 3072,
+ 'value_id': 3
+ },
+ {
+ disabled: 0,
+ file: '/j/u/jupiter.jpg',
+ position: 5,
+ size: 5120,
+ url: 'http://localhost/media/catalog/product/j/u/jupiter.jpg',
+ 'value_id': 5
+ }
+ ],
+ types: {
+ 'image': {
+ code: 'image',
+ label: 'Base',
+ name: 'product[image]'
+ },
+ 'small_image': {
+ code: 'small_image',
+ label: 'Small',
+ name: 'product[image]'
+ },
+ 'thumbnail': {
+ code: 'thumbnail',
+ label: 'Thumbnail',
+ name: 'product[image]'
+ }
+ }
+ };
+
+ function init(config) {
+ $(galleryEl).productGallery($.extend({}, defaultConfig, config || {}));
+ }
+
+ beforeEach(function () {
+ $(''
+ ).appendTo(document.body);
+ galleryEl = document.getElementById('media_gallery_content');
+ });
+
+ afterEach(function () {
+ $(galleryEl).remove();
+ galleryEl = undefined;
+ });
+
+ describe('Magento_Catalog/js/product-gallery', function () {
+ describe('_create()', function () {
+ it('check that existing images are rendered correctly', function () {
+ init();
+ expect($(galleryEl).find('[data-role=image]').length).toBe(3);
+ expect($(galleryEl).find('[data-role=image]:nth-child(1) .position').val()).toBe('2');
+ expect($(galleryEl).find('[data-role=image]:nth-child(2) .position').val()).toBe('3');
+ expect($(galleryEl).find('[data-role=image]:nth-child(3) .position').val()).toBe('5');
+ });
+ });
+ describe('_addItem()', function () {
+ it('check that new image is inserted at the first position if there were no existing images', function () {
+ init({
+ images: []
+ });
+ $(galleryEl).trigger('addItem', {
+ file: '/s/a/saturn.jpg.tmp',
+ name: 'saturn.jpg',
+ size: 1024,
+ type: 'image/jpeg',
+ url: 'http://localhost/media/tmp/catalog/product/s/a/saturn.jpg'
+ });
+ expect($(galleryEl).find('[data-role=image]').length).toBe(1);
+ expect($(galleryEl).find('[data-role=image]:nth-child(1) .position').val()).toBe('1');
+ });
+ it('check that new image is inserted at the last position if there were existing images', function () {
+ init();
+ $(galleryEl).trigger('addItem', {
+ file: '/s/a/saturn.jpg.tmp',
+ name: 'saturn.jpg',
+ size: 1024,
+ type: 'image/jpeg',
+ url: 'http://localhost/media/tmp/catalog/product/s/a/saturn.jpg'
+ });
+ expect($(galleryEl).find('[data-role=image]').length).toBe(4);
+ // check that new image position is the position of previous image in the list plus one
+ expect($(galleryEl).find('[data-role=image]:nth-child(4) .position').val()).toBe('6');
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js
new file mode 100644
index 0000000000000..e07b1fbc69453
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js
@@ -0,0 +1,72 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+/*jscs:disable jsDoc*/
+define(['Magento_Customer/js/form/components/insert-listing'], function (Constr) {
+ 'use strict';
+
+ describe('Magento_Customer/js/form/components/insert-listing', function () {
+ var obj,
+ ids = ['1', '2'],
+ data = {
+ action: 'delete',
+ data: {
+ selected: ids
+ }
+ },
+ selectionsProvider = {
+ selected: jasmine.createSpy().and.returnValue(ids),
+ deselect: jasmine.createSpy()
+ };
+
+ beforeEach(function () {
+ obj = new Constr({
+ name: 'content_name',
+ selections: function () {
+ return selectionsProvider;
+ }
+ });
+ });
+
+ describe('Check delete massaction process', function () {
+ it('Check call to deleteMassaction method', function () {
+ obj.deleteMassaction = {
+ call: jasmine.createSpy()
+ };
+ obj.onMassAction(data);
+
+ expect(obj.deleteMassaction.call).toHaveBeenCalledWith(obj, {
+ selected: ids
+ });
+ });
+
+ it('Check ids are retrieved from selections provider if they are NOT in data', function () {
+ obj._delete = jasmine.createSpy();
+ obj.onMassAction({
+ action: 'delete',
+ data: {}
+ });
+
+ expect(selectionsProvider.selected).toHaveBeenCalled();
+ selectionsProvider.selected.calls.reset();
+ expect(obj._delete).toHaveBeenCalledWith([1, 2]);
+ });
+
+ it('Check removal of default addresses and selections by provided ids', function () {
+ obj.source = {
+ get: jasmine.createSpy().and.returnValues(2, 3),
+ set: jasmine.createSpy()
+ };
+ obj.onMassAction(data);
+
+ expect(selectionsProvider.selected).not.toHaveBeenCalled();
+ expect(obj.source.get.calls.count()).toEqual(2);
+ expect(obj.source.set.calls.count()).toEqual(1);
+ expect(selectionsProvider.deselect.calls.count()).toEqual(2);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js
new file mode 100644
index 0000000000000..c830632ed0e87
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+define([
+ 'jquery',
+ 'Magento_Customer/js/validation'
+], function ($) {
+ 'use strict';
+
+ describe('Testing customer DOB validation to tolerate zeroes in the single digit dates', function () {
+ var params,
+ dataProvider;
+
+ dataProvider = [
+ {
+ format: 'M/d/Y',
+ date: '09/2/18',
+ expects: true
+ },
+ {
+ format: 'M/DD/Y',
+ date: '09/2/18',
+ expects: false
+ }
+ ];
+
+ dataProvider.forEach(function (data) {
+ it('Test date validation for format ' + data.format, function () {
+ params = {
+ 'dateFormat': data.format
+ };
+ expect($.validator.methods['validate-date']
+ .call($.validator.prototype, data.date, null, params)).toEqual(data.expects);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js
new file mode 100644
index 0000000000000..4902fbad26ff3
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'jquery/validate',
+ 'Magento_Payment/js/transparent'
+], function ($) {
+ 'use strict';
+
+ var containerEl,
+ formEl,
+ jQueryAjax;
+
+ function init(config) {
+ var defaultConfig = {
+ orderSaveUrl: '/',
+ gateway: 'payflowpro',
+ editFormSelector: '#' + formEl.id
+ };
+
+ $(formEl).find(':radio[value="payflowpro"]').prop('checked', 'checked');
+ $(formEl).transparent($.extend({}, defaultConfig, config || {}));
+ }
+
+ beforeEach(function () {
+ if (!window.FORM_KEY) {
+ window.FORM_KEY = '61d0c9da0aa473d214f61913967cc0ea';
+ }
+ $('' +
+ '' +
+ '
'
+ ).appendTo(document.body);
+ containerEl = document.getElementById('admin_edit_order_form_container');
+ formEl = document.getElementById('admin_edit_order_form');
+ jQueryAjax = $.ajax;
+ });
+
+ afterEach(function () {
+ $(containerEl).remove();
+ formEl = undefined;
+ containerEl = undefined;
+ $.ajax = jQueryAjax;
+ jQueryAjax = undefined;
+ });
+
+ describe('Magento_Payment/js/transparent', function () {
+ describe('beforeSubmitOrder handler', function () {
+ it('is registered when selected payment method requires transparent', function () {
+ init();
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder[0].type).toBe('beforeSubmitOrder');
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder[0].namespace).toBe('payflowpro');
+ });
+ it('is not registered when selected payment method does not require transparent', function () {
+ init();
+ $(formEl).find(':radio[value="money_order"]').prop('checked', 'checked');
+ $(formEl).trigger('changePaymentMethod', ['money_order']);
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder).toBeUndefined();
+ });
+ it('returns false to prevent normal order creation', function () {
+ var beforeSubmitOrderEvent;
+
+ $.ajax = jasmine.createSpy();
+ init({
+ orderSaveUrl: '/admin/paypal/transparent/requestSecureToken'
+ });
+ beforeSubmitOrderEvent = $.Event('beforeSubmitOrder');
+ $(formEl).trigger(beforeSubmitOrderEvent);
+ expect($.ajax).toHaveBeenCalledWith(jasmine.objectContaining({
+ url: '/admin/paypal/transparent/requestSecureToken',
+ type: 'post'
+ }));
+ expect(beforeSubmitOrderEvent.result).toBe(false);
+ expect(beforeSubmitOrderEvent.isImmediatePropagationStopped()).toBe(true);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js
new file mode 100644
index 0000000000000..7e33a7ad3c1fa
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js
@@ -0,0 +1,117 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/*eslint max-nested-callbacks: 0*/
+define([
+ 'jquery',
+ 'squire',
+ 'underscore',
+ 'Magento_Sales/js/grid/tree-massactions'
+], function ($, Squire, _, TreeMassaction) {
+ 'use strict';
+
+ var injector = new Squire(),
+ mocks = {
+ 'Magento_Ui/js/grid/massactions': {
+ defaultCallback: jasmine.createSpy().and.returnValue({}),
+ applyAction: jasmine.createSpy().and.returnValue({})
+ }
+ },
+ obj,
+ utils;
+
+ describe('Magento_Sales/js/grid/tree-massactions', function () {
+ var model;
+
+ beforeEach(function (done) {
+ injector.mock(mocks);
+ injector.require([
+ 'Magento_Ui/js/grid/massactions',
+ 'mageUtils'
+ ], function (instance, mageUtils) {
+ obj = _.extend({}, instance);
+ utils = mageUtils;
+ done();
+ });
+ model = new TreeMassaction({
+ actions: [
+ {
+ type: 'availability',
+ actions: [{
+ type: 'enable'
+ }, {
+ type: 'disable'
+ }]
+ },
+ {
+ type: 'hold_order',
+ component: 'uiComponent',
+ label: 'hold',
+ url: 'http://local.magento/hold_order',
+ modules: {
+ selections: ['1','2','3']
+ },
+ actions: [{
+ callback: 'defaultCallback'
+ }]
+ }]
+ });
+ });
+
+ afterEach(function () {
+ try {
+ injector.clean();
+ injector.remove();
+ } catch (e) {}
+ });
+
+ describe('check applyAction', function () {
+ it('change visibility of submenu', function () {
+ expect(model.actions()[0].visible()).toBeFalsy();
+ expect(model.applyAction('availability')).toBe(model);
+ expect(model.actions()[0].visible()).toBeTruthy();
+ });
+ });
+ describe('check defaultCallback', function () {
+ it('check model called with action and selected data', function () {
+ expect(model.applyAction('hold_order')).toBe(model);
+ expect(model.actions()[1].visible()).toBeTruthy();
+ expect(model.actions()[1].modules.selections).toBeTruthy();
+ expect(model.actions()[1].modules.selections.total).toBeFalsy();
+ });
+
+ it('check defaultCallback submitted the data', function () {
+ var action = {
+ component: 'uiComponent',
+ label: 'Hold',
+ type: 'hold_order',
+ url: 'http://local.magento/hold_order/'
+ },
+ data = {
+ excludeMode: true,
+ excluded: [],
+ params: {},
+ selected: ['7', '6', '5', '4', '3', '2', '1'],
+ total: 7
+ },
+ result;
+
+ obj.getAction = jasmine.createSpy().and.returnValue('hold_order');
+
+ obj.applyAction(action);
+
+ result = obj.defaultCallback(action, data);
+
+ expect(typeof result).toBe('object');
+ spyOn(utils, 'submit').and.callThrough();
+ utils.submit({
+ url: action.url,
+ data: data.selected
+ });
+ expect(utils.submit).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js
new file mode 100644
index 0000000000000..0071d5af7df4e
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js
@@ -0,0 +1,260 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'squire',
+ 'jquery/validate'
+], function ($, Squire) {
+ 'use strict';
+
+ var formEl,
+ jQueryAjax,
+ order,
+ tmpl = '';
+
+ $.widget('magetest.testPaymentMethodA', {
+ options: {
+ code: null,
+ orderSaveUrl: null,
+ orderFormSelector: null
+ },
+
+ _create: function () {
+ var $editForm = $(this.options.orderFormSelector);
+
+ $editForm.off('changePaymentMethod.' + this.options.code)
+ .on('changePaymentMethod.' + this.options.code, this._onChangePaymentMethod.bind(this));
+ },
+
+ _onChangePaymentMethod: function (event, method) {
+ var $editForm = $(this.options.orderFormSelector);
+
+ $editForm.off('beforeSubmitOrder.' + this.options.code);
+
+ if (method === this.options.code) {
+ $editForm.on('beforeSubmitOrder.' + this.options.code, this._submitOrder.bind(this));
+ }
+ },
+
+ _submitOrder: function (event) {
+ $.ajax({
+ url: this.options.orderSaveUrl,
+ type: 'POST',
+ context: this,
+ data: {
+ code: this.options.code
+ },
+ dataType: 'JSON'
+ });
+ event.stopImmediatePropagation();
+
+ return false;
+ }
+
+ });
+
+ $.widget('magetest.testPaymentMethodB', $.magetest.testPaymentMethodA, {
+ isActive: false,
+ _onChangePaymentMethod: function (event, method) {
+ var $editForm = $(this.options.orderFormSelector),
+ isActive = method === this.options.code;
+
+ if (this.isActive !== isActive) {
+ this.isActive = isActive;
+
+ if (!isActive) {
+ $editForm.off('submitOrder.' + this.options.code);
+ } else {
+ $editForm.off('submitOrder')
+ .on('submitOrder.' + this.options.code, this._submitOrder.bind(this));
+ }
+ }
+ }
+ });
+
+ function init(config) {
+ config = config || {};
+ order = new window.AdminOrder({});
+ $(formEl).validate({});
+ $(formEl).find(':radio[value="payment1"]').testPaymentMethodA({
+ code: 'payment1',
+ orderSaveUrl: '/admin/sales/order/create/payment_method/payment1',
+ orderFormSelector: '#' + formEl.id
+ });
+ $(formEl).find(':radio[value="payment2"]').testPaymentMethodB({
+ code: 'payment2',
+ orderSaveUrl: '/admin/sales/order/create/payment_method/payment2',
+ orderFormSelector: '#' + formEl.id
+ });
+ $(formEl).off('realOrder').on('realOrder', function () {
+ $.ajax({
+ url: '/admin/sales/order/create',
+ type: 'POST',
+ context: this,
+ data: $(this).serializeArray(),
+ dataType: 'JSON'
+ });
+ });
+
+ if (config.method) {
+ $(formEl).find(':radio[value="' + config.method + '"]').prop('checked', true);
+ order.switchPaymentMethod(config.method);
+ }
+ }
+
+ describe('Magento_Sales/order/create/scripts', function () {
+ var injector = new Squire(),
+ mocks = {
+ 'jquery': $,
+ 'Magento_Catalog/catalog/product/composite/configure': jasmine.createSpy(),
+ 'Magento_Ui/js/modal/confirm': jasmine.createSpy(),
+ 'Magento_Ui/js/modal/alert': jasmine.createSpy(),
+ 'Magento_Ui/js/lib/view/utils/async': jasmine.createSpy()
+ };
+
+ beforeEach(function (done) {
+ jQueryAjax = $.ajax;
+ injector.mock(mocks);
+ injector.require(['Magento_Sales/order/create/scripts'], function () {
+ window.FORM_KEY = window.FORM_KEY || '61d0c9da0aa473d214f61913967cc0ea';
+ $(tmpl).appendTo(document.body);
+ formEl = document.getElementById('edit_form');
+ $(formEl).off();
+ done();
+ });
+ });
+
+ afterEach(function () {
+ try {
+ injector.clean();
+ injector.remove();
+ } catch (e) {
+ }
+ $(formEl).off().remove();
+ formEl = undefined;
+ order = undefined;
+ $.ajax = jQueryAjax;
+ jQueryAjax = undefined;
+ });
+
+ describe('submit()', function () {
+ function testSubmit(currentPaymentMethod, paymentMethod, ajaxParams) {
+ $.ajax = jasmine.createSpy('$.ajax');
+ init({
+ method: currentPaymentMethod
+ });
+ $(formEl).find(':radio[value="' + paymentMethod + '"]').prop('checked', true);
+ order.switchPaymentMethod(paymentMethod);
+ order.submit();
+ expect($.ajax).toHaveBeenCalledTimes(1);
+ expect($.ajax).toHaveBeenCalledWith(jasmine.objectContaining(ajaxParams));
+ }
+
+ it('Check that payment custom handler is executed #1', function () {
+ testSubmit(
+ null,
+ 'payment1',
+ {
+ url: '/admin/sales/order/create/payment_method/payment1',
+ data: {
+ code: 'payment1'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #2', function () {
+ testSubmit(
+ 'payment1',
+ 'payment1',
+ {
+ url: '/admin/sales/order/create/payment_method/payment1',
+ data: {
+ code: 'payment1'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #3', function () {
+ testSubmit(
+ null,
+ 'payment2',
+ {
+ url: '/admin/sales/order/create/payment_method/payment2',
+ data: {
+ code: 'payment2'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #4', function () {
+ testSubmit(
+ 'payment2',
+ 'payment2',
+ {
+ url: '/admin/sales/order/create/payment_method/payment2',
+ data: {
+ code: 'payment2'
+ }
+ }
+ );
+ });
+
+ it('Check that native handler is executed for payment without custom handler #1', function () {
+ testSubmit(
+ 'payment1',
+ 'free',
+ {
+ url: '/admin/sales/order/create',
+ data: [
+ {
+ name: 'payment[method]',
+ value: 'free'
+ }
+ ]
+ }
+ );
+ });
+
+ it('Check that native handler is executed for payment without custom handler #2', function () {
+ testSubmit(
+ 'payment2',
+ 'free',
+ {
+ url: '/admin/sales/order/create',
+ data: [
+ {
+ name: 'payment[method]',
+ value: 'free'
+ }
+ ]
+ }
+ );
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
index de3387e31af88..5975f21e08070 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
@@ -135,6 +135,19 @@ define([
expect(multiSelect.selected().toString()).toEqual('3,4,1,2');
});
+ it('Select all rows all over the Grid and deselects all records', function () {
+ multiSelect.rows([{
+ id: 1
+ }, {
+ id: 2
+ }]);
+
+ multiSelect.selectAll();
+ multiSelect.deselectAll();
+ multiSelect.indetermine(2);
+ expect(multiSelect.togglePage().selected()).toEqual([1, 2]);
+ });
+
it('Select all rows all over the Grid without all rows on current page but with specific rows on another page',
function () {
multiSelect.rows([{
diff --git a/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
index 7700c3d64a1b7..5db506b00a883 100644
--- a/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
+++ b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
@@ -98,8 +98,53 @@ define([
expect(gallery.settings.fotoramaApi).toBeDefined();
expect(gallery.settings.data).toBeDefined();
expect(gallery.settings.api).toBeDefined();
+ expect(gallery.settings.activeBreakpoint).toEqual({});
$.fn.data = originSpy;
});
+
+ it('Verify gallery navigation is set properly as dots if specified in options', function () {
+ // added
+ options.breakpoints = {
+ mobile: {
+ conditions: {
+ 'max-width': '767px'
+ },
+ options: {
+ options: {
+ nav: 'dots'
+ }
+ }
+ },
+ desktop: {
+ conditions: {
+ 'min-width': '1024px'
+ },
+ options: {
+ options: {
+ nav: 'thumbs'
+ }
+ }
+ }
+ };
+
+ originSpy = $.fn.data;
+ jqueryDataMock = {
+ setOptions: jasmine.createSpy().and.returnValue(true),
+ updateOptions: jasmine.createSpy().and.returnValue(true)
+ };
+ spyOn($.fn, 'data').and.callFake(function () {
+ return jqueryDataMock;
+ });
+
+ gallery = new Gallery(options, element);
+
+ options.breakpoints.mobile.options.options.arrows = false;
+ expect(JSON.stringify(gallery.settings.activeBreakpoint))
+ .toEqual(JSON.stringify(options.breakpoints.mobile.options));
+
+ $.fn.data = originSpy;
+ });
+
});
});
diff --git a/lib/internal/Magento/Framework/File/Mime.php b/lib/internal/Magento/Framework/File/Mime.php
index 148f43d47cfd4..e0b22e4c944d9 100644
--- a/lib/internal/Magento/Framework/File/Mime.php
+++ b/lib/internal/Magento/Framework/File/Mime.php
@@ -108,6 +108,9 @@ public function getMimeType($file)
if (function_exists('mime_content_type')) {
$result = $this->getNativeMimeType($file);
+ } else {
+ $imageInfo = getimagesize($file);
+ $result = $imageInfo['mime'];
}
if (null === $result && isset($this->mimeTypes[$extension])) {
diff --git a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
index 3c571452d7a3e..7a54a7966b500 100644
--- a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
+++ b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
@@ -58,6 +58,7 @@ public function getMimeTypeDataProvider(): array
'weird extension' => [__DIR__ . '/_files/file.weird', 'application/octet-stream'],
'weird uppercase extension' => [__DIR__ . '/_files/UPPERCASE.WEIRD', 'application/octet-stream'],
'generic mime type' => [__DIR__ . '/_files/blank.html', 'text/html'],
+ 'tmp file mime type' => [__DIR__ . '/_files/magento', 'image/jpeg'],
];
}
}
diff --git a/lib/internal/Magento/Framework/File/Test/Unit/_files/magento b/lib/internal/Magento/Framework/File/Test/Unit/_files/magento
new file mode 100644
index 0000000000000..c377daf8fb0b3
Binary files /dev/null and b/lib/internal/Magento/Framework/File/Test/Unit/_files/magento differ
diff --git a/lib/internal/Magento/Framework/Filter/Template/Tokenizer/AbstractTokenizer.php b/lib/internal/Magento/Framework/Filter/Template/Tokenizer/AbstractTokenizer.php
index b72305c6f5bdc..9afbe8672e1af 100644
--- a/lib/internal/Magento/Framework/Filter/Template/Tokenizer/AbstractTokenizer.php
+++ b/lib/internal/Magento/Framework/Filter/Template/Tokenizer/AbstractTokenizer.php
@@ -115,7 +115,7 @@ public function reset()
*/
public function isWhiteSpace()
{
- return trim($this->char()) != $this->char();
+ return $this->_string === '' ?: trim($this->char()) !== $this->char();
}
/**
diff --git a/lib/internal/Magento/Framework/Filter/Template/Tokenizer/Parameter.php b/lib/internal/Magento/Framework/Filter/Template/Tokenizer/Parameter.php
index 624d6107bb6b8..61fdd2ea1b283 100644
--- a/lib/internal/Magento/Framework/Filter/Template/Tokenizer/Parameter.php
+++ b/lib/internal/Magento/Framework/Filter/Template/Tokenizer/Parameter.php
@@ -19,16 +19,18 @@ public function tokenize()
{
$parameters = [];
$parameterName = '';
- while ($this->next()) {
+ do {
if ($this->isWhiteSpace()) {
continue;
- } elseif ($this->char() != '=') {
+ }
+
+ if ($this->char() !== '=') {
$parameterName .= $this->char();
} else {
$parameters[$parameterName] = $this->getValue();
$parameterName = '';
}
- }
+ } while ($this->next());
return $parameters;
}
diff --git a/lib/internal/Magento/Framework/HTTP/Client/Curl.php b/lib/internal/Magento/Framework/HTTP/Client/Curl.php
index 60825e231504d..5379d481289f5 100644
--- a/lib/internal/Magento/Framework/HTTP/Client/Curl.php
+++ b/lib/internal/Magento/Framework/HTTP/Client/Curl.php
@@ -453,11 +453,8 @@ protected function parseHeaders($ch, $data)
}
if (strlen($name)) {
- if ("Set-Cookie" == $name) {
- if (!isset($this->_responseHeaders[$name])) {
- $this->_responseHeaders[$name] = [];
- }
- $this->_responseHeaders[$name][] = $value;
+ if ('set-cookie' === strtolower($name)) {
+ $this->_responseHeaders['Set-Cookie'][] = $value;
} else {
$this->_responseHeaders[$name] = $value;
}
diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/CurlTest.php b/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/CurlTest.php
index 17b88e398230e..22fed9cfc7467 100644
--- a/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/CurlTest.php
+++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/CurlTest.php
@@ -26,4 +26,57 @@ public function testInvalidProtocol()
$client = new Curl();
$client->get('telnet://127.0.0.1/test');
}
+
+ /**
+ * Check the HTTP client ability to parse headers case-insensitive.
+ */
+ public function testParseHeaders()
+ {
+ // Prepare protected parseHeaders method
+ $curl = new Curl();
+ $parseHeaders = new \ReflectionMethod(
+ $curl,
+ 'parseHeaders'
+ );
+ $parseHeaders->setAccessible(true);
+
+ // Parse headers
+ foreach ($this->headersDataProvider() as $header) {
+ $parseHeaders->invoke($curl, null, $header);
+ }
+
+ // Validate headers
+ $headers = $curl->getHeaders();
+ $this->assertIsArray($headers);
+ $this->assertEquals([
+ 'Content-Type' => 'text/html; charset=utf-8',
+ 'Set-Cookie' => [
+ 'Normal=OK',
+ 'Uppercase=OK',
+ 'Lowercase=OK',
+ ]
+ ], $headers);
+
+ // Validate status
+ $status = $curl->getStatus();
+ $this->assertIsInt($status);
+ $this->assertEquals(200, $status);
+
+ // Validate cookies
+ $cookies = $curl->getCookies();
+ $this->assertIsArray($cookies);
+ $this->assertEquals([
+ 'Normal' => 'OK',
+ 'Uppercase' => 'OK',
+ 'Lowercase' => 'OK',
+ ], $cookies);
+ }
+
+ /**
+ * @return array
+ */
+ public function headersDataProvider()
+ {
+ return array_filter(explode(PHP_EOL, file_get_contents(__DIR__ . '/_files/curl_headers.txt')));
+ }
}
diff --git a/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/_files/curl_headers.txt b/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/_files/curl_headers.txt
new file mode 100644
index 0000000000000..48a2d6fbcf0b7
--- /dev/null
+++ b/lib/internal/Magento/Framework/HTTP/Test/Unit/Client/_files/curl_headers.txt
@@ -0,0 +1,5 @@
+Status: 200 OK
+Content-Type: text/html; charset=utf-8
+Set-Cookie: Normal=OK
+SET-COOKIE: Uppercase=OK
+set-cookie: Lowercase=OK
diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
index 42ffeae2aa883..cdf175767d6aa 100644
--- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
+++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php
@@ -30,6 +30,11 @@ class Timezone implements TimezoneInterface
\IntlDateFormatter::SHORT,
];
+ /**
+ * @var array
+ */
+ private $dateFormatterCache = [];
+
/**
* @var string
*/
@@ -174,16 +179,9 @@ public function date($date = null, $locale = null, $useTimezone = true, $include
case ($date instanceof \DateTimeImmutable):
return new \DateTime($date->format('Y-m-d H:i:s'), $date->getTimezone());
case (!is_numeric($date)):
- $timeType = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE;
- $formatter = new \IntlDateFormatter(
- $locale,
- \IntlDateFormatter::MEDIUM,
- $timeType,
- new \DateTimeZone($timezone)
- );
-
$date = $this->appendTimeIfNeeded($date, $includeTime, $timezone, $locale);
- $date = $formatter->parse($date) ?: (new \DateTime($date))->getTimestamp();
+ $date = $this->parseLocaleDate($date, $locale, $timezone, $includeTime)
+ ?: (new \DateTime($date))->getTimestamp();
break;
}
@@ -343,33 +341,77 @@ public function convertConfigTimeToUtc($date, $format = 'Y-m-d H:i:s')
private function appendTimeIfNeeded($date, $includeTime, $timezone, $locale)
{
if ($includeTime && !preg_match('/\d{1}:\d{2}/', $date)) {
-
- $formatterWithoutHour = new \IntlDateFormatter(
- $locale,
- \IntlDateFormatter::MEDIUM,
- \IntlDateFormatter::NONE,
- new \DateTimeZone($timezone)
- );
- $convertedDate = $formatterWithoutHour->parse($date);
-
+ $convertedDate = $this->parseLocaleDate($date, $locale, $timezone, false);
if (!$convertedDate) {
throw new LocalizedException(
new Phrase(
'Could not append time to DateTime'
)
);
-
}
- $formatterWithHour = new \IntlDateFormatter(
+ $formatterWithHour = $this->getDateFormatter(
$locale,
+ $timezone,
\IntlDateFormatter::MEDIUM,
- \IntlDateFormatter::SHORT,
- new \DateTimeZone($timezone)
+ \IntlDateFormatter::SHORT
);
-
$date = $formatterWithHour->format($convertedDate);
}
return $date;
}
+
+ /**
+ * Parse date by locale format through IntlDateFormatter
+ *
+ * @param string $date
+ * @param string $locale
+ * @param string $timeZone
+ * @param bool $includeTime
+ * @return int|null Timestamp of date
+ */
+ private function parseLocaleDate(string $date, string $locale, string $timeZone, bool $includeTime): ?int
+ {
+ $allowedStyles = [\IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT];
+ $timeStyle = $includeTime ? \IntlDateFormatter::SHORT : \IntlDateFormatter::NONE;
+
+ /**
+ * Try to parse date with different styles
+ */
+ foreach ($allowedStyles as $style) {
+ $formatter = $this->getDateFormatter($locale, $timeZone, $style, $timeStyle);
+ $timeStamp = $formatter->parse($date);
+ if ($timeStamp) {
+ return $timeStamp;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get date formatter for locale
+ *
+ * @param string $locale
+ * @param string $timeZone
+ * @param int $style
+ * @param int $timeStyle
+ * @return \IntlDateFormatter
+ */
+ private function getDateFormatter(string $locale, string $timeZone, int $style, int $timeStyle): \IntlDateFormatter
+ {
+ $cacheKey = "{$locale}_{$timeZone}_{$style}_{$timeStyle}";
+ if (isset($this->dateFormatterCache[$cacheKey])) {
+ return $this->dateFormatterCache[$cacheKey];
+ }
+
+ $this->dateFormatterCache[$cacheKey] = new \IntlDateFormatter(
+ $locale,
+ $style,
+ $timeStyle,
+ new \DateTimeZone($timeZone)
+ );
+
+ return $this->dateFormatterCache[$cacheKey];
+ }
}
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 09f85567e4c0d..2e8110316ec29 100644
--- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php
+++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php
@@ -101,7 +101,12 @@ public function testDateIncludeTime($date, $locale, $includeTime, $expectedTimes
/** @var \DateTime $dateTime */
$dateTime = $timezone->date($date, $locale, true, $includeTime);
- $this->assertEquals($expectedTimestamp, $dateTime->getTimestamp());
+ if (is_numeric($expectedTimestamp)) {
+ $this->assertEquals($expectedTimestamp, $dateTime->getTimestamp());
+ } else {
+ $format = $includeTime ? DateTime::DATETIME_PHP_FORMAT : DateTime::DATE_PHP_FORMAT;
+ $this->assertEquals($expectedTimestamp, date($format, $dateTime->getTimestamp()));
+ }
}
/**
@@ -151,7 +156,25 @@ public function dateIncludeTimeDataProvider(): array
'el_GR', // locale
false, // include time
1635570000 // expected timestamp
- ]
+ ],
+ 'Parse Saudi Arabia date without time' => [
+ '31/8/2020 02020',
+ 'ar_SA',
+ false,
+ '2020-08-31'
+ ],
+ 'Parse date in short style with long year 1999' => [
+ '9/11/1999',
+ 'en_US',
+ false,
+ '1999-09-11'
+ ],
+ 'Parse date in short style with long year 2099' => [
+ '9/2/2099',
+ 'en_US',
+ false,
+ '2099-09-02'
+ ],
];
}
diff --git a/lib/internal/Magento/Framework/View/Template/Html/Minifier.php b/lib/internal/Magento/Framework/View/Template/Html/Minifier.php
index cdbce9d102a89..fdbb3531b6083 100644
--- a/lib/internal/Magento/Framework/View/Template/Html/Minifier.php
+++ b/lib/internal/Magento/Framework/View/Template/Html/Minifier.php
@@ -144,7 +144,7 @@ function ($match) use (&$heredocs) {
'#(?)[^\n\r]*#',
'',
preg_replace(
- '#(?)#',
+ '#(?)#',
' $1',
preg_replace(
'#(?)[^\n\r]*#',
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php
index 3b13a2f723617..4b543753c05bb 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Template/Html/MinifierTest.php
@@ -140,6 +140,7 @@ public function testMinify()
Text Link
someMethod(); ?>
+ = \$block->someMethod(); ?>