diff --git a/app/code/Magento/Elasticsearch/etc/indexer.xml b/app/code/Magento/Elasticsearch/etc/indexer.xml
new file mode 100644
index 0000000000000..245829396a67b
--- /dev/null
+++ b/app/code/Magento/Elasticsearch/etc/indexer.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Paypal/Model/Ipn.php b/app/code/Magento/Paypal/Model/Ipn.php
index a370bbc77ffb2..9107762c54b69 100644
--- a/app/code/Magento/Paypal/Model/Ipn.php
+++ b/app/code/Magento/Paypal/Model/Ipn.php
@@ -7,9 +7,9 @@
namespace Magento\Paypal\Model;
use Exception;
+use Magento\Framework\Exception\LocalizedException;
use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender;
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
-use Magento\Paypal\Model\Info;
/**
* PayPal Instant Payment Notification processor model
@@ -164,11 +164,11 @@ protected function _processOrder()
case Info::TXN_TYPE_NEW_CASE:
$this->_registerDispute();
break;
- // handle new adjustment is created
+ // handle new adjustment is created
case Info::TXN_TYPE_ADJUSTMENT:
$this->_registerAdjustment();
break;
- //handle new transaction created
+ //handle new transaction created
default:
$this->_registerTransaction();
break;
@@ -239,16 +239,16 @@ protected function _registerTransaction()
case Info::PAYMENTSTATUS_COMPLETED:
$this->_registerPaymentCapture(true);
break;
- // the holded payment was denied on paypal side
+ // the holded payment was denied on paypal side
case Info::PAYMENTSTATUS_DENIED:
$this->_registerPaymentDenial();
break;
- // customer attempted to pay via bank account, but failed
+ // customer attempted to pay via bank account, but failed
case Info::PAYMENTSTATUS_FAILED:
// cancel order
$this->_registerPaymentFailure();
break;
- // payment was obtained, but money were not captured yet
+ // payment was obtained, but money were not captured yet
case Info::PAYMENTSTATUS_PENDING:
$this->_registerPaymentPending();
break;
@@ -263,7 +263,7 @@ protected function _registerTransaction()
case Info::PAYMENTSTATUS_REFUNDED:
$this->_registerPaymentRefund();
break;
- // authorization expire/void
+ // authorization expire/void
case Info::PAYMENTSTATUS_EXPIRED:
// break is intentionally omitted
case Info::PAYMENTSTATUS_VOIDED:
@@ -288,24 +288,12 @@ protected function _registerPaymentCapture($skipFraudDetection = false)
$parentTransactionId = $this->getRequestData('parent_txn_id');
$this->_importPaymentInformation();
$payment = $this->_order->getPayment();
- $payment->setTransactionId(
- $this->getRequestData('txn_id')
- );
- $payment->setCurrencyCode(
- $this->getRequestData('mc_currency')
- );
- $payment->setPreparedMessage(
- $this->_createIpnComment('')
- );
- $payment->setParentTransactionId(
- $parentTransactionId
- );
- $payment->setShouldCloseParentTransaction(
- 'Completed' === $this->getRequestData('auth_status')
- );
- $payment->setIsTransactionClosed(
- 0
- );
+ $payment->setTransactionId($this->getRequestData('txn_id'));
+ $payment->setCurrencyCode($this->getRequestData('mc_currency'));
+ $payment->setPreparedMessage($this->_createIpnComment(''));
+ $payment->setParentTransactionId($parentTransactionId);
+ $payment->setShouldCloseParentTransaction('Completed' === $this->getRequestData('auth_status'));
+ $payment->setIsTransactionClosed(0);
$payment->registerCaptureNotification(
$this->getRequestData('mc_gross'),
$skipFraudDetection && $parentTransactionId
@@ -318,9 +306,9 @@ protected function _registerPaymentCapture($skipFraudDetection = false)
$this->orderSender->send($this->_order);
$this->_order->addStatusHistoryComment(
__('You notified customer about invoice #%1.', $invoice->getIncrementId())
- )->setIsCustomerNotified(
- true
- )->save();
+ )
+ ->setIsCustomerNotified(true)
+ ->save();
}
}
@@ -334,15 +322,13 @@ protected function _registerPaymentDenial()
{
try {
$this->_importPaymentInformation();
- $this->_order->getPayment()->setTransactionId(
- $this->getRequestData('txn_id')
- )->setNotificationResult(
- true
- )->setIsTransactionClosed(
- true
- )->deny(false);
+ $this->_order->getPayment()
+ ->setTransactionId($this->getRequestData('txn_id'))
+ ->setNotificationResult(true)
+ ->setIsTransactionClosed(true)
+ ->deny(false);
$this->_order->save();
- } catch (\Magento\Framework\Exception\LocalizedException $e) {
+ } catch (LocalizedException $e) {
if ($e->getMessage() != __('We cannot cancel this order.')) {
throw $e;
}
@@ -386,13 +372,11 @@ public function _registerPaymentPending()
$this->_importPaymentInformation();
- $this->_order->getPayment()->setPreparedMessage(
- $this->_createIpnComment($this->_paypalInfo->explainPendingReason($reason))
- )->setTransactionId(
- $this->getRequestData('txn_id')
- )->setIsTransactionClosed(
- 0
- )->update(false);
+ $this->_order->getPayment()
+ ->setPreparedMessage($this->_createIpnComment($this->_paypalInfo->explainPendingReason($reason)))
+ ->setTransactionId($this->getRequestData('txn_id'))
+ ->setIsTransactionClosed(0)
+ ->update(false);
$this->_order->save();
}
@@ -409,19 +393,12 @@ protected function _registerPaymentAuthorization()
$payment->update(true);
} else {
$this->_importPaymentInformation();
- $payment->setPreparedMessage(
- $this->_createIpnComment('')
- )->setTransactionId(
- $this->getRequestData('txn_id')
- )->setParentTransactionId(
- $this->getRequestData('parent_txn_id')
- )->setCurrencyCode(
- $this->getRequestData('mc_currency')
- )->setIsTransactionClosed(
- 0
- )->registerAuthorizationNotification(
- $this->getRequestData('mc_gross')
- );
+ $payment->setPreparedMessage($this->_createIpnComment(''))
+ ->setTransactionId($this->getRequestData('txn_id'))
+ ->setParentTransactionId($this->getRequestData('parent_txn_id'))
+ ->setCurrencyCode($this->getRequestData('mc_currency'))
+ ->setIsTransactionClosed(0)
+ ->registerAuthorizationNotification($this->getRequestData('mc_gross'));
}
if (!$this->_order->getEmailSent()) {
$this->orderSender->send($this->_order);
@@ -449,12 +426,13 @@ protected function _registerPaymentReversal()
{
$reasonCode = $this->getRequestData('reason_code');
$reasonComment = $this->_paypalInfo->explainReasonCode($reasonCode);
- $notificationAmount = $this->_order->getBaseCurrency()->formatTxt(
- $this->getRequestData('mc_gross') + $this->getRequestData('mc_fee')
- );
+ $notificationAmount = $this->_order->getBaseCurrency()
+ ->formatTxt(
+ $this->getRequestData('mc_gross') + $this->getRequestData('mc_fee')
+ );
$paymentStatus = $this->_filterPaymentStatus($this->getRequestData('payment_status'));
$orderStatus = $paymentStatus ==
- Info::PAYMENTSTATUS_REVERSED ? Info::ORDER_STATUS_REVERSED : Info::ORDER_STATUS_CANCELED_REVERSAL;
+ Info::PAYMENTSTATUS_REVERSED ? Info::ORDER_STATUS_REVERSED : Info::ORDER_STATUS_CANCELED_REVERSAL;
//Change order status to PayPal Reversed/PayPal Cancelled Reversal if it is possible.
$message = __(
'IPN "%1". %2 Transaction amount %3. Transaction ID: "%4"',
@@ -464,8 +442,9 @@ protected function _registerPaymentReversal()
$this->getRequestData('txn_id')
);
$this->_order->setStatus($orderStatus);
- $this->_order->save();
- $this->_order->addStatusHistoryComment($message, $orderStatus)->setIsCustomerNotified(false)->save();
+ $this->_order->addStatusHistoryComment($message, $orderStatus)
+ ->setIsCustomerNotified(false)
+ ->save();
}
/**
@@ -478,17 +457,12 @@ protected function _registerPaymentRefund()
$this->_importPaymentInformation();
$reason = $this->getRequestData('reason_code');
$isRefundFinal = !$this->_paypalInfo->isReversalDisputable($reason);
- $payment = $this->_order->getPayment()->setPreparedMessage(
- $this->_createIpnComment($this->_paypalInfo->explainReasonCode($reason))
- )->setTransactionId(
- $this->getRequestData('txn_id')
- )->setParentTransactionId(
- $this->getRequestData('parent_txn_id')
- )->setIsTransactionClosed(
- $isRefundFinal
- )->registerRefundNotification(
- -1 * $this->getRequestData('mc_gross')
- );
+ $payment = $this->_order->getPayment()
+ ->setPreparedMessage($this->_createIpnComment($this->_paypalInfo->explainReasonCode($reason)))
+ ->setTransactionId($this->getRequestData('txn_id'))
+ ->setParentTransactionId($this->getRequestData('parent_txn_id'))
+ ->setIsTransactionClosed($isRefundFinal)
+ ->registerRefundNotification(-1 * $this->getRequestData('mc_gross'));
$this->_order->save();
// TODO: there is no way to close a capture right now
@@ -498,9 +472,9 @@ protected function _registerPaymentRefund()
$this->creditmemoSender->send($creditMemo);
$this->_order->addStatusHistoryComment(
__('You notified customer about creditmemo #%1.', $creditMemo->getIncrementId())
- )->setIsCustomerNotified(
- true
- )->save();
+ )
+ ->setIsCustomerNotified(true)
+ ->save();
}
}
@@ -513,19 +487,14 @@ protected function _registerPaymentVoid()
{
$this->_importPaymentInformation();
- $parentTxnId = $this->getRequestData(
- 'transaction_entity'
- ) == 'auth' ? $this->getRequestData(
- 'txn_id'
- ) : $this->getRequestData(
- 'parent_txn_id'
- );
+ $parentTxnId = $this->getRequestData('transaction_entity') == 'auth'
+ ? $this->getRequestData('txn_id')
+ : $this->getRequestData('parent_txn_id');
- $this->_order->getPayment()->setPreparedMessage(
- $this->_createIpnComment('')
- )->setParentTransactionId(
- $parentTxnId
- )->registerVoidNotification();
+ $this->_order->getPayment()
+ ->setPreparedMessage($this->_createIpnComment(''))
+ ->setParentTransactionId($parentTxnId)
+ ->registerVoidNotification();
$this->_order->save();
}
@@ -546,14 +515,14 @@ protected function _importPaymentInformation()
// collect basic information
$from = [];
foreach ([
- Info::PAYER_ID,
- 'payer_email' => Info::PAYER_EMAIL,
- Info::PAYER_STATUS,
- Info::ADDRESS_STATUS,
- Info::PROTECTION_EL,
- Info::PAYMENT_STATUS,
- Info::PENDING_REASON,
- ] as $privateKey => $publicKey) {
+ Info::PAYER_ID,
+ 'payer_email' => Info::PAYER_EMAIL,
+ Info::PAYER_STATUS,
+ Info::ADDRESS_STATUS,
+ Info::PROTECTION_EL,
+ Info::PAYMENT_STATUS,
+ Info::PENDING_REASON,
+ ] as $privateKey => $publicKey) {
if (is_int($privateKey)) {
$privateKey = $publicKey;
}
diff --git a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php
index 2aaf0f30fe71d..f3720960ca6e5 100644
--- a/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php
+++ b/app/code/Magento/Persistent/Observer/CheckExpirePersistentQuoteObserver.php
@@ -50,6 +50,20 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface
*/
protected $_persistentData = null;
+ /**
+ * Request
+ *
+ * @var \Magento\Framework\App\RequestInterface
+ */
+ private $request;
+
+ /**
+ * Checkout Page path
+ *
+ * @var string
+ */
+ private $checkoutPagePath = 'checkout';
+
/**
* @param \Magento\Persistent\Helper\Session $persistentSession
* @param \Magento\Persistent\Helper\Data $persistentData
@@ -57,6 +71,7 @@ class CheckExpirePersistentQuoteObserver implements ObserverInterface
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param \Magento\Customer\Model\Session $customerSession
* @param \Magento\Checkout\Model\Session $checkoutSession
+ * @param \Magento\Framework\App\RequestInterface $request
*/
public function __construct(
\Magento\Persistent\Helper\Session $persistentSession,
@@ -64,7 +79,8 @@ public function __construct(
\Magento\Persistent\Model\QuoteManager $quoteManager,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Customer\Model\Session $customerSession,
- \Magento\Checkout\Model\Session $checkoutSession
+ \Magento\Checkout\Model\Session $checkoutSession,
+ \Magento\Framework\App\RequestInterface $request
) {
$this->_persistentSession = $persistentSession;
$this->quoteManager = $quoteManager;
@@ -72,6 +88,7 @@ public function __construct(
$this->_checkoutSession = $checkoutSession;
$this->_eventManager = $eventManager;
$this->_persistentData = $persistentData;
+ $this->request = $request;
}
/**
@@ -90,12 +107,32 @@ public function execute(\Magento\Framework\Event\Observer $observer)
!$this->_persistentSession->isPersistent() &&
!$this->_customerSession->isLoggedIn() &&
$this->_checkoutSession->getQuoteId() &&
- !$observer->getControllerAction() instanceof \Magento\Checkout\Controller\Onepage
- // persistent session does not expire on onepage checkout page to not spoil customer group id
+ !$this->isRequestFromCheckoutPage($this->request)
+ // persistent session does not expire on onepage checkout page
) {
$this->_eventManager->dispatch('persistent_session_expired');
$this->quoteManager->expire();
$this->_customerSession->setCustomerId(null)->setCustomerGroupId(null);
}
}
+
+ /**
+ * Check current request is coming from onepage checkout page.
+ *
+ * @param \Magento\Framework\App\RequestInterface $request
+ * @return bool
+ */
+ private function isRequestFromCheckoutPage(\Magento\Framework\App\RequestInterface $request): bool
+ {
+ $requestUri = (string)$request->getRequestUri();
+ $refererUri = (string)$request->getServer('HTTP_REFERER');
+
+ /** @var bool $isCheckoutPage */
+ $isCheckoutPage = (
+ false !== strpos($requestUri, $this->checkoutPagePath) ||
+ false !== strpos($refererUri, $this->checkoutPagePath)
+ );
+
+ return $isCheckoutPage;
+ }
}
diff --git a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php
index a52e22a960e0b..8cad0b9f2dd89 100644
--- a/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php
+++ b/app/code/Magento/Persistent/Test/Unit/Observer/CheckExpirePersistentQuoteObserverTest.php
@@ -49,24 +49,39 @@ class CheckExpirePersistentQuoteObserverTest extends \PHPUnit\Framework\TestCase
*/
protected $eventManagerMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\RequestInterface
+ */
+ private $requestMock;
+
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$this->sessionMock = $this->createMock(\Magento\Persistent\Helper\Session::class);
$this->customerSessionMock = $this->createMock(\Magento\Customer\Model\Session::class);
$this->persistentHelperMock = $this->createMock(\Magento\Persistent\Helper\Data::class);
- $this->observerMock
- = $this->createPartialMock(\Magento\Framework\Event\Observer::class, ['getControllerAction',
- '__wakeUp']);
+ $this->observerMock = $this->createPartialMock(
+ \Magento\Framework\Event\Observer::class,
+ ['getControllerAction','__wakeUp']
+ );
$this->quoteManagerMock = $this->createMock(\Magento\Persistent\Model\QuoteManager::class);
$this->eventManagerMock = $this->createMock(\Magento\Framework\Event\ManagerInterface::class);
$this->checkoutSessionMock = $this->createMock(\Magento\Checkout\Model\Session::class);
+ $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getRequestUri', 'getServer'])
+ ->getMockForAbstractClass();
+
$this->model = new \Magento\Persistent\Observer\CheckExpirePersistentQuoteObserver(
$this->sessionMock,
$this->persistentHelperMock,
$this->quoteManagerMock,
$this->eventManagerMock,
$this->customerSessionMock,
- $this->checkoutSessionMock
+ $this->checkoutSessionMock,
+ $this->requestMock
);
}
@@ -76,7 +91,7 @@ public function testExecuteWhenCanNotApplyPersistentData()
->expects($this->once())
->method('canProcess')
->with($this->observerMock)
- ->will($this->returnValue(false));
+ ->willReturn(false);
$this->persistentHelperMock->expects($this->never())->method('isEnabled');
$this->model->execute($this->observerMock);
}
@@ -87,31 +102,97 @@ public function testExecuteWhenPersistentIsNotEnabled()
->expects($this->once())
->method('canProcess')
->with($this->observerMock)
- ->will($this->returnValue(true));
- $this->persistentHelperMock->expects($this->once())->method('isEnabled')->will($this->returnValue(false));
+ ->willReturn(true);
+ $this->persistentHelperMock->expects($this->once())->method('isEnabled')->willReturn(false);
$this->eventManagerMock->expects($this->never())->method('dispatch');
$this->model->execute($this->observerMock);
}
- public function testExecuteWhenPersistentIsEnabled()
- {
+ /**
+ * Test method \Magento\Persistent\Observer\CheckExpirePersistentQuoteObserver::execute when persistent is enabled.
+ *
+ * @param string $refererUri
+ * @param string $requestUri
+ * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expireCounter
+ * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $dispatchCounter
+ * @param \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCustomerIdCounter
+ * @return void
+ * @dataProvider requestDataProvider
+ */
+ public function testExecuteWhenPersistentIsEnabled(
+ string $refererUri,
+ string $requestUri,
+ \PHPUnit_Framework_MockObject_Matcher_InvokedCount $expireCounter,
+ \PHPUnit_Framework_MockObject_Matcher_InvokedCount $dispatchCounter,
+ \PHPUnit_Framework_MockObject_Matcher_InvokedCount $setCustomerIdCounter
+ ): void {
$this->persistentHelperMock
->expects($this->once())
->method('canProcess')
->with($this->observerMock)
- ->will($this->returnValue(true));
- $this->persistentHelperMock->expects($this->once())->method('isEnabled')->will($this->returnValue(true));
- $this->sessionMock->expects($this->once())->method('isPersistent')->will($this->returnValue(false));
- $this->customerSessionMock->expects($this->once())->method('isLoggedIn')->will($this->returnValue(false));
- $this->checkoutSessionMock->expects($this->once())->method('getQuoteId')->will($this->returnValue(10));
- $this->observerMock->expects($this->once())->method('getControllerAction');
- $this->eventManagerMock->expects($this->once())->method('dispatch');
- $this->quoteManagerMock->expects($this->once())->method('expire');
+ ->willReturn(true);
+ $this->persistentHelperMock->expects($this->once())->method('isEnabled')->willReturn(true);
+ $this->sessionMock->expects($this->once())->method('isPersistent')->willReturn(false);
$this->customerSessionMock
- ->expects($this->once())
+ ->expects($this->atLeastOnce())
+ ->method('isLoggedIn')
+ ->willReturn(false);
+ $this->checkoutSessionMock
+ ->expects($this->atLeastOnce())
+ ->method('getQuoteId')
+ ->willReturn(10);
+ $this->eventManagerMock->expects($dispatchCounter)->method('dispatch');
+ $this->quoteManagerMock->expects($expireCounter)->method('expire');
+ $this->customerSessionMock
+ ->expects($setCustomerIdCounter)
->method('setCustomerId')
->with(null)
- ->will($this->returnSelf());
+ ->willReturnSelf();
+ $this->requestMock->expects($this->atLeastOnce())->method('getRequestUri')->willReturn($refererUri);
+ $this->requestMock
+ ->expects($this->atLeastOnce())
+ ->method('getServer')
+ ->with('HTTP_REFERER')
+ ->willReturn($requestUri);
$this->model->execute($this->observerMock);
}
+
+ /**
+ * Request Data Provider
+ *
+ * @return array
+ */
+ public function requestDataProvider()
+ {
+ return [
+ [
+ 'refererUri' => 'checkout',
+ 'requestUri' => 'index',
+ 'expireCounter' => $this->never(),
+ 'dispatchCounter' => $this->never(),
+ 'setCustomerIdCounter' => $this->never(),
+ ],
+ [
+ 'refererUri' => 'checkout',
+ 'requestUri' => 'checkout',
+ 'expireCounter' => $this->never(),
+ 'dispatchCounter' => $this->never(),
+ 'setCustomerIdCounter' => $this->never(),
+ ],
+ [
+ 'refererUri' => 'index',
+ 'requestUri' => 'checkout',
+ 'expireCounter' => $this->never(),
+ 'dispatchCounter' => $this->never(),
+ 'setCustomerIdCounter' => $this->never(),
+ ],
+ [
+ 'refererUri' => 'index',
+ 'requestUri' => 'index',
+ 'expireCounter' => $this->once(),
+ 'dispatchCounter' => $this->once(),
+ 'setCustomerIdCounter' => $this->once(),
+ ],
+ ];
+ }
}
diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php
index ee12b459118c1..d38e58d7341c1 100644
--- a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php
+++ b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php
@@ -35,7 +35,7 @@ public function __construct(StatusResolver $statusResolver = null)
*/
public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order)
{
- $state = Order::STATE_PROCESSING;
+ $state = $order->getState() ?: Order::STATE_PROCESSING;
$status = null;
$message = 'Registered notification about captured amount of %1.';
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php
index 32ea9d8869344..1b762fafe0b71 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php
@@ -32,26 +32,29 @@ class RegisterCaptureNotificationCommandTest extends \PHPUnit\Framework\TestCase
*
* @param bool $isTransactionPending
* @param bool $isFraudDetected
+ * @param string|null $currentState
* @param string $expectedState
* @param string $expectedStatus
* @param string $expectedMessage
- *
+ * @return void
* @dataProvider commandResultDataProvider
*/
public function testExecute(
- $isTransactionPending,
- $isFraudDetected,
- $expectedState,
- $expectedStatus,
- $expectedMessage
- ) {
+ bool $isTransactionPending,
+ bool $isFraudDetected,
+ $currentState,
+ string $expectedState,
+ string $expectedStatus,
+ string $expectedMessage
+ ): void {
+ $order = $this->getOrder($currentState);
$actualReturn = (new RegisterCaptureNotificationCommand($this->getStatusResolver()))->execute(
$this->getPayment($isTransactionPending, $isFraudDetected),
$this->amount,
- $this->getOrder()
+ $order
);
- $this->assertOrderStateAndStatus($this->getOrder(), $expectedState, $expectedStatus);
+ $this->assertOrderStateAndStatus($order, $expectedState, $expectedStatus);
self::assertEquals(__($expectedMessage, $this->amount), $actualReturn);
}
@@ -64,30 +67,42 @@ public function commandResultDataProvider()
[
false,
false,
+ Order::STATE_COMPLETE,
+ Order::STATE_COMPLETE,
+ $this->newOrderStatus,
+ 'Registered notification about captured amount of %1.',
+ ],
+ [
+ false,
+ false,
+ null,
Order::STATE_PROCESSING,
$this->newOrderStatus,
- 'Registered notification about captured amount of %1.'
+ 'Registered notification about captured amount of %1.',
],
[
true,
false,
+ Order::STATE_PROCESSING,
Order::STATE_PAYMENT_REVIEW,
$this->newOrderStatus,
- 'An amount of %1 will be captured after being approved at the payment gateway.'
+ 'An amount of %1 will be captured after being approved at the payment gateway.',
],
[
false,
true,
+ Order::STATE_PROCESSING,
Order::STATE_PAYMENT_REVIEW,
Order::STATUS_FRAUD,
- 'Order is suspended as its capture amount %1 is suspected to be fraudulent.'
+ 'Order is suspended as its capture amount %1 is suspected to be fraudulent.',
],
[
true,
true,
+ Order::STATE_PROCESSING,
Order::STATE_PAYMENT_REVIEW,
Order::STATUS_FRAUD,
- 'Order is suspended as its capture amount %1 is suspected to be fraudulent.'
+ 'Order is suspended as its capture amount %1 is suspected to be fraudulent.',
],
];
}
@@ -107,15 +122,19 @@ private function getStatusResolver()
}
/**
+ * @param string|null $state
* @return Order|MockObject
*/
- private function getOrder()
+ private function getOrder($state)
{
+ /** @var Order|MockObject $order */
$order = $this->getMockBuilder(Order::class)
->disableOriginalConstructor()
+ ->setMethods(['getBaseCurrency', 'getOrderStatusByState'])
->getMock();
$order->method('getBaseCurrency')
->willReturn($this->getCurrency());
+ $order->setState($state);
return $order;
}
@@ -159,7 +178,7 @@ private function getCurrency()
*/
private function assertOrderStateAndStatus($order, $expectedState, $expectedStatus)
{
- $order->method('setState')->with($expectedState);
- $order->method('setStatus')->with($expectedStatus);
+ self::assertEquals($expectedState, $order->getState(), 'The order {state} should match.');
+ self::assertEquals($expectedStatus, $order->getStatus(), 'The order {status} should match.');
}
}
diff --git a/app/code/Magento/SalesRule/Model/Quote/ChildrenValidationLocator.php b/app/code/Magento/SalesRule/Model/Quote/ChildrenValidationLocator.php
new file mode 100644
index 0000000000000..af1f61c187129
--- /dev/null
+++ b/app/code/Magento/SalesRule/Model/Quote/ChildrenValidationLocator.php
@@ -0,0 +1,53 @@
+
+ * [
+ * 'ProductType1' => true,
+ * 'ProductType2' => false
+ * ]
+ *
+ */
+ public function __construct(
+ array $productTypeChildrenValidationMap = []
+ ) {
+ $this->productTypeChildrenValidationMap = $productTypeChildrenValidationMap;
+ }
+
+ /**
+ * Checks necessity to validate rule on item's children.
+ *
+ * @param QuoteItem $item
+ * @return bool
+ */
+ public function isChildrenValidationRequired(QuoteItem $item): bool
+ {
+ $type = $item->getProduct()->getTypeId();
+ if (isset($this->productTypeChildrenValidationMap[$type])) {
+ return (bool)$this->productTypeChildrenValidationMap[$type];
+ }
+
+ return true;
+ }
+}
diff --git a/app/code/Magento/SalesRule/Model/RulesApplier.php b/app/code/Magento/SalesRule/Model/RulesApplier.php
index 06a4e252bf60e..f771a4f1e3892 100644
--- a/app/code/Magento/SalesRule/Model/RulesApplier.php
+++ b/app/code/Magento/SalesRule/Model/RulesApplier.php
@@ -6,6 +6,9 @@
namespace Magento\SalesRule\Model;
use Magento\Quote\Model\Quote\Address;
+use Magento\SalesRule\Model\Quote\ChildrenValidationLocator;
+use Magento\Framework\App\ObjectManager;
+use Magento\SalesRule\Model\Rule\Action\Discount\CalculatorFactory;
/**
* Class RulesApplier
@@ -25,19 +28,33 @@ class RulesApplier
*/
protected $validatorUtility;
+ /**
+ * @var ChildrenValidationLocator
+ */
+ private $childrenValidationLocator;
+
+ /**
+ * @var CalculatorFactory
+ */
+ private $calculatorFactory;
+
/**
* @param \Magento\SalesRule\Model\Rule\Action\Discount\CalculatorFactory $calculatorFactory
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param \Magento\SalesRule\Model\Utility $utility
+ * @param ChildrenValidationLocator|null $childrenValidationLocator
*/
public function __construct(
\Magento\SalesRule\Model\Rule\Action\Discount\CalculatorFactory $calculatorFactory,
\Magento\Framework\Event\ManagerInterface $eventManager,
- \Magento\SalesRule\Model\Utility $utility
+ \Magento\SalesRule\Model\Utility $utility,
+ ChildrenValidationLocator $childrenValidationLocator = null
) {
$this->calculatorFactory = $calculatorFactory;
$this->validatorUtility = $utility;
$this->_eventManager = $eventManager;
+ $this->childrenValidationLocator = $childrenValidationLocator
+ ?: ObjectManager::getInstance()->get(ChildrenValidationLocator::class);
}
/**
@@ -61,6 +78,9 @@ public function applyRules($item, $rules, $skipValidation, $couponCode)
}
if (!$skipValidation && !$rule->getActions()->validate($item)) {
+ if (!$this->childrenValidationLocator->isChildrenValidationRequired($item)) {
+ continue;
+ }
$childItems = $item->getChildren();
$isContinue = true;
if (!empty($childItems)) {
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/ChildrenValidationLocatorTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/ChildrenValidationLocatorTest.php
new file mode 100644
index 0000000000000..abb8d791d74c4
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/ChildrenValidationLocatorTest.php
@@ -0,0 +1,104 @@
+objectManager = new ObjectManager($this);
+
+ $this->productTypeChildrenValidationMap = [
+ 'type1' => true,
+ 'type2' => false,
+ ];
+
+ $this->quoteItemMock = $this->getMockBuilder(QuoteItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getProduct'])
+ ->getMockForAbstractClass();
+
+ $this->productMock = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getTypeId'])
+ ->getMock();
+
+ $this->model = $this->objectManager->getObject(
+ ChildrenValidationLocator::class,
+ [
+ 'productTypeChildrenValidationMap' => $this->productTypeChildrenValidationMap,
+ ]
+ );
+ }
+
+ /**
+ * @dataProvider productTypeDataProvider
+ * @param string $type
+ * @param bool $expected
+ *
+ * @return void
+ */
+ public function testIsChildrenValidationRequired(string $type, bool $expected): void
+ {
+ $this->quoteItemMock->expects($this->once())
+ ->method('getProduct')
+ ->willReturn($this->productMock);
+
+ $this->productMock->expects($this->once())
+ ->method('getTypeId')
+ ->willReturn($type);
+
+ $this->assertEquals($this->model->isChildrenValidationRequired($this->quoteItemMock), $expected);
+ }
+
+ /**
+ * @return array
+ */
+ public function productTypeDataProvider(): array
+ {
+ return [
+ ['type1', true],
+ ['type2', false],
+ ['type3', true],
+ ];
+ }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/RulesApplierTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/RulesApplierTest.php
index 814048c2ac1d0..37c839d413d4b 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/RulesApplierTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/RulesApplierTest.php
@@ -6,6 +6,9 @@
namespace Magento\SalesRule\Test\Unit\Model;
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class RulesApplierTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -28,6 +31,11 @@ class RulesApplierTest extends \PHPUnit\Framework\TestCase
*/
protected $validatorUtility;
+ /**
+ * @var \Magento\SalesRule\Model\Quote\ChildrenValidationLocator|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $childrenValidationLocator;
+
protected function setUp()
{
$this->calculatorFactory = $this->createMock(
@@ -38,11 +46,15 @@ protected function setUp()
\Magento\SalesRule\Model\Utility::class,
['canProcessRule', 'minFix', 'deltaRoundingFix', 'getItemQty']
);
-
+ $this->childrenValidationLocator = $this->createPartialMock(
+ \Magento\SalesRule\Model\Quote\ChildrenValidationLocator::class,
+ ['isChildrenValidationRequired']
+ );
$this->rulesApplier = new \Magento\SalesRule\Model\RulesApplier(
$this->calculatorFactory,
$this->eventManager,
- $this->validatorUtility
+ $this->validatorUtility,
+ $this->childrenValidationLocator
);
}
@@ -84,6 +96,10 @@ public function testApplyRulesWhenRuleWithStopRulesProcessingIsUsed($isChildren,
$item->setDiscountCalculationPrice($positivePrice);
$item->setData('calculation_price', $positivePrice);
+ $this->childrenValidationLocator->expects($this->any())
+ ->method('isChildrenValidationRequired')
+ ->willReturn(true);
+
$this->validatorUtility->expects($this->atLeastOnce())
->method('canProcessRule')
->will($this->returnValue(true));
diff --git a/app/code/Magento/Signifyd/Block/Fingerprint.php b/app/code/Magento/Signifyd/Block/Fingerprint.php
index 7afa092b3d0da..db76fc6c94468 100644
--- a/app/code/Magento/Signifyd/Block/Fingerprint.php
+++ b/app/code/Magento/Signifyd/Block/Fingerprint.php
@@ -85,6 +85,8 @@ public function getSignifydOrderSessionId()
*/
public function isModuleActive()
{
- return $this->config->isActive();
+ $storeId = $this->quoteSession->getQuote()->getStoreId();
+
+ return $this->config->isActive($storeId);
}
}
diff --git a/app/code/Magento/Signifyd/Model/Config.php b/app/code/Magento/Signifyd/Model/Config.php
index b68380ee15bf3..15d3608bd38c4 100644
--- a/app/code/Magento/Signifyd/Model/Config.php
+++ b/app/code/Magento/Signifyd/Model/Config.php
@@ -34,13 +34,15 @@ public function __construct(ScopeConfigInterface $scopeConfig)
* If this config option set to false no Signifyd integration should be available
* (only possibility to configure Signifyd setting in admin)
*
+ * @param int|null $storeId
* @return bool
*/
- public function isActive()
+ public function isActive($storeId = null): bool
{
$enabled = $this->scopeConfig->isSetFlag(
'fraud_protection/signifyd/active',
- ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE,
+ $storeId
);
return $enabled;
}
@@ -51,13 +53,15 @@ public function isActive()
* @see https://www.signifyd.com/docs/api/#/introduction/authentication
* @see https://app.signifyd.com/settings
*
+ * @param int|null $storeId
* @return string
*/
- public function getApiKey()
+ public function getApiKey($storeId = null): string
{
$apiKey = $this->scopeConfig->getValue(
'fraud_protection/signifyd/api_key',
- ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE,
+ $storeId
);
return $apiKey;
}
@@ -66,13 +70,15 @@ public function getApiKey()
* Base URL to Signifyd REST API.
* Usually equals to https://api.signifyd.com/v2 and should not be changed
*
+ * @param int|null $storeId
* @return string
*/
- public function getApiUrl()
+ public function getApiUrl($storeId = null): string
{
$apiUrl = $this->scopeConfig->getValue(
'fraud_protection/signifyd/api_url',
- ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE,
+ $storeId
);
return $apiUrl;
}
@@ -80,13 +86,15 @@ public function getApiUrl()
/**
* If is "true" extra information about interaction with Signifyd API are written to debug.log file
*
+ * @param int|null $storeId
* @return bool
*/
- public function isDebugModeEnabled()
+ public function isDebugModeEnabled($storeId = null): bool
{
$debugModeEnabled = $this->scopeConfig->isSetFlag(
'fraud_protection/signifyd/debug',
- ScopeInterface::SCOPE_STORE
+ ScopeInterface::SCOPE_STORE,
+ $storeId
);
return $debugModeEnabled;
}
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php
index 0950ca1e22cfa..2d6d57a510ae3 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/ApiClient.php
@@ -36,12 +36,13 @@ public function __construct(
*
* @param string $url
* @param string $method
- * @param array $params
+ * @param array $params
+ * @param int|null $storeId
* @return array
*/
- public function makeApiCall($url, $method, array $params = [])
+ public function makeApiCall($url, $method, array $params = [], $storeId = null): array
{
- $result = $this->requestBuilder->doRequest($url, $method, $params);
+ $result = $this->requestBuilder->doRequest($url, $method, $params, $storeId);
return $result;
}
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php
index 41006bd7d1e0e..2a9b933b98b5d 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/HttpClientFactory.php
@@ -73,12 +73,13 @@ public function __construct(
* @param string $url
* @param string $method
* @param array $params
+ * @param int|null $storeId
* @return ZendClient
*/
- public function create($url, $method, array $params = [])
+ public function create($url, $method, array $params = [], $storeId = null): ZendClient
{
- $apiKey = $this->getApiKey();
- $apiUrl = $this->buildFullApiUrl($url);
+ $apiKey = $this->getApiKey($storeId);
+ $apiUrl = $this->buildFullApiUrl($url, $storeId);
$client = $this->createNewClient();
$client->setHeaders(
@@ -107,22 +108,24 @@ private function createNewClient()
* Signifyd API key for merchant account.
*
* @see https://www.signifyd.com/docs/api/#/introduction/authentication
+ * @param int|null $storeId
* @return string
*/
- private function getApiKey()
+ private function getApiKey($storeId): string
{
- return $this->config->getApiKey();
+ return $this->config->getApiKey($storeId);
}
/**
* Full URL for Singifyd API based on relative URL.
*
* @param string $url
+ * @param int|null $storeId
* @return string
*/
- private function buildFullApiUrl($url)
+ private function buildFullApiUrl($url, $storeId): string
{
- $baseApiUrl = $this->getBaseApiUrl();
+ $baseApiUrl = $this->getBaseApiUrl($storeId);
$fullUrl = $baseApiUrl . self::$urlSeparator . ltrim($url, self::$urlSeparator);
return $fullUrl;
@@ -131,11 +134,12 @@ private function buildFullApiUrl($url)
/**
* Base Sigifyd API URL without trailing slash.
*
+ * @param int|null $storeId
* @return string
*/
- private function getBaseApiUrl()
+ private function getBaseApiUrl($storeId): string
{
- $baseApiUrl = $this->config->getApiUrl();
+ $baseApiUrl = $this->config->getApiUrl($storeId);
return rtrim($baseApiUrl, self::$urlSeparator);
}
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php
index 2ab4395e1990d..ee079a74d345f 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestBuilder.php
@@ -5,8 +5,6 @@
*/
namespace Magento\Signifyd\Model\SignifydGateway\Client;
-use Magento\Framework\HTTP\ZendClient;
-
/**
* Class RequestBuilder
* Creates HTTP client, sends request to Signifyd and handles response
@@ -50,13 +48,14 @@ public function __construct(
*
* @param string $url
* @param string $method
- * @param array $params
+ * @param array $params
+ * @param int|null $storeId
* @return array
*/
- public function doRequest($url, $method, array $params = [])
+ public function doRequest($url, $method, array $params = [], $storeId = null): array
{
- $client = $this->clientCreator->create($url, $method, $params);
- $response = $this->requestSender->send($client);
+ $client = $this->clientCreator->create($url, $method, $params, $storeId);
+ $response = $this->requestSender->send($client, $storeId);
$result = $this->responseHandler->handle($response);
return $result;
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php
index 38128a799fd59..a63331e055c1c 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Client/RequestSender.php
@@ -39,15 +39,16 @@ public function __construct(
* debug information is recorded to debug.log.
*
* @param ZendClient $client
+ * @param int|null $storeId
* @return \Zend_Http_Response
* @throws ApiCallException
*/
- public function send(ZendClient $client)
+ public function send(ZendClient $client, $storeId = null): \Zend_Http_Response
{
try {
$response = $client->request();
- $this->debuggerFactory->create()->success(
+ $this->debuggerFactory->create($storeId)->success(
$client->getUri(true),
$client->getLastRequest(),
$response->getStatus() . ' ' . $response->getMessage(),
@@ -56,7 +57,7 @@ public function send(ZendClient $client)
return $response;
} catch (\Exception $e) {
- $this->debuggerFactory->create()->failure(
+ $this->debuggerFactory->create($storeId)->failure(
$client->getUri(true),
$client->getLastRequest(),
$e
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php
index 19408e99ae02e..1e61a313899cc 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Debugger/DebuggerFactory.php
@@ -44,11 +44,12 @@ public function __construct(
/**
* Create debugger instance
*
+ * @param int|null $storeId
* @return DebuggerInterface
*/
- public function create()
+ public function create($storeId = null): DebuggerInterface
{
- if (!$this->config->isDebugModeEnabled()) {
+ if (!$this->config->isDebugModeEnabled($storeId)) {
return $this->objectManager->get(BlackHole::class);
}
diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php
index ddcaa6cd696f2..9f7a053c58724 100644
--- a/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php
+++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Gateway.php
@@ -5,8 +5,9 @@
*/
namespace Magento\Signifyd\Model\SignifydGateway;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Signifyd\Api\CaseRepositoryInterface;
use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface;
-use Magento\Signifyd\Model\SignifydGateway\ApiClient;
/**
* Signifyd Gateway.
@@ -53,18 +54,34 @@ class Gateway
*/
private $apiClient;
+ /**
+ * @var OrderRepositoryInterface
+ */
+ private $orderRepository;
+
+ /**
+ * @var CaseRepositoryInterface
+ */
+ private $caseRepository;
+
/**
* Gateway constructor.
*
* @param CreateCaseBuilderInterface $createCaseBuilder
* @param ApiClient $apiClient
+ * @param OrderRepositoryInterface $orderRepository
+ * @param CaseRepositoryInterface $caseRepository
*/
public function __construct(
CreateCaseBuilderInterface $createCaseBuilder,
- ApiClient $apiClient
+ ApiClient $apiClient,
+ OrderRepositoryInterface $orderRepository,
+ CaseRepositoryInterface $caseRepository
) {
$this->createCaseBuilder = $createCaseBuilder;
$this->apiClient = $apiClient;
+ $this->orderRepository = $orderRepository;
+ $this->caseRepository = $caseRepository;
}
/**
@@ -78,11 +95,13 @@ public function __construct(
public function createCase($orderId)
{
$caseParams = $this->createCaseBuilder->build($orderId);
+ $storeId = $this->getStoreIdFromOrder($orderId);
$caseCreationResult = $this->apiClient->makeApiCall(
'/cases',
'POST',
- $caseParams
+ $caseParams,
+ $storeId
);
if (!isset($caseCreationResult['investigationId'])) {
@@ -102,12 +121,14 @@ public function createCase($orderId)
*/
public function submitCaseForGuarantee($signifydCaseId)
{
+ $storeId = $this->getStoreIdFromCase($signifydCaseId);
$guaranteeCreationResult = $this->apiClient->makeApiCall(
'/guarantees',
'POST',
[
'caseId' => $signifydCaseId,
- ]
+ ],
+ $storeId
);
$disposition = $this->processDispositionResult($guaranteeCreationResult);
@@ -124,12 +145,14 @@ public function submitCaseForGuarantee($signifydCaseId)
*/
public function cancelGuarantee($caseId)
{
+ $storeId = $this->getStoreIdFromCase($caseId);
$result = $this->apiClient->makeApiCall(
'/cases/' . $caseId . '/guarantee',
'PUT',
[
'guaranteeDisposition' => self::GUARANTEE_CANCELED
- ]
+ ],
+ $storeId
);
$disposition = $this->processDispositionResult($result);
@@ -172,4 +195,31 @@ private function processDispositionResult(array $result)
return $disposition;
}
+
+ /**
+ * Returns store id by case.
+ *
+ * @param int $caseId
+ * @return int|null
+ */
+ private function getStoreIdFromCase(int $caseId)
+ {
+ $case = $this->caseRepository->getByCaseId($caseId);
+ $orderId = $case->getOrderId();
+
+ return $this->getStoreIdFromOrder($orderId);
+ }
+
+ /**
+ * Returns store id from order.
+ *
+ * @param int $orderId
+ * @return int|null
+ */
+ private function getStoreIdFromOrder(int $orderId)
+ {
+ $order = $this->orderRepository->get($orderId);
+
+ return $order->getStoreId();
+ }
}
diff --git a/app/code/Magento/Signifyd/Observer/PlaceOrder.php b/app/code/Magento/Signifyd/Observer/PlaceOrder.php
index 3798522dbe506..8415bc006b8aa 100644
--- a/app/code/Magento/Signifyd/Observer/PlaceOrder.php
+++ b/app/code/Magento/Signifyd/Observer/PlaceOrder.php
@@ -55,10 +55,6 @@ public function __construct(
*/
public function execute(Observer $observer)
{
- if (!$this->signifydIntegrationConfig->isActive()) {
- return;
- }
-
$orders = $this->extractOrders(
$observer->getEvent()
);
@@ -68,7 +64,10 @@ public function execute(Observer $observer)
}
foreach ($orders as $order) {
- $this->createCaseForOrder($order);
+ $storeId = $order->getStoreId();
+ if ($this->signifydIntegrationConfig->isActive($storeId)) {
+ $this->createCaseForOrder($order);
+ }
}
}
diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php
index 776e8a75b9646..4aefd63355773 100644
--- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php
+++ b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/Client/HttpClientFactoryTest.php
@@ -101,14 +101,17 @@ public function testCreateHttpClient()
public function testCreateWithParams()
{
$param = ['id' => 1];
+ $storeId = 1;
$json = '{"id":1}';
$this->config->expects($this->once())
->method('getApiKey')
+ ->with($storeId)
->willReturn('testKey');
$this->config->expects($this->once())
->method('getApiUrl')
+ ->with($storeId)
->willReturn(self::$dummy);
$this->dataEncoder->expects($this->once())
@@ -121,7 +124,7 @@ public function testCreateWithParams()
->with($this->equalTo($json), 'application/json')
->willReturnSelf();
- $client = $this->httpClient->create('url', 'method', $param);
+ $client = $this->httpClient->create('url', 'method', $param, $storeId);
$this->assertInstanceOf(ZendClient::class, $client);
}
diff --git a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php
index c34e64f469f77..2a05189e0e393 100644
--- a/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php
+++ b/app/code/Magento/Signifyd/Test/Unit/Model/SignifydGateway/GatewayTest.php
@@ -5,7 +5,10 @@
*/
namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway;
-use \PHPUnit\Framework\TestCase as TestCase;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Signifyd\Api\CaseRepositoryInterface;
+use Magento\Signifyd\Api\Data\CaseInterface;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
use Magento\Signifyd\Model\SignifydGateway\Gateway;
use Magento\Signifyd\Model\SignifydGateway\GatewayException;
@@ -30,6 +33,16 @@ class GatewayTest extends \PHPUnit\Framework\TestCase
*/
private $gateway;
+ /**
+ * @var OrderRepositoryInterface|MockObject
+ */
+ private $orderRepository;
+
+ /**
+ * @var CaseRepositoryInterface|MockObject
+ */
+ private $caseRepository;
+
public function setUp()
{
$this->createCaseBuilder = $this->getMockBuilder(CreateCaseBuilderInterface::class)
@@ -39,16 +52,27 @@ public function setUp()
->disableOriginalConstructor()
->getMock();
+ $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class)
+ ->getMockForAbstractClass();
+
+ $this->caseRepository= $this->getMockBuilder(CaseRepositoryInterface::class)
+ ->getMockForAbstractClass();
+
$this->gateway = new Gateway(
$this->createCaseBuilder,
- $this->apiClient
+ $this->apiClient,
+ $this->orderRepository,
+ $this->caseRepository
);
}
public function testCreateCaseForSpecifiedOrder()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
$dummySignifydInvestigationId = 42;
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willReturn([
@@ -68,7 +92,10 @@ public function testCreateCaseForSpecifiedOrder()
public function testCreateCaseCallsValidApiMethod()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
$dummySignifydInvestigationId = 42;
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->createCaseBuilder
->method('build')
->willReturn([]);
@@ -79,7 +106,8 @@ public function testCreateCaseCallsValidApiMethod()
->with(
$this->equalTo('/cases'),
$this->equalTo('POST'),
- $this->isType('array')
+ $this->isType('array'),
+ $this->equalTo($dummyStoreId)
)
->willReturn([
'investigationId' => $dummySignifydInvestigationId
@@ -92,7 +120,10 @@ public function testCreateCaseCallsValidApiMethod()
public function testCreateCaseNormalFlow()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
$dummySignifydInvestigationId = 42;
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->createCaseBuilder
->method('build')
->willReturn([]);
@@ -113,7 +144,10 @@ public function testCreateCaseNormalFlow()
public function testCreateCaseWithFailedApiCall()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
$apiCallFailureMessage = 'Api call failed';
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->createCaseBuilder
->method('build')
->willReturn([]);
@@ -129,6 +163,9 @@ public function testCreateCaseWithFailedApiCall()
public function testCreateCaseWithMissedResponseRequiredData()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->createCaseBuilder
->method('build')
->willReturn([]);
@@ -145,7 +182,10 @@ public function testCreateCaseWithMissedResponseRequiredData()
public function testCreateCaseWithAdditionalResponseData()
{
$dummyOrderId = 1;
+ $dummyStoreId = 2;
$dummySignifydInvestigationId = 42;
+
+ $this->withOrderEntity($dummyOrderId, $dummyStoreId);
$this->createCaseBuilder
->method('build')
->willReturn([]);
@@ -167,8 +207,10 @@ public function testCreateCaseWithAdditionalResponseData()
public function testSubmitCaseForGuaranteeCallsValidApiMethod()
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
$dummyDisposition = 'APPROVED';
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->expects($this->atLeastOnce())
->method('makeApiCall')
@@ -177,7 +219,8 @@ public function testSubmitCaseForGuaranteeCallsValidApiMethod()
$this->equalTo('POST'),
$this->equalTo([
'caseId' => $dummySygnifydCaseId
- ])
+ ]),
+ $this->equalTo($dummyStoreId)
)->willReturn([
'disposition' => $dummyDisposition
]);
@@ -189,8 +232,10 @@ public function testSubmitCaseForGuaranteeCallsValidApiMethod()
public function testSubmitCaseForGuaranteeWithFailedApiCall()
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
$apiCallFailureMessage = 'Api call failed';
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willThrowException(new ApiCallException($apiCallFailureMessage));
@@ -204,10 +249,12 @@ public function testSubmitCaseForGuaranteeWithFailedApiCall()
public function testSubmitCaseForGuaranteeReturnsDisposition()
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
$dummyDisposition = 'APPROVED';
$dummyGuaranteeId = 123;
$dummyRereviewCount = 0;
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willReturn([
@@ -227,9 +274,11 @@ public function testSubmitCaseForGuaranteeReturnsDisposition()
public function testSubmitCaseForGuaranteeWithMissedDisposition()
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
$dummyGuaranteeId = 123;
$dummyRereviewCount = 0;
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willReturn([
@@ -244,8 +293,10 @@ public function testSubmitCaseForGuaranteeWithMissedDisposition()
public function testSubmitCaseForGuaranteeWithUnexpectedDisposition()
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
$dummyUnexpectedDisposition = 'UNEXPECTED';
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willReturn([
@@ -263,7 +314,9 @@ public function testSubmitCaseForGuaranteeWithUnexpectedDisposition()
public function testSubmitCaseForGuaranteeWithExpectedDisposition($dummyExpectedDisposition)
{
$dummySygnifydCaseId = 42;
+ $dummyStoreId = 1;
+ $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
$this->apiClient
->method('makeApiCall')
->willReturn([
@@ -290,11 +343,20 @@ public function testSubmitCaseForGuaranteeWithExpectedDisposition($dummyExpected
public function testCancelGuarantee()
{
$caseId = 123;
+ $dummyStoreId = 1;
+ $this->withCaseEntity($caseId, $dummyStoreId);
$this->apiClient->expects(self::once())
->method('makeApiCall')
- ->with('/cases/' . $caseId . '/guarantee', 'PUT', ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED])
- ->willReturn(['disposition' => Gateway::GUARANTEE_CANCELED]);
+ ->with(
+ '/cases/' . $caseId . '/guarantee',
+ 'PUT',
+ ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED],
+ $dummyStoreId
+ )
+ ->willReturn(
+ ['disposition' => Gateway::GUARANTEE_CANCELED]
+ );
$result = $this->gateway->cancelGuarantee($caseId);
self::assertEquals(Gateway::GUARANTEE_CANCELED, $result);
@@ -310,10 +372,17 @@ public function testCancelGuarantee()
public function testCancelGuaranteeWithUnexpectedDisposition()
{
$caseId = 123;
+ $dummyStoreId = 1;
+ $this->withCaseEntity($caseId, $dummyStoreId);
$this->apiClient->expects(self::once())
->method('makeApiCall')
- ->with('/cases/' . $caseId . '/guarantee', 'PUT', ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED])
+ ->with(
+ '/cases/' . $caseId . '/guarantee',
+ 'PUT',
+ ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED],
+ $dummyStoreId
+ )
->willReturn(['disposition' => Gateway::GUARANTEE_DECLINED]);
$result = $this->gateway->cancelGuarantee($caseId);
@@ -331,4 +400,46 @@ public function supportedGuaranteeDispositionsProvider()
'UNREQUESTED' => ['UNREQUESTED'],
];
}
+
+ /**
+ * Specifies order entity mock execution.
+ *
+ * @param int $orderId
+ * @param int $storeId
+ * @return void
+ */
+ private function withOrderEntity(int $orderId, int $storeId): void
+ {
+ $orderEntity = $this->getMockBuilder(OrderInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $orderEntity->method('getStoreId')
+ ->willReturn($storeId);
+ $this->orderRepository->method('get')
+ ->with($orderId)
+ ->willReturn($orderEntity);
+ }
+
+ /**
+ * Specifies case entity mock execution.
+ *
+ * @param int $caseId
+ * @param int $storeId
+ * @return void
+ */
+ private function withCaseEntity(int $caseId, int $storeId): void
+ {
+ $orderId = 1;
+
+ $caseEntity = $this->getMockBuilder(CaseInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $caseEntity->method('getOrderId')
+ ->willReturn($orderId);
+ $this->caseRepository->method('getByCaseId')
+ ->with($caseId)
+ ->willReturn($caseEntity);
+
+ $this->withOrderEntity($orderId, $storeId);
+ }
}
diff --git a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php
index e2870953ec280..4e7edddf7b948 100644
--- a/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php
+++ b/app/code/Magento/Signifyd/Test/Unit/Observer/PlaceOrderTest.php
@@ -97,7 +97,10 @@ protected function setUp()
*/
public function testExecuteWithDisabledModule()
{
- $this->withActiveSignifydIntegration(false);
+ $orderId = 1;
+ $storeId = 2;
+ $this->withActiveSignifydIntegration(false, $storeId);
+ $this->withOrderEntity($orderId, $storeId);
$this->creationService->expects(self::never())
->method('createForOrder');
@@ -113,7 +116,7 @@ public function testExecuteWithDisabledModule()
public function testExecuteWithoutOrder()
{
$this->withActiveSignifydIntegration(true);
- $this->withOrderEntity(null);
+ $this->withOrderEntity(null, null);
$this->creationService->expects(self::never())
->method('createForOrder');
@@ -129,8 +132,9 @@ public function testExecuteWithoutOrder()
public function testExecuteWithOfflinePayment()
{
$orderId = 1;
- $this->withActiveSignifydIntegration(true);
- $this->withOrderEntity($orderId);
+ $storeId = 2;
+ $this->withActiveSignifydIntegration(true, $storeId);
+ $this->withOrderEntity($orderId, $storeId);
$this->withAvailablePaymentMethod(false);
$this->creationService->expects(self::never())
@@ -147,10 +151,11 @@ public function testExecuteWithOfflinePayment()
public function testExecuteWithFailedCaseCreation()
{
$orderId = 1;
+ $storeId = 2;
$exceptionMessage = __('Case with the same order id already exists.');
- $this->withActiveSignifydIntegration(true);
- $this->withOrderEntity($orderId);
+ $this->withActiveSignifydIntegration(true, $storeId);
+ $this->withOrderEntity($orderId, $storeId);
$this->withAvailablePaymentMethod(true);
$this->creationService->method('createForOrder')
@@ -172,9 +177,10 @@ public function testExecuteWithFailedCaseCreation()
public function testExecute()
{
$orderId = 1;
+ $storeId = 2;
- $this->withActiveSignifydIntegration(true);
- $this->withOrderEntity($orderId);
+ $this->withActiveSignifydIntegration(true, $storeId);
+ $this->withOrderEntity($orderId, $storeId);
$this->withAvailablePaymentMethod(true);
$this->creationService
@@ -190,10 +196,11 @@ public function testExecute()
/**
* Specifies order entity mock execution.
*
- * @param int $orderId
+ * @param int|null $orderId
+ * @param int|null $storeId
* @return void
*/
- private function withOrderEntity($orderId)
+ private function withOrderEntity($orderId, $storeId): void
{
$this->orderEntity = $this->getMockBuilder(OrderInterface::class)
->disableOriginalConstructor()
@@ -201,6 +208,8 @@ private function withOrderEntity($orderId)
$this->orderEntity->method('getEntityId')
->willReturn($orderId);
+ $this->orderEntity->method('getStoreId')
+ ->willReturn($storeId);
$this->observer->method('getEvent')
->willReturn($this->event);
@@ -214,11 +223,13 @@ private function withOrderEntity($orderId)
* Specifies config mock execution.
*
* @param bool $isActive
+ * @param int|null $storeId
* @return void
*/
- private function withActiveSignifydIntegration($isActive)
+ private function withActiveSignifydIntegration(bool $isActive, $storeId = null): void
{
$this->config->method('isActive')
+ ->with($storeId)
->willReturn($isActive);
}
diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js
index af2b31f7eab43..6c9b4b89bec7a 100644
--- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js
+++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js
@@ -336,11 +336,12 @@ define([
* Set z-index and margin for modal and overlay.
*/
_setActive: function () {
- var zIndex = this.modal.zIndex();
+ var zIndex = this.modal.zIndex(),
+ baseIndex = zIndex + this._getVisibleCount();
+ this.overlay.zIndex(++baseIndex);
this.prevOverlayIndex = this.overlay.zIndex();
- this.modal.zIndex(zIndex + this._getVisibleCount());
- this.overlay.zIndex(zIndex + (this._getVisibleCount() - 1));
+ this.modal.zIndex(this.overlay.zIndex() + 1);
if (this._getVisibleSlideCount()) {
this.modal.css('marginLeft', this.options.modalLeftMargin * this._getVisibleSlideCount());
@@ -354,7 +355,14 @@ define([
this.modal.removeAttr('style');
if (this.overlay) {
- this.overlay.zIndex(this.prevOverlayIndex);
+ // In cases when one modal is closed but there is another modal open (e.g. admin notifications)
+ // to avoid collisions between overlay and modal zIndexes
+ // overlay zIndex is set to be less than modal one
+ if (this._getVisibleCount() === 1) {
+ this.overlay.zIndex(this.prevOverlayIndex - 1);
+ } else {
+ this.overlay.zIndex(this.prevOverlayIndex);
+ }
}
},
diff --git a/app/code/Magento/Wishlist/view/frontend/templates/item/list.phtml b/app/code/Magento/Wishlist/view/frontend/templates/item/list.phtml
index c2172afab94de..ef02768c46a83 100644
--- a/app/code/Magento/Wishlist/view/frontend/templates/item/list.phtml
+++ b/app/code/Magento/Wishlist/view/frontend/templates/item/list.phtml
@@ -19,7 +19,7 @@ $columns = $block->getColumns();
- setItem($item); echo $column->toHtml($item);?>
+ = $column->setItem($item)->toHtml();?>
diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_toolbar.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_toolbar.less
index a0bd5e695a621..aceccb06d47f7 100644
--- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_toolbar.less
+++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_toolbar.less
@@ -188,11 +188,10 @@
.lib-icon-font-symbol(@icon-list);
}
- .limiter {
- float: right;
-
- .products.wrapper ~ .toolbar & {
+ .toolbar {
+ .products.wrapper ~ & .limiter {
display: block;
+ float: right;
}
}
}
diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less
index a36934111eb06..c177f91e9e7e8 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_extends.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less
@@ -1402,7 +1402,7 @@
&.active {
> .title {
.lib-icon-font-symbol(
- @_icon-font-content: @icon-prev,
+ @_icon-font-content: @icon-up,
@_icon-font-position: after
);
}
diff --git a/app/design/frontend/Magento/blank/web/css/source/_sections.less b/app/design/frontend/Magento/blank/web/css/source/_sections.less
index d5d6e5a3d105c..f0a3518c92a8b 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_sections.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_sections.less
@@ -20,9 +20,7 @@
.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) {
.product.data.items {
- .lib-data-tabs(
- @_tab-content-border-top-status: true
- );
+ .lib-data-tabs();
}
}
diff --git a/app/design/frontend/Magento/blank/web/css/source/_variables.less b/app/design/frontend/Magento/blank/web/css/source/_variables.less
index 5d946dd8a826e..256bc796dba94 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_variables.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_variables.less
@@ -15,6 +15,16 @@
@font-family-name__base: 'Open Sans';
@font-family__base: @font-family-name__base, @font-family__sans-serif;
+//
+// Sections variables
+// _____________________________________________
+
+//
+// Tabs
+// ---------------------------------------------
+@tab-content__border-top-status: true;
+
+
//
// Sidebar
// ---------------------------------------------
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
index 01b8c7be93aec..a8778da50f70b 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less
@@ -277,8 +277,8 @@
}
.sidebar & {
.product-item-photo {
- top: 9px;
left: 9px;
+ top: 9px;
}
}
}
@@ -310,8 +310,8 @@
.actions-primary + .actions-secondary {
display: table-cell;
padding-left: 10px;
- width: 50%;
vertical-align: middle;
+ width: 50%;
> .action {
margin-right: 10px;
@@ -443,7 +443,7 @@
.product-item {
margin-left: calc(~'(100% - 4 * 24.439%) / 3');
- padding: 0;
+ padding: 5px;
width: 24.439%;
&:nth-child(4n + 1) {
diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less
index 580abf264cadc..0997b9739125d 100644
--- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less
+++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_toolbar.less
@@ -233,10 +233,10 @@
}
}
- .limiter {
- float: right;
- .products.wrapper ~ .toolbar & {
+ .toolbar {
+ .products.wrapper ~ & .limiter {
display: block;
+ float: right;
}
}
}
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml
new file mode 100644
index 0000000000000..65add76a12af3
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleLinkData.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ 1
+ 1
+ 1.11
+ 1
+ 1
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleOptionData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleOptionData.xml
new file mode 100644
index 0000000000000..02f70ec15cab8
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/BundleOptionData.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ bundle-option-dropdown
+ true
+ dropdown
+ 1
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/CustomAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/CustomAttributeData.xml
new file mode 100644
index 0000000000000..c7f150e7ad6fb
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/CustomAttributeData.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ price_type
+ 0
+
+
+ price_type
+ 1
+
+
+ price_view
+ 1
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml
index a8598ded6ec17..69741ccd5021a 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Data/ProductData.xml
@@ -19,4 +19,18 @@
1
bundleproduct
+
+ Api Bundle Product
+ api-bundle-product
+ bundle
+ 4
+ 4
+ 1
+ api-bundle-product
+ EavStockItem
+ ApiProductDescription
+ ApiProductShortDescription
+ CustomAttributeDynamicPrice
+ CustomAttributePriceView
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_link-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_link-meta.xml
new file mode 100644
index 0000000000000..be881a7e98d65
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_link-meta.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ application/json
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_option-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_option-meta.xml
new file mode 100644
index 0000000000000..991c01ec4c6f0
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_option-meta.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ application/json
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_options-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_options-meta.xml
new file mode 100644
index 0000000000000..a81d5dda6a40b
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Bundle/Metadata/bundle_options-meta.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ application/json
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AddProductToCartActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AddProductToCartActionGroup.xml
index 6caa4fef770b1..9380c3052a5f5 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AddProductToCartActionGroup.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AddProductToCartActionGroup.xml
@@ -14,5 +14,6 @@
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml
index 96e40e348a6f2..9647f6228aea4 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/AdminCategoryActionGroup.xml
@@ -118,6 +118,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml
new file mode 100644
index 0000000000000..0409c3f195013
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/ActionGroup/CustomOptionsActionGroup.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml
index 34110d0417661..17d0c6c2ed340 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductData.xml
@@ -281,4 +281,29 @@
EavStockItem
ApiProductNewsFromDate
+
+
+
+
+
+
+
+
+
+
+ api-simple-product
+ simple
+ 4
+ 4
+ Api Simple Product
+ 1.00
+
+
+ api-simple-product
+ simple
+ 4
+ 4
+ Api Simple Product
+ 100.00
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml
index f2f6e1a6f552f..2abc557d44d96 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Data/ProductOptionData.xml
@@ -18,6 +18,16 @@
fixed
0
+
+
+ OptionField2
+ field
+ true
+ 1
+ 20
+ fixed
+ 0
+
OptionArea
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product-meta.xml
index edbf1133d4fae..b04b4bb98c854 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product-meta.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product-meta.xml
@@ -60,7 +60,10 @@
boolean
-
+
+ application/json
+
+
application/json
@@ -115,7 +118,43 @@
boolean
-
+
+ application/json
+
+
+ application/json
+
+
+
+ application/json
+
+
+
+
+ application/json
+
+
application/json
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_link-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_link-meta.xml
index a6d418dc675e7..899dc3a7f4a8c 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_link-meta.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_link-meta.xml
@@ -14,9 +14,9 @@
string
string
integer
-
- product_link_extension_attribute
-
+
string
@@ -24,8 +24,8 @@
string
string
integer
-
- product_link_extension_attribute
-
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_links-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_links-meta.xml
new file mode 100644
index 0000000000000..34e8d0fca6833
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Metadata/product_links-meta.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ application/json
+
+ product_link
+
+
+
+ application/json
+ product_link
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategorySidebarTreeSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategorySidebarTreeSection.xml
index e914c80c3e6ac..5e080bbb7fdba 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategorySidebarTreeSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminCategorySidebarTreeSection.xml
@@ -14,5 +14,7 @@
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeSetSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeSetSection.xml
index 19e59d1cd22e6..011b2fce3a780 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeSetSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductAttributeSetSection.xml
@@ -17,4 +17,8 @@
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCategoryCreationSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCategoryCreationSection.xml
new file mode 100644
index 0000000000000..a4566115099ef
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCategoryCreationSection.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductContentSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductContentSection.xml
new file mode 100644
index 0000000000000..12a00ae8b3777
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductContentSection.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml
index 702de0a8e1803..cb80dade856a7 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductCustomizableOptionsSection.xml
@@ -29,5 +29,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml
index 5abc388a7d65d..bcbdc22c2eaa0 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/StorefrontProductInfoMainSection.xml
@@ -23,7 +23,14 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminCreateCategoryFromProductPageTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminCreateCategoryFromProductPageTest.xml
new file mode 100644
index 0000000000000..7c81f4472e92a
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdminCreateCategoryFromProductPageTest.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdvanceCatalogSearchSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdvanceCatalogSearchSimpleProductTest.xml
new file mode 100644
index 0000000000000..a302fa58ec241
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/AdvanceCatalogSearchSimpleProductTest.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml
index cc33059dd10b6..cf7f996736eb3 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml
@@ -32,6 +32,7 @@
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml
new file mode 100644
index 0000000000000..2710002d625d7
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Test/SimpleProductTwoCustomOptionsTest.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductTest.xml
new file mode 100644
index 0000000000000..11bc308902ca0
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductTest.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml
deleted file mode 100644
index f52a86810842e..0000000000000
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchSimpleProductsTest.xml
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml
deleted file mode 100644
index 80b1b294fecd5..0000000000000
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/CatalogSearch/Test/AdvanceCatalogSearchVirtualProductsTest.xml
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Page/GuestCheckoutReviewAndPaymentsPage.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Page/GuestCheckoutReviewAndPaymentsPage.xml
new file mode 100644
index 0000000000000..3fb6e99ed6730
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Page/GuestCheckoutReviewAndPaymentsPage.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml
index 32a57a14cd9da..248070940542c 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutCartSummarySection.xml
@@ -15,5 +15,9 @@
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutShippingGuestInfoSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutShippingGuestInfoSection.xml
index f20ae4dac07d5..f2cca89be6c18 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutShippingGuestInfoSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Checkout/Section/CheckoutShippingGuestInfoSection.xml
@@ -17,5 +17,7 @@
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ConfigurableProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ConfigurableProductData.xml
index 0a2429fa8f9e5..70e758a51409d 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ConfigurableProductData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Data/ConfigurableProductData.xml
@@ -34,6 +34,19 @@
EavStockItem
CustomAttributeCategoryIds
+
+ api-configurable-product
+ configurable
+ 4
+ 4
+ API Configurable Product
+ api-configurable-product
+ 1
+ 100
+ EavStockItem
+ ApiProductDescription
+ ApiProductShortDescription
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml
index cebb76e68a6cc..45bf866551319 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Section/StorefrontProductInfoMainSection.xml
@@ -14,5 +14,8 @@
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductOutOfStockTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductOutOfStockTest.xml
new file mode 100644
index 0000000000000..96651e303c5f2
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductOutOfStockTest.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateAttributeTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateAttributeTest.xml
new file mode 100644
index 0000000000000..2a2c331aa158f
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProduct/Test/AdminConfigurableProductUpdateAttributeTest.xml
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/LinkData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/LinkData.xml
index 6276f340f85a6..1498f4b96b3ab 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/LinkData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/LinkData.xml
@@ -41,4 +41,13 @@
URL
https://static.magento.com/sites/all/themes/mag_redesign/images/magento-logo.svg
+
+ Api Downloadable Link
+ 2.00
+ url
+ No
+ 1000
+ 0
+ https://static.magento.com/sites/all/themes/mag_redesign/images/magento-logo.svg
+
\ No newline at end of file
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/ProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/ProductData.xml
index 427f2577a8ab9..f71ebd481a97d 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/ProductData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Data/ProductData.xml
@@ -19,4 +19,19 @@
1
downloadableproduct
+
+ api-downloadable-product
+ downloadable
+ 4
+ 4
+ Api Downloadable Product
+ 123.00
+ api-downloadable-product
+ 1
+ 100
+ EavStockItem
+ ApiProductDescription
+ ApiProductShortDescription
+ apiDownloadableLink
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/downloadable_link-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/downloadable_link-meta.xml
new file mode 100644
index 0000000000000..dc86c4e8d7957
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/downloadable_link-meta.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+ application/json
+
+ boolean
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/link_file_content-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/link_file_content-meta.xml
new file mode 100644
index 0000000000000..72f643e06800d
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/link_file_content-meta.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ string
+ string
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/sample_file_content-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/sample_file_content-meta.xml
new file mode 100644
index 0000000000000..144ce67bb25bb
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Downloadable/Metadata/sample_file_content-meta.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ string
+ string
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/GroupedProductData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/GroupedProductData.xml
index 019b0d24dec52..9960d698a7861 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/GroupedProductData.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/GroupedProductData.xml
@@ -17,4 +17,15 @@
groupedproduct
EavStockItem
+
+ api-grouped-product
+ grouped
+ 4
+ Api Grouped Product
+ 1
+ api-grouped-product
+ EavStockItem
+ ApiProductDescription
+ ApiProductShortDescription
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkData.xml
new file mode 100644
index 0000000000000..9a5df1e379a86
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkData.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ associated
+ simple
+ 1
+ Qty1000
+
+
+
+
+ associated
+ simple
+ 2
+ Qty1000
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkExtensionAttributeData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkExtensionAttributeData.xml
new file mode 100644
index 0000000000000..5f5dcb3a0ef4f
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinkExtensionAttributeData.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ 1000
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinksData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinksData.xml
new file mode 100644
index 0000000000000..523517aa70080
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/GroupedProduct/Data/ProductLinksData.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ ProductLinkSimple1
+
+
+ ProductLinkSimple2
+
+
+
+ - ProductLinkSimple1
+ - ProductLinkSimple2
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Data/PersistentData.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Data/PersistentData.xml
new file mode 100644
index 0000000000000..4ba2e1ae73824
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Data/PersistentData.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ persistentDefaultState
+
+
+ 0
+
+
+
+ persistentEnabledState
+
+
+ 1
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Metadata/persistent_config-meta.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Metadata/persistent_config-meta.xml
new file mode 100644
index 0000000000000..69a835aa703eb
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Metadata/persistent_config-meta.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Test/GuestCheckoutWithEnabledPersistentTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Test/GuestCheckoutWithEnabledPersistentTest.xml
new file mode 100644
index 0000000000000..f7f76da7d3895
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Persistent/Test/GuestCheckoutWithEnabledPersistentTest.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminCartPriceRuleActionGroup.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminCartPriceRuleActionGroup.xml
new file mode 100644
index 0000000000000..24d881ee6081d
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/ActionGroup/AdminCartPriceRuleActionGroup.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/PriceRuleConditionsSection.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/PriceRuleConditionsSection.xml
index 39c6dd6b31968..93ed408ce7a0e 100644
--- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/PriceRuleConditionsSection.xml
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Section/PriceRuleConditionsSection.xml
@@ -16,5 +16,9 @@
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleCountry.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleCountry.xml
new file mode 100644
index 0000000000000..a6a0d669f3435
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleCountry.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRulePostcode.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRulePostcode.xml
new file mode 100644
index 0000000000000..97936382e60e0
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRulePostcode.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleQuantity.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleQuantity.xml
new file mode 100644
index 0000000000000..f4342b5d480b4
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleQuantity.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleState.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleState.xml
new file mode 100644
index 0000000000000..e2de2117d78e7
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleState.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleSubtotal.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleSubtotal.xml
new file mode 100644
index 0000000000000..6c49534ee43c1
--- /dev/null
+++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/SalesRule/Test/StorefrontCartPriceRuleSubtotal.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Page/Adminhtml/CatalogRuleNew.xml b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Page/Adminhtml/CatalogRuleNew.xml
index 1823dd1e1bbe2..ba4ff8d42d951 100644
--- a/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Page/Adminhtml/CatalogRuleNew.xml
+++ b/dev/tests/functional/tests/app/Magento/CatalogRule/Test/Page/Adminhtml/CatalogRuleNew.xml
@@ -9,6 +9,6 @@
-
+
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php
new file mode 100644
index 0000000000000..03949115ea62c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php
@@ -0,0 +1,54 @@
+create(\Magento\Catalog\Model\Product::class);
+$product->load(3);
+
+/** @var $typeInstance \Magento\Bundle\Model\Product\Type */
+$typeInstance = $product->getTypeInstance();
+$typeInstance->setStoreFilter($product->getStoreId(), $product);
+$optionCollection = $typeInstance->getOptionsCollection($product);
+
+$bundleOptions = [];
+$bundleOptionsQty = [];
+foreach ($optionCollection as $option) {
+ /** @var $option \Magento\Bundle\Model\Option */
+ $selectionsCollection = $typeInstance->getSelectionsCollection([$option->getId()], $product);
+ if ($option->isMultiSelection()) {
+ $bundleOptions[$option->getId()] = array_column($selectionsCollection->toArray(), 'selection_id');
+ } else {
+ $bundleOptions[$option->getId()] = $selectionsCollection->getFirstItem()->getSelectionId();
+ }
+ $bundleOptionsQty[$option->getId()] = 1;
+}
+
+$requestInfo = new \Magento\Framework\DataObject(
+ [
+ 'product' => $product->getId(),
+ 'bundle_option' => $bundleOptions,
+ 'bundle_option_qty' => $bundleOptionsQty,
+ 'qty' => 1,
+ ]
+);
+
+/** @var $cart \Magento\Checkout\Model\Cart */
+$cart = Bootstrap::getObjectManager()->create(\Magento\Checkout\Model\Cart::class);
+$cart->addProduct($product, $requestInfo);
+$cart->getQuote()->setReservedOrderId('test_cart_with_bundle_and_options');
+$cart->save();
+
+/** @var $objectManager \Magento\TestFramework\ObjectManager */
+$objectManager = Bootstrap::getObjectManager();
+$objectManager->removeSharedInstance(\Magento\Checkout\Model\Session::class);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php
new file mode 100644
index 0000000000000..d32d6fab33319
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php
@@ -0,0 +1,28 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var $objectManager \Magento\TestFramework\ObjectManager */
+$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+$quote->load('test_cart_with_bundle_and_options', 'reserved_order_id');
+$quote->delete();
+
+/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
+$quoteIdMask = $objectManager->create(\Magento\Quote\Model\QuoteIdMask::class);
+$quoteIdMask->delete($quote->getId());
+
+require __DIR__ . 'product_with_multiple_options_rollback.php';
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
index 0731871b4bd40..61add5f7d0ea7 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Client/ElasticsearchTest.php
@@ -100,7 +100,7 @@ private function search($text)
*/
public function testSearchConfigurableProductBySimpleProductName()
{
- $this->assertProductWithSkuFound('configurable', $this->search('Configurable OptionOption'));
+ $this->assertProductWithSkuFound('configurable', $this->search('Configurable Option'));
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
index 4cdff533af737..014aaf7679bc9 100755
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php
@@ -5,13 +5,18 @@
*/
namespace Magento\Elasticsearch\Model\Indexer;
-use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Action as ProductAction;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\CatalogInventory\Api\StockRegistryInterface;
+use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
+use Magento\CatalogSearch\Model\Indexer\Fulltext as CatalogSearchFulltextIndexer;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient;
use Magento\Elasticsearch\Model\Config;
use Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver;
+use Magento\Indexer\Model\Indexer;
/**
* Important: Please make sure that each integration test file works with unique elastic search index. In order to
@@ -20,107 +25,79 @@
*
* @magentoDbIsolation disabled
* @magentoDataFixture Magento/Elasticsearch/_files/indexer.php
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class IndexHandlerTest extends \PHPUnit\Framework\TestCase
{
/**
- * @var ConnectionManager
+ * @var ProductRepositoryInterface
*/
- protected $connectionManager;
+ private $productRepository;
/**
* @var ElasticsearchClient
*/
- protected $client;
+ private $client;
/**
* @var StoreManagerInterface
*/
- protected $storeManager;
+ private $storeManager;
/**
* @var int[]
*/
- protected $storeIds;
+ private $storeIds;
/**
- * @var Config
+ * @var string
*/
- protected $clientConfig;
+ private $entityType;
/**
- * @var SearchIndexNameResolver
- */
- protected $searchIndexNameResolver;
-
- /**
- * @var Product
- */
- protected $productApple;
-
- /**
- * @var Product
- */
- protected $productBanana;
-
- /**
- * @var Product
- */
- protected $productOrange;
-
- /**
- * @var Product
+ * @var Indexer
*/
- protected $productPapaya;
+ private $indexer;
/**
- * @var Product
+ * @var SearchIndexNameResolver
*/
- protected $productCherry;
+ private $searchIndexNameResolver;
/**
- * Setup method
+ * {@inheritdoc}
*/
protected function setUp()
{
- $this->connectionManager = Bootstrap::getObjectManager()->create(
- \Magento\Elasticsearch\SearchAdapter\ConnectionManager::class
- );
-
- $this->client = $this->connectionManager->getConnection();
+ $connectionManager = Bootstrap::getObjectManager()->create(ConnectionManager::class);
+ $this->client = $connectionManager->getConnection();
- $this->storeManager = Bootstrap::getObjectManager()->create(
- \Magento\Store\Model\StoreManagerInterface::class
- );
+ $this->storeManager = Bootstrap::getObjectManager()->create(StoreManagerInterface::class);
$this->storeIds = array_keys($this->storeManager->getStores());
- $this->clientConfig = Bootstrap::getObjectManager()->create(
- \Magento\Elasticsearch\Model\Config::class
- );
+ $clientConfig = Bootstrap::getObjectManager()->create(Config::class);
+ $this->entityType = $clientConfig->getEntityType();
- $this->searchIndexNameResolver = Bootstrap::getObjectManager()->create(
- \Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver::class
- );
+ $this->indexer = Bootstrap::getObjectManager()->create(Indexer::class);
+ $this->indexer->load(CatalogSearchFulltextIndexer::INDEXER_ID);
+ $this->indexer->reindexAll();
- $this->productApple = $this->getProductBySku('fulltext-1');
- $this->productBanana = $this->getProductBySku('fulltext-2');
- $this->productOrange = $this->getProductBySku('fulltext-3');
- $this->productPapaya = $this->getProductBySku('fulltext-4');
- $this->productCherry = $this->getProductBySku('fulltext-5');
+ $this->searchIndexNameResolver = Bootstrap::getObjectManager()->create(SearchIndexNameResolver::class);
+ $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
}
/**
- * Test reindex process
* @magentoConfigFixture default/catalog/search/engine elasticsearch
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
+ * @return void
*/
- public function testReindexAll()
+ public function testReindexAll(): void
{
- $this->reindexAll();
+ $productApple = $this->productRepository->get('fulltext-1');
foreach ($this->storeIds as $storeId) {
$products = $this->searchByName('Apple', $storeId);
$this->assertCount(1, $products);
- $this->assertEquals($this->productApple->getId(), $products[0]['_id']);
+ $this->assertEquals($productApple->getId(), $products[0]['_id']);
$products = $this->searchByName('Simple Product', $storeId);
$this->assertCount(5, $products);
@@ -128,19 +105,17 @@ public function testReindexAll()
}
/**
+ * @magentoAppIsolation enabled
* @magentoConfigFixture default/catalog/search/engine elasticsearch
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
+ * @return void
*/
- public function testReindexRowAfterEdit()
+ public function testReindexRowAfterEdit(): void
{
- // The test executes fine locally. On bamboo there is some issue with parallel test execution or other
- // test interaction. It is being marked as skipped until more time is available to investigate and
- // fix the issue.
- $this->markTestSkipped('MAGETWO-53851 - Ticket to investiage this test failure on Bamboo and fix it.');
-
- $this->productApple->setData('name', 'Simple Product Cucumber');
- $this->productApple->save();
- $this->reindexAll();
+ $this->storeManager->setCurrentStore('admin');
+ $productApple = $this->productRepository->get('fulltext-1');
+ $productApple->setName('Simple Product Cucumber');
+ $this->productRepository->save($productApple);
foreach ($this->storeIds as $storeId) {
$products = $this->searchByName('Apple', $storeId);
@@ -148,7 +123,7 @@ public function testReindexRowAfterEdit()
$products = $this->searchByName('Cucumber', $storeId);
$this->assertCount(1, $products);
- $this->assertEquals($this->productApple->getId(), $products[0]['_id']);
+ $this->assertEquals($productApple->getId(), $products[0]['_id']);
$products = $this->searchByName('Simple Product', $storeId);
$this->assertCount(5, $products);
@@ -158,22 +133,21 @@ public function testReindexRowAfterEdit()
/**
* @magentoConfigFixture default/catalog/search/engine elasticsearch
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
+ * @return void
*/
- public function testReindexRowAfterMassAction()
+ public function testReindexRowAfterMassAction(): void
{
- $this->reindexAll();
+ $productApple = $this->productRepository->get('fulltext-1');
+ $productBanana = $this->productRepository->get('fulltext-2');
$productIds = [
- $this->productApple->getId(),
- $this->productBanana->getId(),
+ $productApple->getId(),
+ $productBanana->getId(),
];
$attrData = [
'name' => 'Simple Product Common',
];
-
- /** @var \Magento\Catalog\Model\Product\Action $action */
- $action = Bootstrap::getObjectManager()->get(
- \Magento\Catalog\Model\Product\Action::class
- );
+ /** @var ProductAction $action */
+ $action = Bootstrap::getObjectManager()->get(ProductAction::class);
foreach ($this->storeIds as $storeId) {
$action->updateAttributes($productIds, $attrData, $storeId);
@@ -199,30 +173,67 @@ public function testReindexRowAfterMassAction()
* @magentoConfigFixture default/catalog/search/engine elasticsearch
* @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
* @magentoAppArea adminhtml
+ * @return void
*/
- public function testReindexRowAfterDelete()
+ public function testReindexRowAfterDelete(): void
{
- $this->reindexAll();
- $this->productBanana->delete();
+ $productBanana = $this->productRepository->get('fulltext-2');
+ $this->productRepository->delete($productBanana);
foreach ($this->storeIds as $storeId) {
+ $products = $this->searchByName('Banana', $storeId);
+ $this->assertEmpty($products);
+
$products = $this->searchByName('Simple Product', $storeId);
$this->assertCount(4, $products);
}
}
/**
- * Search docs in Elasticsearch by name
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ * @magentoConfigFixture default/catalog/search/engine elasticsearch
+ * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest
+ * @magentoDataFixture Magento/Elasticsearch/_files/configurable_products.php
+ * @return void
+ */
+ public function testReindexRowAfterUpdateStockStatus(): void
+ {
+ foreach ($this->storeIds as $storeId) {
+ $products = $this->searchByName('ProductOption1', $storeId);
+ $this->assertNotEmpty($products);
+ }
+ $product = $this->productRepository->get('simple_10');
+ /** @var StockRegistryInterface $stockRegistry */
+ $stockRegistry = Bootstrap::getObjectManager()->create(StockRegistryInterface::class);
+ $stockItem = $stockRegistry->getStockItem($product->getId());
+ $stockItem->setIsInStock(false);
+ /** @var StockItemRepositoryInterface $stockRepository */
+ $stockRepository = Bootstrap::getObjectManager()->create(StockItemRepositoryInterface::class);
+ $stockRepository->save($stockItem);
+
+ foreach ($this->storeIds as $storeId) {
+ $products = $this->searchByName('ProductOption1', $storeId);
+ $this->assertEmpty($products);
+
+ $products = $this->searchByName('Configurable', $storeId);
+ $this->assertNotEmpty($products);
+ }
+ }
+
+ /**
+ * Search docs in Elasticsearch by name.
*
* @param string $text
* @param int $storeId
* @return array
*/
- protected function searchByName($text, $storeId)
+ private function searchByName(string $text, int $storeId): array
{
+ $index = $this->searchIndexNameResolver->getIndexName($storeId, $this->indexer->getId());
$searchQuery = [
- 'index' => $this->searchIndexNameResolver->getIndexName($storeId, 'catalogsearch_fulltext'),
- 'type' => $this->clientConfig->getEntityType(),
+ 'index' => $index,
+ 'type' => $this->entityType,
'body' => [
'query' => [
'bool' => [
@@ -240,35 +251,7 @@ protected function searchByName($text, $storeId)
];
$queryResult = $this->client->query($searchQuery);
$products = isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : [];
- return $products;
- }
-
- /**
- * Return product by SKU
- *
- * @param string $sku
- * @return Product
- */
- protected function getProductBySku($sku)
- {
- /** @var Product $product */
- $product = Bootstrap::getObjectManager()->get(
- \Magento\Catalog\Model\Product::class
- );
- return $product->loadByAttribute('sku', $sku);
- }
- /**
- * Perform full reindex
- *
- * @return void
- */
- private function reindexAll()
- {
- $indexer = Bootstrap::getObjectManager()->create(
- \Magento\Indexer\Model\Indexer::class
- );
- $indexer->load('catalogsearch_fulltext');
- $indexer->reindexAll();
+ return $products;
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute.php
new file mode 100644
index 0000000000000..7ec53d9099d35
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute.php
@@ -0,0 +1,62 @@
+get(\Magento\Eav\Model\Config::class);
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
+
+$eavConfig->clear();
+
+/** @var $installer \Magento\Catalog\Setup\CategorySetup */
+$installer = Bootstrap::getObjectManager()->create(\Magento\Catalog\Setup\CategorySetup::class);
+
+if (!$attribute->getId()) {
+ /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+ $attribute = Bootstrap::getObjectManager()->create(
+ \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
+ );
+
+ /** @var AttributeRepositoryInterface $attributeRepository */
+ $attributeRepository = Bootstrap::getObjectManager()->create(AttributeRepositoryInterface::class);
+
+ $attribute->setData(
+ [
+ 'attribute_code' => 'test_configurable',
+ 'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
+ 'is_global' => 1,
+ 'is_user_defined' => 1,
+ 'frontend_input' => 'select',
+ 'is_unique' => 0,
+ 'is_required' => 0,
+ 'is_searchable' => 0,
+ 'is_visible_in_advanced_search' => 0,
+ 'is_comparable' => 0,
+ 'is_filterable' => 0,
+ 'is_filterable_in_search' => 0,
+ 'is_used_for_promo_rules' => 0,
+ 'is_html_allowed_on_front' => 1,
+ 'is_visible_on_front' => 0,
+ 'used_in_product_listing' => 0,
+ 'used_for_sort_by' => 0,
+ 'frontend_label' => ['Test Configurable'],
+ 'backend_type' => 'int',
+ 'option' => [
+ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']],
+ 'order' => ['option_0' => 1, 'option_1' => 2],
+ ],
+ ]
+ );
+
+ $attributeRepository->save($attribute);
+
+ /* Assign attribute to attribute set */
+ $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $attribute->getId());
+}
+
+$eavConfig->clear();
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute_rollback.php
new file mode 100644
index 0000000000000..7bdfbc6d7f9b3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_attribute_rollback.php
@@ -0,0 +1,24 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Eav\Model\Config::class);
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
+if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
+ && $attribute->getId()
+) {
+ $attribute->delete();
+}
+$eavConfig->clear();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products.php
index c2dd3c2f879e1..f8872b02ba246 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products.php
@@ -17,7 +17,7 @@
require __DIR__ . '/select_attribute.php';
require __DIR__ . '/multiselect_attribute.php';
-require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute.php';
+require __DIR__ . '/configurable_attribute.php';
$objectManager = Bootstrap::getObjectManager();
@@ -45,7 +45,7 @@
->setId($productId)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
- ->setName('Configurable Option' . $option->getLabel())
+ ->setName('Configurable Option Product' . str_replace(' ', '', $option->getLabel()))
->setSku('simple_' . $productId)
->setPrice($productId)
->setTestConfigurable($option->getValue())
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products_rollback.php
index 0d062c9d3f4e4..e73d2ab1b5906 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/configurable_products_rollback.php
@@ -25,7 +25,7 @@
}
}
-require __DIR__ . '/../../ConfigurableProduct/_files/configurable_attribute_rollback.php';
+require __DIR__ . '/configurable_attribute_rollback.php';
require __DIR__ . '/select_attribute_rollback.php';
require __DIR__ . '/multiselect_attribute_rollback.php';
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php
index 8ee3a40915028..b5c86a63fa47f 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php
@@ -52,7 +52,7 @@
->setStockData(['use_config_manage_stock' => 0])
->save();
-/** @var $productFirst \Magento\Catalog\Model\Product */
+/** @var $productSecond \Magento\Catalog\Model\Product */
$productSecond = $objectManager->create(\Magento\Catalog\Model\Product::class);
$productSecond->setTypeId('simple')
->setAttributeSetId(4)
@@ -68,7 +68,7 @@
->setStockData(['use_config_manage_stock' => 0])
->save();
-/** @var $productFirst \Magento\Catalog\Model\Product */
+/** @var $productThird \Magento\Catalog\Model\Product */
$productThird = $objectManager->create(\Magento\Catalog\Model\Product::class);
$productThird->setTypeId('simple')
->setAttributeSetId(4)
@@ -84,7 +84,7 @@
->setStockData(['use_config_manage_stock' => 0])
->save();
-/** @var $productFirst \Magento\Catalog\Model\Product */
+/** @var $productFourth \Magento\Catalog\Model\Product */
$productFourth = $objectManager->create(\Magento\Catalog\Model\Product::class);
$productFourth->setTypeId('simple')
->setAttributeSetId(4)
@@ -100,7 +100,7 @@
->setStockData(['use_config_manage_stock' => 0])
->save();
-/** @var $productFirst \Magento\Catalog\Model\Product */
+/** @var $productFifth \Magento\Catalog\Model\Product */
$productFifth = $objectManager->create(\Magento\Catalog\Model\Product::class);
$productFifth->setTypeId('simple')
->setAttributeSetId(4)
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/requests.xml b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/requests.xml
index c40ac9e8b9b1c..0aaaf9b85857f 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/requests.xml
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/requests.xml
@@ -394,13 +394,18 @@
+
+
+
+
+
0
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
index 581630e9626fa..ecbce25cd3377 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php
@@ -63,6 +63,10 @@ protected function setUp()
);
$this->adapter = $this->createAdapter();
+
+ $indexer = $this->objectManager->create(\Magento\Indexer\Model\Indexer::class);
+ $indexer->load('catalogsearch_fulltext');
+ $indexer->reindexAll();
}
/**
@@ -531,9 +535,15 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption()
->create(Collection::class)
->setAttributeFilter($attribute->getId());
+ $visibility = [
+ \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_SEARCH,
+ \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH,
+ ];
+
$firstOption = $selectOptions->getFirstItem();
$firstOptionId = $firstOption->getId();
$this->requestBuilder->bind('test_configurable', $firstOptionId);
+ $this->requestBuilder->bind('visibility', $visibility);
$this->requestBuilder->setRequestName('filter_out_of_stock_child');
$queryResponse = $this->executeQuery();
@@ -542,6 +552,7 @@ public function testAdvancedSearchCompositeProductWithOutOfStockOption()
$secondOption = $selectOptions->getLastItem();
$secondOptionId = $secondOption->getId();
$this->requestBuilder->bind('test_configurable', $secondOptionId);
+ $this->requestBuilder->bind('visibility', $visibility);
$this->requestBuilder->setRequestName('filter_out_of_stock_child');
$queryResponse = $this->executeQuery();
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
index 2f30cba3d8ee6..8a7c5246f373a 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/product_configurable.php
@@ -15,8 +15,6 @@
use Magento\Eav\Api\Data\AttributeOptionInterface;
use Magento\TestFramework\Helper\Bootstrap;
-Bootstrap::getInstance()->reinitialize();
-
require __DIR__ . '/configurable_attribute.php';
/** @var ProductRepositoryInterface $productRepository */
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml
index 70f9ac75b07f3..dcf3cd582507c 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml
@@ -394,13 +394,18 @@
+
+
+
+
+
0
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products.php
index b672fbe9f8cec..3902e78d1fb55 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/search_weight_products.php
@@ -7,18 +7,8 @@
*/
use Magento\Catalog\Api\ProductRepositoryInterface;
-use Magento\Catalog\Model\Product;
-use Magento\Catalog\Model\Product\Attribute\Source\Status;
-use Magento\Catalog\Model\Product\Type;
-use Magento\Catalog\Model\Product\Visibility;
-use Magento\Catalog\Setup\CategorySetup;
-use Magento\ConfigurableProduct\Helper\Product\Options\Factory;
-use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
-use Magento\Eav\Api\Data\AttributeOptionInterface;
use Magento\TestFramework\Helper\Bootstrap;
-Bootstrap::getInstance()->reinitialize();
-
$objectManager = Bootstrap::getObjectManager();
/** @var ProductRepositoryInterface $productRepository */
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
index 02a4ba4c282a0..98826adeb2147 100644
--- a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Rule/Condition/ProductTest.php
@@ -58,6 +58,28 @@ public function testValidateCategorySalesRuleIncludesChildren($categoryId, $expe
$this->assertEquals($expectedResult, $rule->validate($quote));
}
+ /**
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture Magento/Bundle/_files/order_item_with_bundle_and_options.php
+ * @magentoDataFixture Magento/SalesRule/_files/rules_sku_exclude.php
+ *
+ * @return void
+ */
+ public function testValidateSalesRuleExcludesBundleChildren(): void
+ {
+ // Load the quote that contains a child of a bundle product
+ /** @var \Magento\Quote\Model\Quote $quote */
+ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class)
+ ->load('test_cart_with_bundle_and_options', 'reserved_order_id');
+
+ // Load the SalesRule looking for excluding products with selected sku
+ /** @var $rule \Magento\SalesRule\Model\Rule */
+ $rule = $this->objectManager->get(\Magento\Framework\Registry::class)
+ ->registry('_fixture/Magento_SalesRule_Sku_Exclude');
+
+ $this->assertEquals(false, $rule->validate($quote));
+ }
+
/**
* @return array
*/
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude.php
new file mode 100644
index 0000000000000..9d88fe48ae111
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude.php
@@ -0,0 +1,79 @@
+create(\Magento\Eav\Api\AttributeRepositoryInterface::class);
+
+/** @var \Magento\Eav\Api\Data\AttributeInterface $skuAttribute */
+$skuAttribute = $repository->get(
+ 'catalog_product',
+ 'sku'
+);
+$data = $skuAttribute->getData();
+$data['is_used_for_promo_rules'] = 1;
+$skuAttribute->setData($data);
+$skuAttribute->save();
+
+/** @var \Magento\SalesRule\Model\Rule $rule */
+$salesRule = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Rule::class);
+$salesRule->setData(
+ [
+ 'name' => '20% Off',
+ 'is_active' => 1,
+ 'customer_group_ids' => [\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID],
+ 'coupon_type' => \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON,
+ 'simple_action' => 'by_percent',
+ 'discount_amount' => 20,
+ 'discount_step' => 0,
+ 'stop_rules_processing' => 1,
+ 'website_ids' => [
+ \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\Store\Model\StoreManagerInterface::class
+ )->getWebsite()->getId(),
+ ],
+ ]
+);
+
+$salesRule->getConditions()->loadArray([
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Combine::class,
+ 'attribute' => null,
+ 'operator' => null,
+ 'value' => '1',
+ 'is_value_processed' => null,
+ 'aggregator' => 'all',
+ 'conditions' =>
+ [
+ [
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Product\Found::class,
+ 'attribute' => null,
+ 'operator' => null,
+ 'value' => '1',
+ 'is_value_processed' => null,
+ 'aggregator' => 'all',
+ 'conditions' =>
+ [
+ [
+ 'type' => \Magento\SalesRule\Model\Rule\Condition\Product::class,
+ 'attribute' => 'sku',
+ 'operator' => '!=',
+ 'value' => 'product-bundle',
+ 'is_value_processed' => false,
+ ],
+ ],
+ ],
+ ],
+]);
+
+$salesRule->save();
+
+/** @var Magento\Framework\Registry $registry */
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
+
+$registry->unregister('_fixture/Magento_SalesRule_Sku_Exclude');
+$registry->register('_fixture/Magento_SalesRule_Sku_Exclude', $salesRule);
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude_rollback.php
new file mode 100644
index 0000000000000..79ec3259fd86a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/rules_sku_exclude_rollback.php
@@ -0,0 +1,29 @@
+create(\Magento\Eav\Api\AttributeRepositoryInterface::class);
+
+/** @var \Magento\Eav\Api\Data\AttributeInterface $skuAttribute */
+$skuAttribute = $repository->get(
+ 'catalog_product',
+ 'sku'
+);
+$data = $skuAttribute->getData();
+$data['is_used_for_promo_rules'] = 0;
+$skuAttribute->setData($data);
+$skuAttribute->save();
+
+/** @var Magento\Framework\Registry $registry */
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
+
+/** @var Magento\SalesRule\Model\Rule $rule */
+$rule = $registry->registry('_fixture/Magento_SalesRule_Sku_Exclude');
+
+$rule->delete();
diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php b/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php
index d4204314453e5..e547187be5ed7 100644
--- a/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Signifyd/Observer/PlaceOrderTest.php
@@ -12,10 +12,17 @@
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Signifyd\Api\CaseCreationServiceInterface;
+use Magento\Store\Api\StoreRepositoryInterface;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\MockObject_MockObject as MockObject;
use Psr\Log\LoggerInterface;
+/**
+ * Test for Magento\Signifyd\Observer\PlaceOrderTest class.
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class PlaceOrderTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -105,7 +112,46 @@ public function testExecute()
$event = $this->objectManager->create(
Event::class,
[
- 'data' => ['order' => $order]
+ 'data' => ['order' => $order],
+ ]
+ );
+
+ /** @var Observer $observer */
+ $observer = $this->objectManager->get(Observer::class);
+ $observer->setEvent($event);
+
+ $this->placeOrder->execute($observer);
+ }
+
+ /**
+ * Signifyd is enabled for default store.
+ * Checks a test case when order placed with website where signifyd is disabled.
+ *
+ * @return void
+ * @covers \Magento\Signifyd\Observer\PlaceOrder::execute
+ * @magentoDataFixture Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php
+ * @magentoDataFixture Magento/Signifyd/_files/website_configuration.php
+ */
+ public function testExecuteWithWebsiteConfiguration(): void
+ {
+ /** @var StoreRepositoryInterface $storeRepository */
+ $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class);
+ $store = $storeRepository->get('test_second_store');
+
+ /** @var StoreManagerInterface $storeManager */
+ $storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $storeManager->setCurrentStore($store->getId());
+
+ $order = $this->getOrder('100000001');
+ $order->setStoreId($store->getId());
+
+ $this->creationService->expects(self::never())
+ ->method('createForOrder');
+
+ $event = $this->objectManager->create(
+ Event::class,
+ [
+ 'data' => ['order' => $order],
]
);
diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php
index 8991825c9381e..49a0a2d33e236 100644
--- a/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php
+++ b/dev/tests/integration/testsuite/Magento/Signifyd/_files/order_with_customer_and_two_simple_products.php
@@ -10,7 +10,7 @@
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\TestFramework\Helper\Bootstrap;
-require __DIR__ . '/../../../Magento/Catalog/_files/multiple_products.php';
+require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php';
require __DIR__ . '/../../../Magento/Customer/_files/customer.php';
require __DIR__ . '/store.php';
@@ -36,33 +36,28 @@
->setCcExpMonth('01')
->setCcExpYear('21');
-/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
-$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
-
-$product1 = $productRepository->get('simple1');
/** @var Item $orderItem */
$orderItem1 = $objectManager->create(Item::class);
-$orderItem1->setProductId($product1->getId())
- ->setSku($product1->getSku())
- ->setName($product1->getName())
+$orderItem1->setProductId($product->getId())
+ ->setSku($product->getSku())
+ ->setName($product->getName())
->setQtyOrdered(1)
- ->setBasePrice($product1->getPrice())
- ->setPrice($product1->getPrice())
- ->setRowTotal($product1->getPrice())
- ->setProductType($product1->getTypeId());
+ ->setBasePrice($product->getPrice())
+ ->setPrice($product->getPrice())
+ ->setRowTotal($product->getPrice())
+ ->setProductType($product->getTypeId());
-$product2 = $productRepository->get('simple2');
/** @var Item $orderItem */
$orderItem2 = $objectManager->create(Item::class);
-$orderItem2->setProductId($product2->getId())
- ->setSku($product2->getSku())
- ->setName($product2->getName())
- ->setPrice($product2->getPrice())
+$orderItem2->setProductId($product->getId())
+ ->setSku('simple2')
+ ->setName('Simple product')
+ ->setPrice(100)
->setQtyOrdered(2)
- ->setBasePrice($product2->getPrice())
- ->setPrice($product2->getPrice())
- ->setRowTotal($product2->getPrice())
- ->setProductType($product2->getTypeId());
+ ->setBasePrice($product->getPrice())
+ ->setPrice($product->getPrice())
+ ->setRowTotal($product->getPrice())
+ ->setProductType($product->getTypeId());
$orderAmount = 100;
$customerEmail = $billingAddress->getEmail();
diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php
new file mode 100644
index 0000000000000..e53b0431503e7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration.php
@@ -0,0 +1,67 @@
+create(Website::class);
+$website->setData(['code' => 'test_website', 'name' => 'Test Website', 'default_group_id' => '1', 'is_default' => '0']);
+$websiteResourceModel = $objectManager->create(WebsiteResourceModel::class);
+$websiteResourceModel->save($website);
+
+$websiteId = $website->getId();
+$store = $objectManager->create(Store::class);
+$groupId = Bootstrap::getObjectManager()->get(StoreManagerInterface::class)
+ ->getWebsite()
+ ->getDefaultGroupId();
+$store->setCode('test_second_store')
+ ->setWebsiteId($websiteId)
+ ->setGroupId($groupId)
+ ->setName('Test Second Store')
+ ->setSortOrder(10)
+ ->setIsActive(1);
+$storeResourceModel = $objectManager->create(StoreResourceModel::class);
+$storeResourceModel->save($store);
+
+/* Refresh stores memory cache */
+$objectManager->get(StoreManagerInterface::class)->reinitStores();
+
+$processConfigData = function (Config $config, array $data) {
+ foreach ($data as $key => $value) {
+ $config->setDataByPath($key, $value);
+ $config->save();
+ }
+};
+
+// save signifyd configuration for the default scope
+$configData = [
+ 'fraud_protection/signifyd/active' => '1',
+];
+/** @var Config $defConfig */
+$defConfig = $objectManager->create(Config::class);
+$defConfig->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
+$processConfigData($defConfig, $configData);
+
+// save signifyd website config data
+$websiteConfigData = [
+ 'fraud_protection/signifyd/active' => '0',
+];
+/** @var Config $websiteConfig */
+$websiteConfig = $objectManager->create(Config::class);
+$websiteConfig->setScope(ScopeInterface::SCOPE_WEBSITES);
+$websiteConfig->setWebsite($websiteId);
+$processConfigData($websiteConfig, $websiteConfigData);
diff --git a/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php
new file mode 100644
index 0000000000000..9b731813fea3b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Signifyd/_files/website_configuration_rollback.php
@@ -0,0 +1,44 @@
+delete($path, $scope, $scopeId);
+ }
+};
+
+/** @var WriterInterface $configWriter */
+$configWriter = $objectManager->get(WriterInterface::class);
+$deleteConfigData($configWriter, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null);
+
+/** @var WebsiteRepositoryInterface $websiteRepository */
+$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
+$website = $websiteRepository->get('test_website');
+$deleteConfigData($configWriter, ScopeInterface::SCOPE_WEBSITES, $website->getId());
+
+$website = $objectManager->create(Website::class);
+/** @var $website Website */
+if ($website->load('test_website', 'code')->getId()) {
+ $website->delete();
+}
+$store = $objectManager->create(Store::class);
+if ($store->load('test_second_store', 'code')->getId()) {
+ $store->delete();
+}
diff --git a/lib/web/mage/adminhtml/form.js b/lib/web/mage/adminhtml/form.js
index e136e8c0f7354..487c71484e4c5 100644
--- a/lib/web/mage/adminhtml/form.js
+++ b/lib/web/mage/adminhtml/form.js
@@ -389,7 +389,7 @@ define([
var idTo, idFrom, values, fromId, radioFrom;
if (config) {
- this._config = config;
+ this._config = jQuery.extend(this._config, config);
}
for (idTo in elementsMap) { //eslint-disable-line guard-for-in
diff --git a/lib/web/mage/apply/scripts.js b/lib/web/mage/apply/scripts.js
index f35e9a2140e67..bf211c38adba5 100644
--- a/lib/web/mage/apply/scripts.js
+++ b/lib/web/mage/apply/scripts.js
@@ -14,7 +14,7 @@ define([
virtuals = [];
/**
- * Adds components to the virtula list.
+ * Adds components to the virtual list.
*
* @param {Object} components
*/