diff --git a/README.md b/README.md index 9e3cf448f99fb..af1d3d4c80404 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results. ## Magento System Requirements -[Magento System Requirements](https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html). +[Magento System Requirements](https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements.html). ## Install Magento diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewed.php b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewed.php index 0de1111ffa722..a42a44814cb0c 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewed.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewed.php @@ -6,12 +6,12 @@ */ namespace Magento\Backend\Controller\Adminhtml\Dashboard; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * Get most viewed products controller. */ -class ProductsViewed extends AjaxBlock implements HttpGetActionInterface +class ProductsViewed extends AjaxBlock implements HttpPostActionInterface { /** * Gets most viewed products list diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/SetWebsiteCountryOptionsToDefaultActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SetWebsiteCountryOptionsToDefaultActionGroup.xml new file mode 100644 index 0000000000000..4519648eb1d1b --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/SetWebsiteCountryOptionsToDefaultActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/Section/CountryOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/CountryOptionsSection.xml new file mode 100644 index 0000000000000..2e2e5aec35ecd --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Section/CountryOptionsSection.xml @@ -0,0 +1,18 @@ + + + + +
+ + + + + +
+
diff --git a/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php new file mode 100644 index 0000000000000..a6c1b088400a7 --- /dev/null +++ b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php @@ -0,0 +1,177 @@ +orderManagement = $orderManagement; + $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->getPaymentNonceCommand = $getPaymentNonceCommand; + } + + /** + * @inheritdoc + */ + public function place(array $orderList): array + { + if (empty($orderList)) { + return []; + } + + $errorList = []; + $firstOrder = $this->orderManagement->place(array_shift($orderList)); + // get payment token from first placed order + $paymentToken = $this->getPaymentToken($firstOrder); + + foreach ($orderList as $order) { + try { + /** @var OrderInterface $order */ + $orderPayment = $order->getPayment(); + $this->setVaultPayment($orderPayment, $paymentToken); + $this->orderManagement->place($order); + } catch (\Exception $e) { + $incrementId = $order->getIncrementId(); + $errorList[$incrementId] = $e; + } + } + + return $errorList; + } + + /** + * Sets vault payment method. + * + * @param OrderPaymentInterface $orderPayment + * @param PaymentTokenInterface $paymentToken + * @return void + */ + private function setVaultPayment(OrderPaymentInterface $orderPayment, PaymentTokenInterface $paymentToken): void + { + $vaultMethod = $this->getVaultPaymentMethod( + $orderPayment->getMethod() + ); + $orderPayment->setMethod($vaultMethod); + + $publicHash = $paymentToken->getPublicHash(); + $customerId = $paymentToken->getCustomerId(); + $result = $this->getPaymentNonceCommand->execute( + ['public_hash' => $publicHash, 'customer_id' => $customerId] + ) + ->get(); + + $orderPayment->setAdditionalInformation( + DataAssignObserver::PAYMENT_METHOD_NONCE, + $result['paymentMethodNonce'] + ); + $orderPayment->setAdditionalInformation( + PaymentTokenInterface::PUBLIC_HASH, + $publicHash + ); + $orderPayment->setAdditionalInformation( + PaymentTokenInterface::CUSTOMER_ID, + $customerId + ); + } + + /** + * Returns vault payment method. + * + * For placing sequence of orders, we need to replace the original method on the vault method. + * + * @param string $method + * @return string + */ + private function getVaultPaymentMethod(string $method): string + { + $vaultPaymentMap = [ + ConfigProvider::CODE => ConfigProvider::CC_VAULT_CODE, + PaypalConfigProvider::PAYPAL_CODE => PaypalConfigProvider::PAYPAL_VAULT_CODE + ]; + + return $vaultPaymentMap[$method] ?? $method; + } + + /** + * Returns payment token. + * + * @param OrderInterface $order + * @return PaymentTokenInterface + * @throws \BadMethodCallException + */ + private function getPaymentToken(OrderInterface $order): PaymentTokenInterface + { + $orderPayment = $order->getPayment(); + $extensionAttributes = $this->getExtensionAttributes($orderPayment); + $paymentToken = $extensionAttributes->getVaultPaymentToken(); + + if ($paymentToken === null) { + throw new \BadMethodCallException('Vault Payment Token should be defined for placed order payment.'); + } + + return $paymentToken; + } + + /** + * Gets payment extension attributes. + * + * @param OrderPaymentInterface $payment + * @return OrderPaymentExtensionInterface + */ + private function getExtensionAttributes(OrderPaymentInterface $payment): OrderPaymentExtensionInterface + { + $extensionAttributes = $payment->getExtensionAttributes(); + if (null === $extensionAttributes) { + $extensionAttributes = $this->paymentExtensionFactory->create(); + $payment->setExtensionAttributes($extensionAttributes); + } + + return $extensionAttributes; + } +} diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 5af56a2afd3fe..2f956076f3846 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -21,7 +21,8 @@ "magento/module-quote": "*", "magento/module-sales": "*", "magento/module-ui": "*", - "magento/module-vault": "*" + "magento/module-vault": "*", + "magento/module-multishipping": "*" }, "suggest": { "magento/module-checkout-agreements": "*", diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index 9de4773af023a..fe4cfab9c0e30 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -42,7 +42,7 @@ cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision Magento\Braintree\Model\AvsEmsCodeMapper Magento\Braintree\Model\CvvEmsCodeMapper - braintree + braintree_group BraintreePayPalFacade @@ -68,7 +68,7 @@ processorResponseCode,processorResponseText,paymentId processorResponseCode,processorResponseText,paymentId,payerEmail en_US,en_GB,en_AU,da_DK,fr_FR,fr_CA,de_DE,zh_HK,it_IT,nl_NL,no_NO,pl_PL,es_ES,sv_SE,tr_TR,pt_BR,ja_JP,id_ID,ko_KR,pt_PT,ru_RU,th_TH,zh_CN,zh_TW - braintree + braintree_group BraintreeCreditCardVaultFacade @@ -78,7 +78,7 @@ Magento\Braintree\Model\InstantPurchase\CreditCard\TokenFormatter Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider - braintree + braintree_group BraintreePayPalVaultFacade @@ -88,7 +88,7 @@ Magento\Braintree\Model\InstantPurchase\PayPal\TokenFormatter Magento\Braintree\Model\InstantPurchase\PaymentAdditionalInformationProvider - braintree + braintree_group diff --git a/app/code/Magento/Braintree/etc/frontend/di.xml b/app/code/Magento/Braintree/etc/frontend/di.xml index ea417c407dffd..d8d3a93b71dc3 100644 --- a/app/code/Magento/Braintree/etc/frontend/di.xml +++ b/app/code/Magento/Braintree/etc/frontend/di.xml @@ -61,4 +61,12 @@ Magento\Braintree\Model\LocaleResolver + + + + Magento\Braintree\Model\Multishipping\PlaceOrder + Magento\Braintree\Model\Multishipping\PlaceOrder + + + diff --git a/app/code/Magento/Braintree/etc/payment.xml b/app/code/Magento/Braintree/etc/payment.xml index dbabd91151022..4cae049aaf5a9 100644 --- a/app/code/Magento/Braintree/etc/payment.xml +++ b/app/code/Magento/Braintree/etc/payment.xml @@ -8,8 +8,16 @@ - + + + + 1 + + + 1 + + diff --git a/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml new file mode 100644 index 0000000000000..06390d403e63d --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/layout/multishipping_checkout_billing.xml @@ -0,0 +1,20 @@ + + + + + + + + Magento_Braintree::multishipping/form.phtml + Magento_Braintree::multishipping/form_paypal.phtml + + + + + diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml new file mode 100644 index 0000000000000..bf8aa8dd09c2c --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml @@ -0,0 +1,29 @@ + + + diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml new file mode 100644 index 0000000000000..ea3eb2214c2d8 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form_paypal.phtml @@ -0,0 +1,29 @@ + + + diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js new file mode 100644 index 0000000000000..1ceebc8e66282 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js @@ -0,0 +1,102 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields', + 'Magento_Braintree/js/validator', + 'Magento_Ui/js/model/messageList', + 'mage/translate', + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Checkout/js/action/set-payment-information', + 'Magento_Checkout/js/model/payment/additional-validators' +], function ( + $, + Component, + validator, + messageList, + $t, + fullScreenLoader, + setPaymentInformationAction, + additionalValidators +) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Magento_Braintree/payment/multishipping/form' + }, + + /** + * Get list of available CC types + * + * @returns {Object} + */ + getCcAvailableTypes: function () { + var availableTypes = validator.getAvailableCardTypes(), + billingCountryId; + + billingCountryId = $('#multishipping_billing_country_id').val(); + + if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { + return validator.collectTypes( + availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) + ); + } + + return availableTypes; + }, + + /** + * @override + */ + placeOrder: function () { + var self = this; + + this.validatorManager.validate(self, function () { + return self.setPaymentInformation(); + }); + }, + + /** + * @override + */ + setPaymentInformation: function () { + if (additionalValidators.validate()) { + + fullScreenLoader.startLoader(); + + $.when( + setPaymentInformationAction( + this.messageContainer, + this.getData() + ) + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); + } + }, + + /** + * {Function} + */ + fail: function () { + fullScreenLoader.stopLoader(); + + return this; + }, + + /** + * {Function} + */ + done: function () { + fullScreenLoader.stopLoader(); + $('#multishipping-billing-form').submit(); + + return this; + } + }); +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js new file mode 100644 index 0000000000000..6702e58d1214b --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js @@ -0,0 +1,143 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define([ + 'jquery', + 'underscore', + 'Magento_Braintree/js/view/payment/method-renderer/paypal', + 'Magento_Checkout/js/action/set-payment-information', + 'Magento_Checkout/js/model/payment/additional-validators', + 'Magento_Checkout/js/model/full-screen-loader', + 'mage/translate' +], function ( + $, + _, + Component, + setPaymentInformationAction, + additionalValidators, + fullScreenLoader, + $t +) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Magento_Braintree/payment/multishipping/paypal', + submitButtonSelector: '#payment-continue span' + }, + + /** + * @override + */ + onActiveChange: function (isActive) { + this.updateSubmitButtonTitle(isActive); + + this._super(isActive); + }, + + /** + * @override + */ + beforePlaceOrder: function (data) { + this._super(data); + + this.updateSubmitButtonTitle(true); + }, + + /** + * @override + */ + getShippingAddress: function () { + return {}; + }, + + /** + * @override + */ + getData: function () { + var data = this._super(); + + data['additional_data']['is_active_payment_token_enabler'] = true; + + return data; + }, + + /** + * @override + */ + isActiveVault: function () { + return true; + }, + + /** + * Skipping order review step on checkout with multiple addresses is not allowed. + * + * @returns {Boolean} + */ + isSkipOrderReview: function () { + return false; + }, + + /** + * Checks if payment method nonce is already received. + * + * @returns {Boolean} + */ + isPaymentMethodNonceReceived: function () { + return this.paymentMethodNonce !== null; + }, + + /** + * Updates submit button title on multi-addresses checkout billing form. + * + * @param {Boolean} isActive + */ + updateSubmitButtonTitle: function (isActive) { + var title = this.isPaymentMethodNonceReceived() || !isActive ? + $t('Go to Review Your Order') : $t('Continue to PayPal'); + + $(this.submitButtonSelector).html(title); + }, + + /** + * @override + */ + placeOrder: function () { + if (!this.isPaymentMethodNonceReceived()) { + this.payWithPayPal(); + } else { + fullScreenLoader.startLoader(); + + $.when( + setPaymentInformationAction( + this.messageContainer, + this.getData() + ) + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); + } + }, + + /** + * {Function} + */ + fail: function () { + fullScreenLoader.stopLoader(); + + return this; + }, + + /** + * {Function} + */ + done: function () { + fullScreenLoader.stopLoader(); + $('#multishipping-billing-form').submit(); + + return this; + } + }); +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html new file mode 100644 index 0000000000000..964e15df166d3 --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html @@ -0,0 +1,106 @@ + + +
+
+
+
+ + + +
+
+
+
    + +
  • + + + +
  • + +
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ + + +
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+
\ No newline at end of file diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html new file mode 100644 index 0000000000000..722989e41f98f --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html @@ -0,0 +1,40 @@ + +
+
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+
+
+
+
diff --git a/app/code/Magento/Catalog/Block/Product/View/Gallery.php b/app/code/Magento/Catalog/Block/Product/View/Gallery.php index 706d9b83b9711..8b98fbdc8f7ef 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Gallery.php +++ b/app/code/Magento/Catalog/Block/Product/View/Gallery.php @@ -207,8 +207,8 @@ public function isMainImage($image) */ public function getImageAttribute($imageId, $attributeName, $default = null) { - $attributes = - $this->getConfigView()->getMediaAttributes('Magento_Catalog', Image::MEDIA_TYPE_CONFIG_NODE, $imageId); + $attributes = $this->getConfigView() + ->getMediaAttributes('Magento_Catalog', Image::MEDIA_TYPE_CONFIG_NODE, $imageId); return $attributes[$attributeName] ?? $default; } diff --git a/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php new file mode 100644 index 0000000000000..0384c9cd9acce --- /dev/null +++ b/app/code/Magento/Catalog/Block/Product/View/GalleryOptions.php @@ -0,0 +1,156 @@ +gallery = $gallery; + $this->jsonSerializer = $jsonSerializer; + parent::__construct($context, $arrayUtils, $data); + } + + /** + * Retrieve gallery options in JSON format + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ElseExpression) + */ + public function getOptionsJson() + { + $optionItems = null; + + //Special case for gallery/nav which can be the string "thumbs/false/dots" + if (is_bool($this->getVar("gallery/nav"))) { + $optionItems['nav'] = $this->getVar("gallery/nav") ? 'true' : 'false'; + } else { + $optionItems['nav'] = $this->escapeHtml($this->getVar("gallery/nav")); + } + + $optionItems['loop'] = $this->getVar("gallery/loop"); + $optionItems['keyboard'] = $this->getVar("gallery/keyboard"); + $optionItems['arrows'] = $this->getVar("gallery/arrows"); + $optionItems['allowfullscreen'] = $this->getVar("gallery/allowfullscreen"); + $optionItems['showCaption'] = $this->getVar("gallery/caption"); + $optionItems['width'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_medium', 'width') + ); + $optionItems['thumbwidth'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_small', 'width') + ); + + if ($this->gallery->getImageAttribute('product_page_image_small', 'height') || + $this->gallery->getImageAttribute('product_page_image_small', 'width')) { + $optionItems['thumbheight'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_small', 'height') ?: + $this->gallery->getImageAttribute('product_page_image_small', 'width') + ); + } + + if ($this->gallery->getImageAttribute('product_page_image_medium', 'height') || + $this->gallery->getImageAttribute('product_page_image_medium', 'width')) { + $optionItems['height'] = (int)$this->escapeHtml( + $this->gallery->getImageAttribute('product_page_image_medium', 'height') ?: + $this->gallery->getImageAttribute('product_page_image_medium', 'width') + ); + } + + if ($this->getVar("gallery/transition/duration")) { + $optionItems['transitionduration'] = + (int)$this->escapeHtml($this->getVar("gallery/transition/duration")); + } + + $optionItems['transition'] = $this->escapeHtml($this->getVar("gallery/transition/effect")); + $optionItems['navarrows'] = $this->getVar("gallery/navarrows"); + $optionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/navtype")); + $optionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/navdir")); + + if ($this->getVar("gallery/thumbmargin")) { + $optionItems['thumbmargin'] = (int)$this->escapeHtml($this->getVar("gallery/thumbmargin")); + } + + return $this->jsonSerializer->serialize($optionItems); + } + + /** + * Retrieve gallery fullscreen options in JSON format + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ElseExpression) + */ + public function getFSOptionsJson() + { + $fsOptionItems = null; + + //Special case for gallery/nav which can be the string "thumbs/false/dots" + if (is_bool($this->getVar("gallery/fullscreen/nav"))) { + $fsOptionItems['nav'] = $this->getVar("gallery/fullscreen/nav") ? 'true' : 'false'; + } else { + $fsOptionItems['nav'] = $this->escapeHtml($this->getVar("gallery/fullscreen/nav")); + } + + $fsOptionItems['loop'] = $this->getVar("gallery/fullscreen/loop"); + $fsOptionItems['navdir'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navdir")); + $fsOptionItems['navarrows'] = $this->getVar("gallery/fullscreen/navarrows"); + $fsOptionItems['navtype'] = $this->escapeHtml($this->getVar("gallery/fullscreen/navtype")); + $fsOptionItems['arrows'] = $this->getVar("gallery/fullscreen/arrows"); + $fsOptionItems['showCaption'] = $this->getVar("gallery/fullscreen/caption"); + + if ($this->getVar("gallery/fullscreen/transition/duration")) { + $fsOptionItems['transitionduration'] = (int)$this->escapeHtml( + $this->getVar("gallery/fullscreen/transition/duration") + ); + } + + $fsOptionItems['transition'] = $this->escapeHtml($this->getVar("gallery/fullscreen/transition/effect")); + + if ($this->getVar("gallery/fullscreen/keyboard")) { + $fsOptionItems['keyboard'] = $this->getVar("gallery/fullscreen/keyboard"); + } + + if ($this->getVar("gallery/fullscreen/thumbmargin")) { + $fsOptionItems['thumbmargin'] = + (int)$this->escapeHtml($this->getVar("gallery/fullscreen/thumbmargin")); + } + + return $this->jsonSerializer->serialize($fsOptionItems); + } +} diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 170f1209ad9e6..9b8d0ad75a8c9 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Helper; use Magento\Framework\App\Helper\AbstractHelper; +use Magento\Framework\View\Element\Block\ArgumentInterface; /** * Catalog image helper @@ -14,7 +15,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @since 100.0.2 */ -class Image extends AbstractHelper +class Image extends AbstractHelper implements ArgumentInterface { /** * Media config node @@ -764,7 +765,7 @@ protected function getImageFile() protected function parseSize($string) { $size = explode('x', strtolower($string)); - if (sizeof($size) == 2) { + if (count($size) == 2) { return ['width' => $size[0] > 0 ? $size[0] : null, 'height' => $size[1] > 0 ? $size[1] : null]; } return false; diff --git a/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php b/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php index f6893a41113e6..dabfdb74f0118 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php +++ b/app/code/Magento/Catalog/Model/Product/Type/FrontSpecialPrice.php @@ -17,6 +17,9 @@ * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @deprecated + * @see \Magento\Catalog\Model\Product\Type\Price */ class FrontSpecialPrice extends Price { @@ -66,6 +69,8 @@ public function __construct( /** * @inheritdoc + * + * @deprecated */ protected function _applySpecialPrice($product, $finalPrice) { diff --git a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php index a597b8fddda9f..ed9f89efc6891 100644 --- a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php +++ b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Set value for Special Price start date @@ -13,21 +16,20 @@ class SetSpecialPriceStartDate implements ObserverInterface { /** - * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface + * @var TimezoneInterface */ private $localeDate; /** - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @codeCoverageIgnore + * @param TimezoneInterface $localeDate */ - public function __construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate) + public function __construct(TimezoneInterface $localeDate) { $this->localeDate = $localeDate; } /** - * Set the current date to Special Price From attribute if it empty + * Set the current date to Special Price From attribute if it's empty. * * @param \Magento\Framework\Event\Observer $observer * @return $this @@ -36,8 +38,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) { /** @var $product \Magento\Catalog\Model\Product */ $product = $observer->getEvent()->getProduct(); - if ($product->getSpecialPrice() && !$product->getSpecialFromDate()) { - $product->setData('special_from_date', $this->localeDate->date()); + if ($product->getSpecialPrice() && ! $product->getSpecialFromDate()) { + $product->setData('special_from_date', $this->localeDate->date()->setTime(0, 0)); } return $this; diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index cbdabe6a2d218..ad32b8edbd243 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -289,4 +289,13 @@ + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml index ea0bcafe56c48..71c8af318e9b4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductGridData.xml @@ -12,4 +12,7 @@ 10 100 + + 1 + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml index 3027416ee520b..bcfab6ccfdf1f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml @@ -34,7 +34,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml new file mode 100644 index 0000000000000..b24ed7f9c9a81 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml @@ -0,0 +1,69 @@ + + + + + + + + + <description value="Checking Catalog grid page number after Save and Close action"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-96164"/> + <useCaseId value="MAGETWO-96127"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Clear product grid--> + <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" + dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteProductIfTheyExist"/> + <createData stepKey="category1" entity="SimpleSubCategory"/> + <createData stepKey="product1" entity="SimpleProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="category2" entity="SimpleSubCategory"/> + <createData stepKey="product2" entity="SimpleProduct"> + <requiredEntity createDataKey="category2"/> + </createData> + </before> + <after> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <click selector="{{AdminDataGridPaginationSection.previousPage}}" stepKey="clickPrevPageOrderGrid"/> + <actionGroup ref="adminDataGridDeleteCustomPerPage" stepKey="deleteCustomAddedPerPage"> + <argument name="perPage" value="ProductPerPage.productCount"/> + </actionGroup> + <deleteData stepKey="deleteCategory1" createDataKey="category1"/> + <deleteData stepKey="deleteProduct1" createDataKey="product1"/> + <deleteData stepKey="deleteCategory2" createDataKey="category2"/> + <deleteData stepKey="deleteProduct2" createDataKey="product2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="adminDataGridSelectCustomPerPage" stepKey="select1OrderPerPage"> + <argument name="perPage" value="ProductPerPage.productCount"/> + </actionGroup> + <!--Go to the next page and edit the product--> + <comment userInput="Go to the next page and edit the product" stepKey="commentEdiProduct"/> + <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageOrderGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> + <argument name="product" value="$$product2$$"/> + </actionGroup> + <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProduct"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageOrderGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php new file mode 100644 index 0000000000000..7ed8b13fce750 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/View/GalleryOptionsTest.php @@ -0,0 +1,223 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Block\Product\View; + +use Magento\Catalog\Block\Product\Context; +use Magento\Catalog\Block\Product\View\Gallery; +use Magento\Catalog\Block\Product\View\GalleryOptions; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Escaper; +use Magento\Framework\View\Config; +use Magento\Framework\Config\View; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class GalleryOptionsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var GalleryOptions + */ + private $model; + + /** + * @var Gallery|\PHPUnit_Framework_MockObject_MockObject + */ + private $gallery; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var Json + */ + private $jsonSerializer; + + /** + * @var View|\PHPUnit_Framework_MockObject_MockObject + */ + private $configView; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $viewConfig; + + /** + * @var Escaper + */ + private $escaper; + + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->escaper = $objectManager->getObject(Escaper::class); + $this->configView = $this->createMock(View::class); + + $this->viewConfig = $this->createConfiguredMock( + Config::class, + [ + 'getViewConfig' => $this->configView + ] + ); + + $this->context = $this->createConfiguredMock( + Context::class, + [ + 'getEscaper' => $this->escaper, + 'getViewConfig' => $this->viewConfig + ] + ); + + $this->gallery = $this->createMock(Gallery::class); + + $this->jsonSerializer = $objectManager->getObject( + Json::class + ); + + $this->model = $objectManager->getObject(GalleryOptions::class, [ + 'context' => $this->context, + 'jsonSerializer' => $this->jsonSerializer, + 'gallery' => $this->gallery + ]); + } + + public function testGetOptionsJson() + { + $configMap = [ + ['Magento_Catalog', 'gallery/nav', 'thumbs'], + ['Magento_Catalog', 'gallery/loop', false], + ['Magento_Catalog', 'gallery/keyboard', true], + ['Magento_Catalog', 'gallery/arrows', true], + ['Magento_Catalog', 'gallery/caption', false], + ['Magento_Catalog', 'gallery/allowfullscreen', true], + ['Magento_Catalog', 'gallery/navdir', 'horizontal'], + ['Magento_Catalog', 'gallery/navarrows', true], + ['Magento_Catalog', 'gallery/navtype', 'slides'], + ['Magento_Catalog', 'gallery/thumbmargin', '5'], + ['Magento_Catalog', 'gallery/transition/effect', 'slide'], + ['Magento_Catalog', 'gallery/transition/duration', '500'], + ]; + + $imageAttributesMap = [ + ['product_page_image_medium','height',null, 100], + ['product_page_image_medium','width',null, 200], + ['product_page_image_small','height',null, 300], + ['product_page_image_small','width',null, 400] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + $this->gallery->expects($this->any()) + ->method('getImageAttribute') + ->will($this->returnValueMap($imageAttributesMap)); + + $json = $this->model->getOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertSame('thumbs', $decodedJson['nav']); + $this->assertSame(false, $decodedJson['loop']); + $this->assertSame(true, $decodedJson['keyboard']); + $this->assertSame(true, $decodedJson['arrows']); + $this->assertSame(false, $decodedJson['showCaption']); + $this->assertSame(true, $decodedJson['allowfullscreen']); + $this->assertSame('horizontal', $decodedJson['navdir']); + $this->assertSame(true, $decodedJson['navarrows']); + $this->assertSame('slides', $decodedJson['navtype']); + $this->assertSame(5, $decodedJson['thumbmargin']); + $this->assertSame('slide', $decodedJson['transition']); + $this->assertSame(500, $decodedJson['transitionduration']); + $this->assertSame(100, $decodedJson['height']); + $this->assertSame(200, $decodedJson['width']); + $this->assertSame(300, $decodedJson['thumbheight']); + $this->assertSame(400, $decodedJson['thumbwidth']); + } + + public function testGetFSOptionsJson() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/nav', false], + ['Magento_Catalog', 'gallery/fullscreen/loop', true], + ['Magento_Catalog', 'gallery/fullscreen/keyboard', true], + ['Magento_Catalog', 'gallery/fullscreen/arrows', false], + ['Magento_Catalog', 'gallery/fullscreen/caption', true], + ['Magento_Catalog', 'gallery/fullscreen/navdir', 'vertical'], + ['Magento_Catalog', 'gallery/fullscreen/navarrows', false], + ['Magento_Catalog', 'gallery/fullscreen/navtype', 'thumbs'], + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', '10'], + ['Magento_Catalog', 'gallery/fullscreen/transition/effect', 'dissolve'], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', '300'] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getFSOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + //Note, this tests the special case for nav variable set to false. It + //Should not be converted to boolean. + $this->assertSame('false', $decodedJson['nav']); + $this->assertSame(true, $decodedJson['loop']); + $this->assertSame(false, $decodedJson['arrows']); + $this->assertSame(true, $decodedJson['keyboard']); + $this->assertSame(true, $decodedJson['showCaption']); + $this->assertSame('vertical', $decodedJson['navdir']); + $this->assertSame(false, $decodedJson['navarrows']); + $this->assertSame(10, $decodedJson['thumbmargin']); + $this->assertSame('thumbs', $decodedJson['navtype']); + $this->assertSame('dissolve', $decodedJson['transition']); + $this->assertSame(300, $decodedJson['transitionduration']); + } + + public function testGetOptionsJsonOptionals() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertArrayNotHasKey('thumbmargin', $decodedJson); + $this->assertArrayNotHasKey('transitionduration', $decodedJson); + } + + public function testGetFSOptionsJsonOptionals() + { + $configMap = [ + ['Magento_Catalog', 'gallery/fullscreen/keyboard', false], + ['Magento_Catalog', 'gallery/fullscreen/thumbmargin', false], + ['Magento_Catalog', 'gallery/fullscreen/transition/duration', false] + ]; + + $this->configView->expects($this->any()) + ->method('getVarValue') + ->will($this->returnValueMap($configMap)); + + $json = $this->model->getFSOptionsJson(); + + $decodedJson = $this->jsonSerializer->unserialize($json); + + $this->assertArrayNotHasKey('thumbmargin', $decodedJson); + $this->assertArrayNotHasKey('keyboard', $decodedJson); + $this->assertArrayNotHasKey('transitionduration', $decodedJson); + } +} diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 793a2291f599c..ee9c5b29da894 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -120,5 +120,4 @@ <plugin name="catalog_app_action_dispatch_controller_context_plugin" type="Magento\Catalog\Plugin\Framework\App\Action\ContextPlugin" /> </type> - <preference for="Magento\Catalog\Model\Product\Type\Price" type="Magento\Catalog\Model\Product\Type\FrontSpecialPrice" /> </config> diff --git a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml index 8d3248896b434..41a2a6142d506 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/catalog_product_view.xml @@ -121,7 +121,12 @@ </arguments> </block> </container> - <block class="Magento\Catalog\Block\Product\View\Gallery" name="product.info.media.image" template="Magento_Catalog::product/view/gallery.phtml"/> + <block class="Magento\Catalog\Block\Product\View\Gallery" name="product.info.media.image" template="Magento_Catalog::product/view/gallery.phtml"> + <arguments> + <argument name="gallery_options" xsi:type="object">Magento\Catalog\Block\Product\View\GalleryOptions</argument> + <argument name="imageHelper" xsi:type="object">Magento\Catalog\Helper\Image</argument> + </arguments> + </block> <container name="skip_gallery_after.wrapper" htmlTag="div" htmlClass="action-skip-wrapper"> <block class="Magento\Framework\View\Element\Template" after="product.info.media.image" name="skip_gallery_after" template="Magento_Theme::html/skip.phtml"> <arguments> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml index 1bfa30478df8a..1f06b90758d0b 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml @@ -12,32 +12,32 @@ * @var $block \Magento\Catalog\Block\Product\View\Gallery */ ?> -<div class="gallery-placeholder _block-content-loading" data-gallery-role="gallery-placeholder"> - <div data-role="loader" class="loading-mask"> - <div class="loader"> - <img src="<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>" - alt="<?= /* @escapeNotVerified */ __('Loading...') ?>"> - </div> - </div> -</div> -<!--Fix for jumping content. Loader must be the same size as gallery.--> -<script> - var config = { - "width": <?= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_medium', 'width') ?>, - "thumbheight": <?php /* @escapeNotVerified */ echo $block->getImageAttribute('product_page_image_small', 'height') - ?: $block->getImageAttribute('product_page_image_small', 'width'); ?>, - "navtype": "<?= /* @escapeNotVerified */ $block->getVar("gallery/navtype") ?>", - "height": <?= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_medium', 'height') ?> - }, - thumbBarHeight = 0, - loader = document.querySelectorAll('[data-gallery-role="gallery-placeholder"] [data-role="loader"]')[0]; - if (config.navtype === 'horizontal') { - thumbBarHeight = config.thumbheight; +<?php + $images = $block->getGalleryImages()->getItems(); + $mainImage = current(array_filter($images, function ($img) use ($block) { + return $block->isMainImage($img); + })); + + if (!empty($images) && empty($mainImage)) { + $mainImage = $block->getGalleryImages()->getFirstItem(); } - loader.style.paddingBottom = ( config.height / config.width * 100) + "%"; -</script> + $helper = $block->getData('imageHelper'); + $mainImageData = $mainImage ? + $mainImage->getData('medium_image_url') : + $helper->getDefaultPlaceholderUrl('image'); + +?> + +<div class="gallery-placeholder _block-content-loading" data-gallery-role="gallery-placeholder"> + <img + alt="main product photo" + class="gallery-placeholder__image" + src="<?= /* @noEscape */ $mainImageData ?>" + /> +</div> + <script type="text/x-magento-init"> { "[data-gallery-role=gallery-placeholder]": { @@ -45,44 +45,8 @@ "mixins":["magnifier/magnify"], "magnifierOpts": <?= /* @escapeNotVerified */ $block->getMagnifier() ?>, "data": <?= /* @escapeNotVerified */ $block->getGalleryImagesJson() ?>, - "options": { - "nav": "<?= /* @escapeNotVerified */ $block->getVar("gallery/nav") ?>", - "loop": <?= /* @escapeNotVerified */ $block->getVar("gallery/loop") ? 'true' : 'false' ?>, - "keyboard": <?= /* @escapeNotVerified */ $block->getVar("gallery/keyboard") ? 'true' : 'false' ?>, - "arrows": <?= /* @escapeNotVerified */ $block->getVar("gallery/arrows") ? 'true' : 'false' ?>, - "allowfullscreen": <?= /* @escapeNotVerified */ $block->getVar("gallery/allowfullscreen") ? 'true' : 'false' ?>, - "showCaption": <?= /* @escapeNotVerified */ $block->getVar("gallery/caption") ? 'true' : 'false' ?>, - "width": "<?= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_medium', 'width') ?>", - "thumbwidth": "<?= /* @escapeNotVerified */ $block->getImageAttribute('product_page_image_small', 'width') ?>", - <?php if ($block->getImageAttribute('product_page_image_small', 'height') || $block->getImageAttribute('product_page_image_small', 'width')): ?> - "thumbheight": <?php /* @escapeNotVerified */ echo $block->getImageAttribute('product_page_image_small', 'height') - ?: $block->getImageAttribute('product_page_image_small', 'width'); ?>, - <?php endif; ?> - <?php if ($block->getImageAttribute('product_page_image_medium', 'height') || $block->getImageAttribute('product_page_image_medium', 'width')): ?> - "height": <?php /* @escapeNotVerified */ echo $block->getImageAttribute('product_page_image_medium', 'height') - ?: $block->getImageAttribute('product_page_image_medium', 'width'); ?>, - <?php endif; ?> - <?php if ($block->getVar("gallery/transition/duration")): ?> - "transitionduration": <?= /* @escapeNotVerified */ $block->getVar("gallery/transition/duration") ?>, - <?php endif; ?> - "transition": "<?= /* @escapeNotVerified */ $block->getVar("gallery/transition/effect") ?>", - "navarrows": <?= /* @escapeNotVerified */ $block->getVar("gallery/navarrows") ? 'true' : 'false' ?>, - "navtype": "<?= /* @escapeNotVerified */ $block->getVar("gallery/navtype") ?>", - "navdir": "<?= /* @escapeNotVerified */ $block->getVar("gallery/navdir") ?>" - }, - "fullscreen": { - "nav": "<?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/nav") ?>", - "loop": <?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/loop") ? 'true' : 'false' ?>, - "navdir": "<?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navdir") ?>", - "navarrows": <?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navarrows") ? 'true' : 'false' ?>, - "navtype": "<?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/navtype") ?>", - "arrows": <?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/arrows") ? 'true' : 'false' ?>, - "showCaption": <?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/caption") ? 'true' : 'false' ?>, - <?php if ($block->getVar("gallery/fullscreen/transition/duration")): ?> - "transitionduration": <?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/transition/duration") ?>, - <?php endif; ?> - "transition": "<?= /* @escapeNotVerified */ $block->getVar("gallery/fullscreen/transition/effect") ?>" - }, + "options": <?= /* @noEscape */ $block->getGalleryOptions()->getOptionsJson() ?>, + "fullscreen": <?= /* @noEscape */ $block->getGalleryOptions()->getFSOptionsJson() ?>, "breakpoints": <?= /* @escapeNotVerified */ $block->getBreakpoints() ?> } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Search/Adapter/Mysql/Query/Builder/Match.php b/app/code/Magento/CatalogGraphQl/Model/Search/Adapter/Mysql/Query/Builder/Match.php new file mode 100644 index 0000000000000..4490cf031e5e1 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Search/Adapter/Mysql/Query/Builder/Match.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Search\Adapter\Mysql\Query\Builder; + +use Magento\Framework\DB\Helper\Mysql\Fulltext; +use Magento\Framework\Search\Adapter\Mysql\Field\ResolverInterface; +use Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match as BuilderMatch; +use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; +use Magento\Framework\Search\Request\Query\BoolExpression; +use Magento\Search\Helper\Data; + +/** + * @inheritdoc + */ +class Match extends BuilderMatch +{ + /** + * @var Data + */ + private $searchHelper; + + /** + * @param ResolverInterface $resolver + * @param Fulltext $fulltextHelper + * @param Data $searchHelper + * @param string $fulltextSearchMode + * @param PreprocessorInterface[] $preprocessors + */ + public function __construct( + ResolverInterface $resolver, + Fulltext $fulltextHelper, + Data $searchHelper, + $fulltextSearchMode = Fulltext::FULLTEXT_MODE_BOOLEAN, + array $preprocessors = [] + ) { + parent::__construct($resolver, $fulltextHelper, $fulltextSearchMode, $preprocessors); + $this->searchHelper = $searchHelper; + } + + /** + * @inheritdoc + */ + protected function prepareQuery($queryValue, $conditionType) + { + $replaceSymbols = str_split(self::SPECIAL_CHARACTERS, 1); + $queryValue = str_replace($replaceSymbols, ' ', $queryValue); + foreach ($this->preprocessors as $preprocessor) { + $queryValue = $preprocessor->process($queryValue); + } + + $stringPrefix = ''; + if ($conditionType === BoolExpression::QUERY_CONDITION_MUST) { + $stringPrefix = '+'; + } elseif ($conditionType === BoolExpression::QUERY_CONDITION_NOT) { + $stringPrefix = '-'; + } + + $queryValues = explode(' ', $queryValue); + + foreach ($queryValues as $queryKey => $queryValue) { + if (empty($queryValue)) { + unset($queryValues[$queryKey]); + } else { + $stringSuffix = $this->searchHelper->getMinQueryLength() > strlen($queryValue) ? '' : '*'; + $queryValues[$queryKey] = $stringPrefix . $queryValue . $stringSuffix; + } + } + + $queryValue = implode(' ', $queryValues); + + return $queryValue; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index b5622e948b7cd..a5bd42860ded0 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -78,4 +78,6 @@ </argument> </arguments> </virtualType> + <preference for="Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match" + type="Magento\CatalogGraphQl\Model\Search\Adapter\Mysql\Query\Builder\Match" /> </config> diff --git a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php index 31fd5606a9849..6851b05aa56a6 100644 --- a/app/code/Magento/CatalogInventory/Model/StockStateProvider.php +++ b/app/code/Magento/CatalogInventory/Model/StockStateProvider.php @@ -119,14 +119,12 @@ public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQ $result->setItemIsQtyDecimal($stockItem->getIsQtyDecimal()); if (!$stockItem->getIsQtyDecimal()) { $result->setHasQtyOptionUpdate(true); - $qty = (int) $qty; + $qty = (int) $qty ?: 1; /** * Adding stock data to quote item */ $result->setItemQty($qty); - $qty = $this->getNumber($qty); - $origQty = (int) $origQty; - $result->setOrigQty($origQty); + $result->setOrigQty((int)$this->getNumber($origQty) ?: 1); } if ($stockItem->getMinSaleQty() && $qty < $stockItem->getMinSaleQty()) { diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml new file mode 100644 index 0000000000000..b9ef37cb4effe --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminSetMinimalQueryLengthActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetMinimalQueryLengthActionGroup"> + <arguments> + <argument name="minLength" type="string" defaultValue="1"/> + </arguments> + <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="wait1"/> + <scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab"/> + <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" visible="false" stepKey="expandCatalogSearchTab"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" stepKey="waitTabToCollapse"/> + <see userInput="{{MinMaxQueryLength.Hint}}" selector="{{AdminCatalogSearchConfigurationSection.minQueryLengthHint}}" stepKey="seeHint1"/> + <see userInput="{{MinMaxQueryLength.Hint}}" selector="{{AdminCatalogSearchConfigurationSection.maxQueryLengthHint}}" stepKey="seeHint2"/> + <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.minQueryLengthInherit}}" stepKey="uncheckSystemValue"/> + <fillField selector="{{AdminCatalogSearchConfigurationSection.minQueryLength}}" userInput="{{minLength}}" stepKey="setMinQueryLength"/> + <click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForConfigSaved"/> + <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml new file mode 100644 index 0000000000000..6868456079110 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SetMinQueryLengthToDefault" type="catalog_search_config_def"> + <requiredEntity type="enable">DefaultMinQueryLength</requiredEntity> + </entity> + <entity name="UncheckMinQueryLengthAndSet" type="catalog_search_config_query_length"> + <requiredEntity type="number">SetMinQueryLengthToOne</requiredEntity> + </entity> + <entity name="DefaultMinQueryLength" type="enable"> + <data key="inherit">true</data> + </entity> + <entity name="DefaultMinQueryLengthDisable" type="enable"> + <data key="inherit">0</data> + </entity> + <entity name="SetMinQueryLengthToOne" type="number"> + <data key="value">1</data> + </entity> + +</entities> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/MinMaxQueryLengthHintsData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/MinMaxQueryLengthHintsData.xml new file mode 100644 index 0000000000000..6fb254afea347 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/MinMaxQueryLengthHintsData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="MinMaxQueryLength" type="constant"> + <data key="Hint">This value must be compatible with the corresponding setting in the configured search engine</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml new file mode 100644 index 0000000000000..7405377249aa4 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="CatalogSearchConfigDefault" dataType="catalog_search_config_def" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="catalog_search_config_def"> + <object key="search" dataType="catalog_search_config_def"> + <object key="fields" dataType="catalog_search_config_def"> + <object key="min_query_length" dataType="enable"> + <field key="inherit">boolean</field> + </object> + </object> + </object> + </object> + </operation> + <operation name="CatalogSearchConfigQueryLength" dataType="catalog_search_config_query_length" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="catalog_search_config_query_length"> + <object key="search" dataType="catalog_search_config_query_length"> + <object key="fields" dataType="catalog_search_config_query_length"> + <object key="min_query_length" dataType="number"> + <field key="value">integer</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml new file mode 100644 index 0000000000000..605bcabb8a81d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/CatalogSearchAdminConfigSection.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogSearchConfigurationSection"> + <element name="minQueryLength" type="input" selector="#catalog_search_min_query_length"/> + <element name="minQueryLengthInherit" type="checkbox" selector="#catalog_search_min_query_length_inherit"/> + <element name="minQueryLengthHint" type="text" selector="#row_catalog_search_min_query_length .value span"/> + <element name="maxQueryLengthHint" type="text" selector="#row_catalog_search_max_query_length .value span"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml new file mode 100644 index 0000000000000..2fea5c988bf9d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/MinimalQueryLengthForCatalogSearchTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="MinimalQueryLengthForCatalogSearchTest"> + <annotations> + <features value="CatalogSearch"/> + <title value="Minimal query length for catalog search"/> + <description value="Minimal query length for catalog search"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-6325"/> + <useCaseId value="MAGETWO-58764"/> + <group value="CatalogSearch"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <createData entity="SetMinQueryLengthToDefault" stepKey="setMinimumQueryLengthToDefault"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="SetMinimalQueryLengthActionGroup" stepKey="setMinQueryLength"/> + <comment userInput="Go to Storefront and search for product" stepKey="searchProdUsingMinQueryLength"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="s" stepKey="fillAttribute"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInCategoryPage"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml index b8f2863139e9b..c358062b88a41 100644 --- a/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogSearch/etc/adminhtml/system.xml @@ -19,13 +19,15 @@ <field id="engine" canRestore="1"> <backend_model>Magento\CatalogSearch\Model\Adminhtml\System\Config\Backend\Engine</backend_model> </field> - <field id="min_query_length" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="min_query_length" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Minimal Query Length</label> <validate>validate-digits</validate> + <comment>This value must be compatible with the corresponding setting in the configured search engine. Be aware: a low query length limit may cause the performance impact.</comment> </field> - <field id="max_query_length" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="max_query_length" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Maximum Query Length</label> <validate>validate-digits</validate> + <comment>This value must be compatible with the corresponding setting in the configured search engine.</comment> </field> <field id="max_count_cacheable_search_terms" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Number of top search results to cache</label> diff --git a/app/code/Magento/CatalogSearch/etc/config.xml b/app/code/Magento/CatalogSearch/etc/config.xml index 66b79226c9f34..7ea15c6caa590 100644 --- a/app/code/Magento/CatalogSearch/etc/config.xml +++ b/app/code/Magento/CatalogSearch/etc/config.xml @@ -13,7 +13,7 @@ </seo> <search> <engine>mysql</engine> - <min_query_length>1</min_query_length> + <min_query_length>3</min_query_length> <max_query_length>128</max_query_length> <max_count_cacheable_search_terms>100</max_count_cacheable_search_terms> <autocomplete_limit>8</autocomplete_limit> diff --git a/app/code/Magento/CatalogSearch/i18n/en_US.csv b/app/code/Magento/CatalogSearch/i18n/en_US.csv index ba97dc9de1d31..f25d1a589d455 100644 --- a/app/code/Magento/CatalogSearch/i18n/en_US.csv +++ b/app/code/Magento/CatalogSearch/i18n/en_US.csv @@ -38,3 +38,5 @@ name,name "Maximum Query Length","Maximum Query Length" "Rebuild Catalog product fulltext search index","Rebuild Catalog product fulltext search index" "Please enter a valid price range.","Please enter a valid price range." +"This value must be compatible with the corresponding setting in the configured search engine. Be aware: a low query length limit may cause the performance impact.","This value must be compatible with the corresponding setting in the configured search engine. Be aware: a low query length limit may cause the performance impact." +"This value must be compatible with the corresponding setting in the configured search engine.","This value must be compatible with the corresponding setting in the configured search engine." \ No newline at end of file diff --git a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php index 86ae1f96749df..c622a48b7f2c8 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php +++ b/app/code/Magento/Config/Console/Command/ConfigSet/DefaultProcessor.php @@ -90,10 +90,10 @@ public function process($path, $value, $scope, $scopeCode) } try { - $config = $this->configFactory->create([ + $config = $this->configFactory->create(['data' => [ 'scope' => $scope, 'scope_code' => $scopeCode, - ]); + ]]); $config->setDataByPath($path, $value); $config->save(); } catch (\Exception $exception) { diff --git a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php index edb76c067bf35..35b2406b328cb 100644 --- a/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php +++ b/app/code/Magento/Config/Test/Unit/Console/Command/ConfigSet/DefaultProcessorTest.php @@ -103,7 +103,7 @@ public function testProcess($path, $value, $scope, $scopeCode) $config = $this->createMock(Config::class); $this->configFactory->expects($this->once()) ->method('create') - ->with(['scope' => $scope, 'scope_code' => $scopeCode]) + ->with(['data' => ['scope' => $scope, 'scope_code' => $scopeCode]]) ->willReturn($config); $config->expects($this->once()) ->method('setDataByPath') diff --git a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php index 79c2dd812acf1..2f07f8b90ce7e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php +++ b/app/code/Magento/ConfigurableProduct/Model/LinkManagement.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -11,6 +10,11 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; +/** + * Configurable product link management. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementInterface { /** @@ -68,7 +72,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getChildren($sku) { @@ -107,11 +111,15 @@ public function getChildren($sku) } /** - * {@inheritdoc} + * @inheritdoc + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function addChild($sku, $childSku) { - $product = $this->productRepository->get($sku); + $product = $this->productRepository->get($sku, true); $child = $this->productRepository->get($childSku); $childrenIds = array_values($this->configurableType->getChildrenIds($product->getId())[0]); @@ -150,7 +158,11 @@ public function addChild($sku, $childSku) } /** - * {@inheritdoc} + * @inheritdoc + * @throws InputException + * @throws NoSuchEntityException + * @throws StateException + * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function removeChild($sku, $childSku) { diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index 46f10608bc95e..a849d964eaed5 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -1232,6 +1232,8 @@ public function isPossibleBuyFromList($product) /** * Returns array of sub-products for specified configurable product * + * $requiredAttributeIds - one dimensional array, if provided + * * Result array contains all children for specified configurable product * * @param \Magento\Catalog\Model\Product $product @@ -1245,9 +1247,12 @@ public function getUsedProducts($product, $requiredAttributeIds = null) __METHOD__, $product->getData($metadata->getLinkField()), $product->getStoreId(), - $this->getCustomerSession()->getCustomerGroupId(), - $requiredAttributeIds + $this->getCustomerSession()->getCustomerGroupId() ]; + if ($requiredAttributeIds !== null) { + sort($requiredAttributeIds); + $keyParts[] = implode('', $requiredAttributeIds); + } $cacheKey = $this->getUsedProductsCacheKey($keyParts); return $this->loadUsedProducts($product, $cacheKey); } diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml deleted file mode 100644 index 23a067cd94eea..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontPasswordAutoCompleteOffActionGroup"> - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml deleted file mode 100644 index b12858fc1037e..0000000000000 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontClickSignInButtonActionGroup"> - <click stepKey="signIn" selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" /> - <waitForPageLoad stepKey="waitForStorefrontSignInPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml index 3cbd70d342824..11a47459ab7b3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml @@ -21,4 +21,11 @@ <entity name="GlobalCustomerAccountSharing" type="account_share_scope_value"> <data key="value">0</data> </entity> + + <entity name="CustomerAccountSharingSystemValue" type="customer_account_sharing_config_inherit"> + <requiredEntity type="account_share_scope_inherit">CustomerAccountSharingInherit</requiredEntity> + </entity> + <entity name="CustomerAccountSharingInherit" type="account_share_scope_inherit"> + <data key="inherit">true</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml index 41701bfac11ad..c3132b5b6a44f 100644 --- a/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml +++ b/app/code/Magento/Customer/Test/Mftf/Metadata/customer_config_account_sharing-meta.xml @@ -18,4 +18,16 @@ </object> </object> </operation> + <operation name="CustomerAccountShareConfigInherit" dataType="customer_account_sharing_config_inherit" type="create" auth="adminFormKey" url="/admin/system_config/save/section/customer/" + method="POST"> + <object key="groups" dataType="customer_account_sharing_config_inherit"> + <object key="account_share" dataType="customer_account_sharing_config_inherit"> + <object key="fields" dataType="customer_account_sharing_config_inherit"> + <object key="scope" dataType="account_share_scope_inherit"> + <field key="inherit">boolean</field> + </object> + </object> + </object> + </object> + </operation> </operations> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml index 9e104eb52cf90..a934d71397b8c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerConfigSection.xml @@ -8,5 +8,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerConfigSection"> <element name="customerDataLifetime" type="input" selector="#customer_online_customers_section_data_lifetime"/> + <element name="accountSharingOptionsTab" type="button" selector="#customer_account_share-head"/> + <element name="shareCustomerAccountInherit" type="checkbox" selector="#customer_account_share_scope_inherit"/> + <element name="shareCustomerAccount" type="select" selector="#customer_account_share_scope"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml index 25617ca05dd44..17a4a283c2648 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerFiltersSection.xml @@ -18,5 +18,6 @@ <element name="clearAll" type="button" selector=".admin__data-grid-header .action-tertiary.action-clear" timeout="30"/> <element name="viewDropdown" type="button" selector=".admin__data-grid-action-bookmarks button.admin__action-dropdown"/> <element name="viewBookmark" type="button" selector="//div[contains(@class, 'admin__data-grid-action-bookmarks')]/ul/li/div/a[text() = '{{label}}']" parameterized="true" timeout="30"/> + <element name="countryOptions" type="button" selector=".admin__data-grid-filters select[name=billing_country_id] option"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml new file mode 100644 index 0000000000000..f39394ef312e4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AllowedCountriesRestrictionApplyOnBackendTest"> + <annotations> + <title value="Country filter on Customers page when allowed countries restriction for a default website is applied"/> + <description value="Country filter on Customers page when allowed countries restriction for a default website is applied"/> + <features value="Customer"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6441"/> + <useCaseId value="MAGETWO-91523"/> + <group value="customer"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginActionGroup" stepKey="login"/> + <!--Create new website,store and store view--> + <comment userInput="Create new website,store and store view" stepKey="createWebsite"/> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="goToAdminSystemStorePage"/> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="adminCreateNewWebsite"> + <argument name="newWebsiteName" value="{{NewWebSiteData.name}}"/> + <argument name="websiteCode" value="{{NewWebSiteData.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="adminCreateNewStore"> + <argument name="website" value="{{NewWebSiteData.name}}"/> + <argument name="storeGroupName" value="{{NewStoreData.name}}"/> + <argument name="storeGroupCode" value="{{NewStoreData.code}}"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="adminCreateNewStoreView"> + <argument name="StoreGroup" value="NewStoreData"/> + </actionGroup> + <!--Set account sharing option - Default value is 'Per Website'--> + <comment userInput="Set account sharing option - Default value is 'Per Website'" stepKey="setAccountSharingOption"/> + <createData entity="CustomerAccountSharingDefault" stepKey="setToAccountSharingToDefault"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <!--delete all created data and set main website country options to default--> + <comment userInput="Delete all created data and set main website country options to default" stepKey="resetConfigToDefault"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> + <argument name="websiteName" value="{{NewWebSiteData.name}}"/> + </actionGroup> + <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigGeneralPage2"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchWebsiteActionGroup"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <actionGroup ref="SetWebsiteCountryOptionsToDefaultActionGroup" stepKey="setCountryOptionsToDefault"/> + <createData entity="CustomerAccountSharingSystemValue" stepKey="setAccountSharingToSystemValue"/> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + <!--Check that all countries are allowed initially and get amount--> + <comment userInput="Check that all countries are allowed initially and get amount" stepKey="checkAllCountriesAreAllowed"/> + <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigGeneralPage"/> + <createData entity="DisableAdminAccountAllowCountry" stepKey="setDefaultValueForAllowCountries"/> + <executeJS function="return document.querySelectorAll('{{CountryOptionsSection.allowedCountries}} option').length" stepKey="countriesAmount"/> + <!-- Create customer for US --> + <comment userInput="Create customer for US" stepKey="createUSCustomer"/> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + <!-- Switch to first website, allow only Canada and set Canada as default country --> + <comment userInput="Switch to first website, allow only Canada and set Canada as default country" stepKey="setCanadaAsDefaultCountry"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchWebsiteActionGroup"> + <argument name="website" value="_defaultWebsite"/> + </actionGroup> + <conditionalClick selector="{{CountryOptionsSection.countryOptions}}" dependentSelector="{{CountryOptionsSection.countryOptionsOpen}}" visible="false" stepKey="clickOnStoreInformation2"/> + <waitForElementVisible selector="{{CountryOptionsSection.allowedCountries}}" stepKey="waitAllowedCountriesToBeVisible"/> + <uncheckOption selector="{{CountryOptionsSection.generalCountryDefaultInherit}}" stepKey="uncheckCheckbox1"/> + <selectOption selector="{{CountryOptionsSection.generalCountryDefault}}" userInput="Canada" stepKey="chooseCanada1"/> + <uncheckOption selector="{{CountryOptionsSection.generalCountryAllowInherit}}" stepKey="uncheckCheckbox2"/> + <selectOption selector="{{CountryOptionsSection.allowedCountries}}" userInput="Canada" stepKey="chooseCanada2"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig2"/> + <waitForPageLoad stepKey="waitForSavingSystemConfiguration2"/> + <!--Switch to second website and allow all countries except Canada--> + <comment userInput="Switch to second website and allow all countries except Canada" stepKey="switchToWebsiteAndAllowOnlyCanada"/> + <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchWebsiteActionGroup2"> + <argument name="website" value="NewWebSiteData"/> + </actionGroup> + <conditionalClick selector="{{CountryOptionsSection.countryOptions}}" dependentSelector="{{CountryOptionsSection.countryOptionsOpen}}" visible="false" stepKey="clickOnStoreInformation3"/> + <waitForElementVisible selector="{{CountryOptionsSection.allowedCountries}}" stepKey="waitAllowedCountriesToBeVisible2"/> + <uncheckOption selector="{{CountryOptionsSection.generalCountryAllowInherit}}" stepKey="uncheckCheckbox3"/> + <unselectOption selector="{{CountryOptionsSection.allowedCountries}}" userInput="Canada" stepKey="unselectCanada"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig3"/> + <waitForPageLoad stepKey="waitForSavingSystemConfiguration3"/> + <amOnPage url="{{AdminEditCustomerPage.url($$createCustomer.id$$)}}" stepKey="goToCustomerEditPage"/> + <waitForPageLoad stepKey="waitPageToLoad"/> + <!--Open created customer details page and change US address to Canada address--> + <comment userInput="Open created customer details page and change US address to Canada address" stepKey="changeCustomerAddressToCanada"/> + <actionGroup ref="OpenEditCustomerAddressFromAdminActionGroup" stepKey="editCustomerAddress"> + <argument name="address" value="US_Address_CA"/> + </actionGroup> + <selectOption selector="{{AdminEditCustomerAddressesSection.country}}" userInput="Canada" stepKey="selectCountry"/> + <selectOption selector="{{AdminEditCustomerAddressesSection.state}}" userInput="Quebec" stepKey="selectState"/> + <click selector="{{AdminEditCustomerAddressesSection.save}}" stepKey="saveAddress"/> + <waitForPageLoad stepKey="waitForAddressSaved"/> + <click stepKey="saveCustomer" selector="{{AdminCustomerAccountInformationSection.saveCustomerAndContinueEdit}}"/> + <waitForPageLoad stepKey="waitForCustomersPage"/> + <!--Go to Customers grid and check that filter countries amount is the same as initial allowed countries amount--> + <comment userInput="Go to Customers grid and check that filter countries amount is the same as initial allowed countries amount" stepKey="compareCountriesAmount"/> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersGrid"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomersGrid"/> + <executeJS function="var len = document.querySelectorAll('{{AdminCustomerFiltersSection.countryOptions}}').length; return len-1;" stepKey="countriesAmount2"/> + <assertEquals expected='($countriesAmount)' expectedType="integer" actual="($countriesAmount2)" stepKey="assertCountryAmounts"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml index 6b479ad1cb290..f845d407d401a 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_listing.xml @@ -174,6 +174,7 @@ </column> <column name="billing_country_id" component="Magento_Ui/js/grid/columns/select" sortOrder="80"> <settings> + <options class="Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites"/> <filter>select</filter> <dataType>select</dataType> <label translate="true">Country</label> diff --git a/app/code/Magento/GraphQl/Controller/GraphQl.php b/app/code/Magento/GraphQl/Controller/GraphQl.php index c04bb7f5775a0..9e27ca5d608f0 100644 --- a/app/code/Magento/GraphQl/Controller/GraphQl.php +++ b/app/code/Magento/GraphQl/Controller/GraphQl.php @@ -12,6 +12,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\GraphQl\Exception\ExceptionFormatter; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\QueryProcessor; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\SchemaGeneratorInterface; @@ -47,12 +48,12 @@ class GraphQl implements FrontControllerInterface private $queryProcessor; /** - * @var \Magento\Framework\GraphQl\Exception\ExceptionFormatter + * @var ExceptionFormatter */ private $graphQlError; /** - * @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface + * @var ContextInterface */ private $resolverContext; @@ -71,8 +72,8 @@ class GraphQl implements FrontControllerInterface * @param SchemaGeneratorInterface $schemaGenerator * @param SerializerInterface $jsonSerializer * @param QueryProcessor $queryProcessor - * @param \Magento\Framework\GraphQl\Exception\ExceptionFormatter $graphQlError - * @param \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $resolverContext + * @param ExceptionFormatter $graphQlError + * @param ContextInterface $resolverContext * @param HttpRequestProcessor $requestProcessor * @param QueryFields $queryFields */ @@ -107,12 +108,14 @@ public function dispatch(RequestInterface $request) : ResponseInterface $statusCode = 200; try { /** @var Http $request */ + $this->requestProcessor->validateRequest($request); $this->requestProcessor->processHeaders($request); - $data = $this->jsonSerializer->unserialize($request->getContent()); - $query = isset($data['query']) ? $data['query'] : ''; - $variables = isset($data['variables']) ? $data['variables'] : null; - // We have to extract queried field names to avoid instantiation of non necessary fields in webonyx schema + $data = $this->getDataFromRequest($request); + $query = $data['query'] ?? ''; + $variables = $data['variables'] ?? null; + + // We must extract queried field names to avoid instantiation of unnecessary fields in webonyx schema // Temporal coupling is required for performance optimization $this->queryFields->setQuery($query, $variables); $schema = $this->schemaGenerator->generate(); @@ -121,7 +124,7 @@ public function dispatch(RequestInterface $request) : ResponseInterface $schema, $query, $this->resolverContext, - isset($data['variables']) ? $data['variables'] : [] + $data['variables'] ?? [] ); } catch (\Exception $error) { $result['errors'] = isset($result) && isset($result['errors']) ? $result['errors'] : []; @@ -134,4 +137,26 @@ public function dispatch(RequestInterface $request) : ResponseInterface )->setHttpResponseCode($statusCode); return $this->response; } + + /** + * Get data from request body or query string + * + * @param RequestInterface $request + * @return array + */ + private function getDataFromRequest(RequestInterface $request) : array + { + /** @var Http $request */ + if ($request->isPost()) { + $data = $this->jsonSerializer->unserialize($request->getContent()); + } elseif ($request->isGet()) { + $data = $request->getParams(); + $data['variables'] = isset($data['variables']) ? + $this->jsonSerializer->unserialize($data['variables']) : null; + } else { + return []; + } + + return $data; + } } diff --git a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/ContentTypeProcessor.php b/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/ContentTypeProcessor.php deleted file mode 100644 index 2270f2616e67b..0000000000000 --- a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/ContentTypeProcessor.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\Controller\HttpHeaderProcessor; - -use Magento\Framework\Exception\LocalizedException; -use Magento\GraphQl\Controller\HttpHeaderProcessorInterface; - -/** - * Processes the "Content-Type" header entry - */ -class ContentTypeProcessor implements HttpHeaderProcessorInterface -{ - /** - * Handle the mandatory application/json header - * - * {@inheritDoc} - * @throws LocalizedException - */ - public function processHeaderValue(string $headerValue) : void - { - if (!$headerValue || strpos($headerValue, 'application/json') === false) { - throw new LocalizedException( - new \Magento\Framework\Phrase('Request content type must be application/json') - ); - } - } -} diff --git a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php b/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php index be359eafdf246..246ad15379f85 100644 --- a/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php +++ b/app/code/Magento/GraphQl/Controller/HttpHeaderProcessor/StoreProcessor.php @@ -7,7 +7,7 @@ namespace Magento\GraphQl\Controller\HttpHeaderProcessor; -use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\App\HttpRequestInterface; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Controller\HttpHeaderProcessorInterface; use Magento\Store\Model\StoreManagerInterface; @@ -35,8 +35,9 @@ public function __construct(StoreManagerInterface $storeManager) /** * Handle the value of the store and set the scope * - * {@inheritDoc} - * @throws NoSuchEntityException + * @param string $headerValue + * @return void + * @throws GraphQlInputException */ public function processHeaderValue(string $headerValue) : void { diff --git a/app/code/Magento/GraphQl/Controller/HttpRequestProcessor.php b/app/code/Magento/GraphQl/Controller/HttpRequestProcessor.php index 5e42b81a61981..bb29f1fa68af9 100644 --- a/app/code/Magento/GraphQl/Controller/HttpRequestProcessor.php +++ b/app/code/Magento/GraphQl/Controller/HttpRequestProcessor.php @@ -19,12 +19,19 @@ class HttpRequestProcessor */ private $headerProcessors = []; + /** + * @var HttpRequestValidatorInterface[] array + */ + private $requestValidators = []; + /** * @param HttpHeaderProcessorInterface[] $graphQlHeaders + * @param HttpRequestValidatorInterface[] $requestValidators */ - public function __construct(array $graphQlHeaders = []) + public function __construct(array $graphQlHeaders = [], array $requestValidators = []) { $this->headerProcessors = $graphQlHeaders; + $this->requestValidators = $requestValidators; } /** @@ -39,4 +46,17 @@ public function processHeaders(Http $request) : void $headerClass->processHeaderValue((string)$request->getHeader($headerName)); } } + + /** + * Validate HTTP request + * + * @param Http $request + * @return void + */ + public function validateRequest(Http $request) : void + { + foreach ($this->requestValidators as $requestValidator) { + $requestValidator->validate($request); + } + } } diff --git a/app/code/Magento/GraphQl/Controller/HttpRequestValidator/ContentTypeValidator.php b/app/code/Magento/GraphQl/Controller/HttpRequestValidator/ContentTypeValidator.php new file mode 100644 index 0000000000000..555048aac6771 --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpRequestValidator/ContentTypeValidator.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Controller\HttpRequestValidator; + +use Magento\Framework\App\HttpRequestInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\GraphQl\Controller\HttpRequestValidatorInterface; + +/** + * Processes the "Content-Type" header entry + */ +class ContentTypeValidator implements HttpRequestValidatorInterface +{ + /** + * Handle the mandatory application/json header + * + * @param HttpRequestInterface $request + * @return void + * @throws GraphQlInputException + */ + public function validate(HttpRequestInterface $request) : void + { + $headerName = 'Content-Type'; + $requiredHeaderValue = 'application/json'; + + $headerValue = (string)$request->getHeader($headerName); + if ($request->isPost() + && strpos($headerValue, $requiredHeaderValue) === false + ) { + throw new GraphQlInputException( + new \Magento\Framework\Phrase('Request content type must be application/json') + ); + } + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpRequestValidator/HttpVerbValidator.php b/app/code/Magento/GraphQl/Controller/HttpRequestValidator/HttpVerbValidator.php new file mode 100644 index 0000000000000..300b3d4f44dca --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpRequestValidator/HttpVerbValidator.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Controller\HttpRequestValidator; + +use Magento\Framework\App\HttpRequestInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\App\Request\Http; +use Magento\GraphQl\Controller\HttpRequestValidatorInterface; + +/** + * Validator to check HTTP verb for Graphql requests + */ +class HttpVerbValidator implements HttpRequestValidatorInterface +{ + /** + * Check if request is using correct verb for query or mutation + * + * @param HttpRequestInterface $request + * @return void + * @throws GraphQlInputException + */ + public function validate(HttpRequestInterface $request) : void + { + /** @var Http $request */ + if (false === $request->isPost()) { + $query = $request->getParam('query', ''); + // The easiest way to determine mutations without additional parsing + if (strpos(trim($query), 'mutation') === 0) { + throw new GraphQlInputException( + new \Magento\Framework\Phrase('Mutation requests allowed only for POST requests') + ); + } + } + } +} diff --git a/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php b/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php new file mode 100644 index 0000000000000..c0873b0caff89 --- /dev/null +++ b/app/code/Magento/GraphQl/Controller/HttpRequestValidatorInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Controller; + +use Magento\Framework\App\HttpRequestInterface; + +/** + * Use this interface to implement a validator for a Graphql HTTP requests + */ +interface HttpRequestValidatorInterface +{ + /** + * Perform validation of request + * + * @param HttpRequestInterface $request + * @return void + */ + public function validate(HttpRequestInterface $request) : void; +} diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index f4e6ca59364b2..b4f0113f58776 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -28,9 +28,12 @@ <type name="Magento\GraphQl\Controller\HttpRequestProcessor"> <arguments> <argument name="graphQlHeaders" xsi:type="array"> - <item name="Content-Type" xsi:type="object">Magento\GraphQl\Controller\HttpHeaderProcessor\ContentTypeProcessor</item> <item name="Store" xsi:type="object">Magento\GraphQl\Controller\HttpHeaderProcessor\StoreProcessor</item> </argument> + <argument name="requestValidators" xsi:type="array"> + <item name="ContentTypeValidator" xsi:type="object">Magento\GraphQl\Controller\HttpRequestValidator\ContentTypeValidator</item> + <item name="VerbValidator" xsi:type="object">Magento\GraphQl\Controller\HttpRequestValidator\HttpVerbValidator</item> + </argument> </arguments> </type> </config> diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index d115aea7f2ff9..2a4d6904b11b5 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -126,6 +126,11 @@ class Import extends \Magento\ImportExport\Model\AbstractModel */ protected $_importExportData = null; + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $_coreConfig; + /** * @var \Magento\ImportExport\Model\Import\ConfigInterface */ diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml index da553b7823da9..4354cfb7c1c3e 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml @@ -55,6 +55,10 @@ <div class="box-content"> <address> <?= /* @noEscape */ $block->getCheckoutData()->getAddressHtml($block->getAddress()); ?> + <input type="hidden" + id="multishipping_billing_country_id" + value="<?= /* @noEscape */ $block->getAddress()->getCountryId(); ?>" + name="multishipping_billing_country_id"/> </address> </div> </div> @@ -80,35 +84,44 @@ $block->setMethodFormTemplate($code, $methodsForms[$code]); } ?> - <dt class="item-title"> - <?php if ($methodsCount > 1) : ?> - <input type="radio" - id="p_method_<?= $block->escapeHtml($code); ?>" - value="<?= $block->escapeHtml($code); ?>" - name="payment[method]" - title="<?= $block->escapeHtml($_method->getTitle()) ?>" - <?php if ($checked) : ?> - checked="checked" + <div data-bind="scope: 'payment_method_<?= $block->escapeHtml($code);?>'"> + <dt class="item-title"> + <?php if ($methodsCount > 1) : ?> + <input type="radio" + id="p_method_<?= $block->escapeHtml($code); ?>" + value="<?= $block->escapeHtml($code); ?>" + name="payment[method]" + title="<?= $block->escapeHtml($_method->getTitle()) ?>" + data-bind=" + value: getCode(), + checked: isChecked, + click: selectPaymentMethod, + visible: isRadioButtonVisible()" + <?php if ($checked) : ?> + checked="checked" + <?php endif; ?> + class="radio"/> + <?php else : ?> + <input type="radio" + id="p_method_<?= $block->escapeHtml($code); ?>" + value="<?= $block->escapeHtml($code); ?>" + name="payment[method]" + data-bind=" + value: getCode(), + afterRender: selectPaymentMethod" + checked="checked" + class="radio solo method" /> <?php endif; ?> - class="radio"/> - <?php else : ?> - <input type="radio" - id="p_method_<?= $block->escapeHtml($code); ?>" - value="<?= $block->escapeHtml($code); ?>" - name="payment[method]" - checked="checked" - class="radio solo method" /> - <?php endif; ?> - <label for="p_method_<?= $block->escapeHtml($code); ?>"> - <?= $block->escapeHtml($_method->getTitle()) ?> - </label> - </dt> - <?php if ($html = $block->getChildHtml('payment.method.' . $code)) : ?> - <dd class="item-content <?= $checked ? '' : 'no-display'; ?>" - data-bind="scope: 'payment_method_<?= $block->escapeHtml($code);?>'"> - <?= /* @noEscape */ $html; ?> - </dd> - <?php endif; ?> + <label for="p_method_<?= $block->escapeHtml($code); ?>"> + <?= $block->escapeHtml($_method->getTitle()) ?> + </label> + </dt> + <?php if ($html = $block->getChildHtml('payment.method.' . $code)) : ?> + <dd class="item-content <?= $checked ? '' : 'no-display'; ?>"> + <?= /* @noEscape */ $html; ?> + </dd> + <?php endif; ?> + </div> <?php endforeach; ?> </dl> <?= $block->getChildHtml('payment_methods_after') ?> diff --git a/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml new file mode 100644 index 0000000000000..32810ecef20da --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/frontend/layout/multishipping_checkout_billing.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceBlock name="checkout_billing"> + <arguments> + <argument name="form_templates" xsi:type="array"> + <item name="checkmo" xsi:type="string">Magento_OfflinePayments::multishipping/checkmo_form.phtml</item> + </argument> + </arguments> + </referenceBlock> + </body> +</page> diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml new file mode 100644 index 0000000000000..b96918243a7a7 --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/frontend/templates/multishipping/checkmo_form.phtml @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +?> +<script> + require([ + 'uiLayout', + 'jquery' + ], function (layout, $) { + $(function () { + var paymentMethodData = { + method: 'checkmo' + }; + layout([ + { + component: 'Magento_Checkout/js/view/payment/default', + name: 'payment_method_checkmo', + method: paymentMethodData.method, + item: paymentMethodData + } + ]); + + $('body').trigger('contentUpdated'); + }) + }) +</script> diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl index 8068447e5ca99..c73c4e39170e6 100644 --- a/app/code/Magento/PageCache/etc/varnish4.vcl +++ b/app/code/Magento/PageCache/etc/varnish4.vcl @@ -91,10 +91,11 @@ sub vcl_recv { } } - # Remove Google gclid parameters to minimize the cache objects - set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA" - set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar" - set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz" + # Remove all marketing get parameters to minimize the cache objects + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + set req.url = regsub(req.url, "[?|&]+$", ""); + } # Static files caching if (req.url ~ "^/(pub/)?(media|static)/") { diff --git a/app/code/Magento/PageCache/etc/varnish5.vcl b/app/code/Magento/PageCache/etc/varnish5.vcl index 6c8414a5cb641..ea586858eff75 100644 --- a/app/code/Magento/PageCache/etc/varnish5.vcl +++ b/app/code/Magento/PageCache/etc/varnish5.vcl @@ -92,10 +92,11 @@ sub vcl_recv { } } - # Remove Google gclid parameters to minimize the cache objects - set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA" - set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar" - set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz" + # Remove all marketing get parameters to minimize the cache objects + if (req.url ~ "(\?|&)(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=") { + set req.url = regsuball(req.url, "(gclid|cx|ie|cof|siteurl|zanpid|origin|mc_[a-z]+|utm_[a-z]+)=[-_A-z0-9+()%.]+&?", ""); + set req.url = regsub(req.url, "[?|&]+$", ""); + } # Static files caching if (req.url ~ "^/(pub/)?(media|static)/") { diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml index 425e4cffb666c..694e517816b22 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/paypal_payflowpro_with_express_checkout.xml @@ -6,7 +6,7 @@ */ --> <include xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_include.xsd"> - <group id="paypal_payflowpro_with_express_checkout" translate="label comment" extends="payment_all_paypal/paypal_payflowpro"> + <group id="paypal_payflowpro_with_express_checkout" translate="label" extends="payment_all_paypal/paypal_payflowpro"> <label>Payflow Pro</label> <attribute type="paypal_ec_separate">0</attribute> <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> @@ -30,7 +30,7 @@ <field id="enable_paypal_payflow"/> </requires> </field> - <field id="enable_express_checkout_bml_payflow" translate="label" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> + <field id="enable_express_checkout_bml_payflow" translate="label comment" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> <label>Enable PayPal Credit</label> <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. diff --git a/app/code/Magento/Paypal/i18n/en_US.csv b/app/code/Magento/Paypal/i18n/en_US.csv index ad07d642de127..e7264a6de807f 100644 --- a/app/code/Magento/Paypal/i18n/en_US.csv +++ b/app/code/Magento/Paypal/i18n/en_US.csv @@ -697,6 +697,11 @@ User,User The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% or more. <a href=""https://financing.paypal.com/ppfinportal/content/forrester"" target=""_blank"">See Details</a>. " +"PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. + You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a>","PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. + You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. + <a href=""https://www.paypal.com/webapps/mpp/promotional-financing"" target=""_blank"">Learn More</a>" "Customize Smart Buttons","Customize Smart Buttons" "Checkout Page","Checkout Page" "Label","Label" @@ -731,4 +736,4 @@ User,User "PayPal will automatically display each enabled funding option to eligible buyers. For example, PayPal Credit is only shown to buyers in countries where PayPal Credit is offered and the currency offered by the merchant is USD.","PayPal will automatically display each enabled funding option to eligible buyers. For example, PayPal Credit is only shown to buyers in countries where PayPal Credit is offered and the currency offered by the merchant is USD." "PayPal Credit","PayPal Credit" "PayPal Guest Checkout Credit Card Icons","PayPal Guest Checkout Credit Card Icons" -"Elektronisches Lastschriftverfahren - German ELV","Elektronisches Lastschriftverfahren - German ELV" \ No newline at end of file +"Elektronisches Lastschriftverfahren - German ELV","Elektronisches Lastschriftverfahren - German ELV" diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index 145e9c01980a5..3506ffc8c8792 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -13,6 +13,7 @@ use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; /** * Get cart @@ -29,16 +30,24 @@ class GetCartForUser */ private $cartRepository; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository + * @param StoreManagerInterface $storeManager */ public function __construct( MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, - CartRepositoryInterface $cartRepository + CartRepositoryInterface $cartRepository, + StoreManagerInterface $storeManager ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; + $this->storeManager = $storeManager; } /** @@ -75,6 +84,15 @@ public function execute(string $cartHash, ?int $customerId): Quote ); } + if ((int)$cart->getStoreId() !== (int)$this->storeManager->getStore()->getId()) { + throw new GraphQlNoSuchEntityException( + __( + 'Wrong store code specified for cart "%masked_cart_id"', + ['masked_cart_id' => $cartHash] + ) + ); + } + $cartCustomerId = (int)$cart->getCustomerId(); /* Guest cart, allow operations */ diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php index 582055bc6e13d..13d6a1d3dce70 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -7,6 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; +use Magento\Customer\Helper\Address as AddressHelper; use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddress; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; @@ -30,16 +31,24 @@ class QuoteAddressFactory */ private $getCustomerAddress; + /** + * @var AddressHelper + */ + private $addressHelper; + /** * @param BaseQuoteAddressFactory $quoteAddressFactory * @param GetCustomerAddress $getCustomerAddress + * @param AddressHelper $addressHelper */ public function __construct( BaseQuoteAddressFactory $quoteAddressFactory, - GetCustomerAddress $getCustomerAddress + GetCustomerAddress $getCustomerAddress, + AddressHelper $addressHelper ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->getCustomerAddress = $getCustomerAddress; + $this->addressHelper = $addressHelper; } /** @@ -47,11 +56,19 @@ public function __construct( * * @param array $addressInput * @return QuoteAddress + * @throws GraphQlInputException */ public function createBasedOnInputData(array $addressInput): QuoteAddress { $addressInput['country_id'] = $addressInput['country_code'] ?? ''; + $maxAllowedLineCount = $this->addressHelper->getStreetLines(); + if (is_array($addressInput['street']) && count($addressInput['street']) > $maxAllowedLineCount) { + throw new GraphQlInputException( + __('"Street Address" cannot contain more than %1 lines.', $maxAllowedLineCount) + ); + } + $quoteAddress = $this->quoteAddressFactory->create(); $quoteAddress->addData($addressInput); return $quoteAddress; diff --git a/app/code/Magento/ReleaseNotification/etc/di.xml b/app/code/Magento/ReleaseNotification/etc/di.xml index 1404a6adb0a10..a4c434ff7f623 100644 --- a/app/code/Magento/ReleaseNotification/etc/di.xml +++ b/app/code/Magento/ReleaseNotification/etc/di.xml @@ -6,7 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> - <preference for="Magento\Framework\HTTP\ClientInterface" type="Magento\Framework\HTTP\Client\Curl" /> <preference for="Magento\ReleaseNotification\Model\ContentProviderInterface" type="Magento\ReleaseNotification\Model\ContentProvider\Http\HttpContentProvider" /> <type name="Magento\Config\Model\Config\TypePool"> <arguments> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php index 4bd2227d4bb1e..9a271f741edda 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid.php @@ -5,12 +5,17 @@ */ namespace Magento\Sales\Block\Adminhtml\Order\Create\Search; +use Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider\ProductCollection + as ProductCollectionDataProvider; +use Magento\Framework\App\ObjectManager; + /** * Adminhtml sales order create search products block * * @api * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended { @@ -42,6 +47,11 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended */ protected $_productFactory; + /** + * @var ProductCollectionDataProvider $productCollectionProvider + */ + private $productCollectionProvider; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Backend\Helper\Data $backendHelper @@ -50,6 +60,7 @@ class Grid extends \Magento\Backend\Block\Widget\Grid\Extended * @param \Magento\Backend\Model\Session\Quote $sessionQuote * @param \Magento\Sales\Model\Config $salesConfig * @param array $data + * @param ProductCollectionDataProvider|null $productCollectionProvider */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -58,12 +69,15 @@ public function __construct( \Magento\Catalog\Model\Config $catalogConfig, \Magento\Backend\Model\Session\Quote $sessionQuote, \Magento\Sales\Model\Config $salesConfig, - array $data = [] + array $data = [], + ProductCollectionDataProvider $productCollectionProvider = null ) { $this->_productFactory = $productFactory; $this->_catalogConfig = $catalogConfig; $this->_sessionQuote = $sessionQuote; $this->_salesConfig = $salesConfig; + $this->productCollectionProvider = $productCollectionProvider + ?: ObjectManager::getInstance()->get(ProductCollectionDataProvider::class); parent::__construct($context, $backendHelper, $data); } @@ -140,20 +154,18 @@ protected function _addColumnFilterToCollection($column) */ protected function _prepareCollection() { + $attributes = $this->_catalogConfig->getProductAttributes(); + $store = $this->getStore(); + /* @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */ - $collection = $this->_productFactory->create()->getCollection(); - $collection->setStore( - $this->getStore() - )->addAttributeToSelect( + $collection = $this->productCollectionProvider->getCollectionForStore($store); + $collection->addAttributeToSelect( $attributes - )->addAttributeToSelect( - 'sku' - )->addStoreFilter()->addAttributeToFilter( + ); + $collection->addAttributeToFilter( 'type_id', $this->_salesConfig->getAvailableProductTypes() - )->addAttributeToSelect( - 'gift_message_available' ); $this->setCollection($collection); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php new file mode 100644 index 0000000000000..733791a2f9549 --- /dev/null +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Search/Grid/DataProvider/ProductCollection.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Create\Search\Grid\DataProvider; + +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Store\Model\Store; + +/** + * Prepares product collection for the grid + */ +class ProductCollection +{ + /** + * @var ProductCollectionFactory + */ + private $collectionFactory; + + /** + * @param ProductCollectionFactory $collectionFactory + */ + public function __construct( + ProductCollectionFactory $collectionFactory + ) { + $this->collectionFactory = $collectionFactory; + } + + /** + * Provide products collection filtered with store + * + * @param Store $store + * @return Collection + */ + public function getCollectionForStore(Store $store):Collection + { + /** @var Collection $collection */ + $collection = $this->collectionFactory->create(); + + $collection->setStore($store); + $collection->addAttributeToSelect( + 'gift_message_available' + ); + $collection->addAttributeToSelect( + 'sku' + ); + $collection->addStoreFilter(); + + return $collection; + } +} diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml index b0a88b8fa37dc..d1a90783c68c7 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml @@ -89,11 +89,8 @@ endif; ?> <?= $block->getForm()->toHtml() ?> <div class="admin__field admin__field-option order-save-in-address-book"> - <input name="<?= $block->getForm()->getHtmlNamePrefix() ?>[save_in_address_book]" type="checkbox" - id="<?= $block->getForm()->getHtmlIdPrefix() ?>save_in_address_book" - value="1" - <?php if (!$block->getDontSaveInAddressBook() && $block->getAddress()->getSaveInAddressBook()): ?> checked="checked"<?php endif; ?> - class="admin__control-checkbox"/> + <input name="<?= $block->getForm()->getHtmlNamePrefix() ?>[save_in_address_book]" type="checkbox" id="<?= $block->getForm()->getHtmlIdPrefix() ?>save_in_address_book" value="1" + <?php if (!$block->getDontSaveInAddressBook()): ?> checked="checked"<?php endif; ?> class="admin__control-checkbox"/> <label for="<?= $block->getForm()->getHtmlIdPrefix() ?>save_in_address_book" class="admin__field-label"><?= /* @escapeNotVerified */ __('Save in address book') ?></label> </div> diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml index 81b025c9554e2..ff9fce57c4524 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml @@ -12,6 +12,7 @@ <element name="searchTextBox" type="text" selector="#search"/> <element name="searchTextBoxButton" type="button" selector="button[class='action search']"/> <element name="productLink" type="select" selector="a[class='product-item-link']"/> + <element name="productSpecialPrice" type="text" selector="//a[contains(text(), '{{productName}}')]/ancestor::div//span[contains(@data-price-type, 'finalPrice')]/span[contains(@class, 'price')]" parameterized="true"/> <element name="asLowAsLabel" type="text" selector=".minimal-price-link > span"/> <element name="textArea" type="text" selector="li[class='item']"/> <element name="regularPrice" type="text" selector="//span[@class='price-wrapper ']/span[@class='price']"/> diff --git a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php index 9304d25cc8a98..d16b1eaad7f37 100644 --- a/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php +++ b/app/code/Magento/Ui/TemplateEngine/Xhtml/Result.php @@ -115,7 +115,7 @@ public function __toString() $this->compiler->compile($templateRootElement, $this->component, $this->component); $this->appendLayoutConfiguration(); $result = $this->compiler->postprocessing($this->template->__toString()); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->logger->critical($e->getMessage()); $result = $e->getMessage(); } diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml index 9148c22976c19..fbb543a6cab92 100644 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml @@ -23,8 +23,23 @@ </arguments> <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> <click selector="{{AdminDataGridPaginationSection.perPageOption('Custom')}}" stepKey="selectCustomPerPage"/> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageInput}}" time="30" stepKey="waitForInputVisible"/> <fillField selector="{{AdminDataGridPaginationSection.perPageInput}}" userInput="{{perPage}}" stepKey="fillCustomPerPage"/> <click selector="{{AdminDataGridPaginationSection.perPageApplyInput}}" stepKey="applyCustomPerPage"/> <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <seeInField selector="{{AdminDataGridPaginationSection.perPageDropDownValue}}" userInput="{{perPage}}" stepKey="seePerPageValueInDropDown"/> + </actionGroup> + + <actionGroup name="adminDataGridDeleteCustomPerPage"> + <arguments> + <argument name="perPage"/> + </arguments> + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown1"/> + <click selector="{{AdminDataGridPaginationSection.perPageEditCustomValue(perPage)}}" stepKey="clickToEditCustomPerPage"/> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" time="30" stepKey="waitForDeleteButtonVisible"/> + <click selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" stepKey="clickToDeleteCustomPerPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> + <dontSeeElement selector="{{AdminDataGridPaginationSection.perPageDropDownItem(perPage)}}" stepKey="dontSeeDropDownItem"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml index 0f54f51549e7a..133836761174d 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml @@ -11,11 +11,15 @@ <section name="AdminDataGridPaginationSection"> <element name="perPageDropdown" type="select" selector=".admin__data-grid-pager-wrap .selectmenu"/> <element name="perPageOption" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//button[text()='{{var1}}']" parameterized="true"/> - <element name="perPageInput" type="input" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//div[@class='selectmenu-item-edit']//input"/> - <element name="perPageApplyInput" type="button" selector="//div[@class='admin__data-grid-pager-wrap']//div[@class='selectmenu-items _active']//li//div[@class='selectmenu-item-edit']//button"/> + <element name="perPageInput" type="input" selector="//div[contains(@class, 'admin__data-grid-pager-wrap')]//div[contains(@class, 'selectmenu-items _active')]//li[contains(@class, '_edit')]//div[contains(@class, 'selectmenu-item-edit')]//input"/> + <element name="perPageApplyInput" type="button" selector="//div[contains(@class, 'admin__data-grid-pager-wrap')]//div[contains(@class, 'selectmenu-items _active')]//li[@class='_edit']//div[contains(@class, 'selectmenu-item-edit')]//button"/> + <element name="perPageDropDownItem" type="button" selector="//*[contains(@class, 'selectmenu-items _active')]//button[contains(@class, 'selectmenu-item-action') and text()='{{dropDownItem}}']" timeout="30" parameterized="true"/> + <element name="perPageEditCustomValue" type="button" selector="//div[contains(@class, 'selectmenu-items _active')]//div[contains(@class, 'selectmenu-item')]//button[text()='{{perPageCustomValue}}']/following-sibling::button[contains(@class, 'action-edit')]" parameterized="true"/> + <element name="perPageDeleteCustomValue" type="button" selector="//div[contains(@class, 'selectmenu-items _active')]//div[contains(@class, 'selectmenu-item')]//button[text()='{{perPageCustomValue}}']/parent::div/preceding-sibling::div/button[contains(@class, 'action-delete')]" parameterized="true"/> <element name="nextPage" type="button" selector="div.admin__data-grid-pager > button.action-next" timeout="30"/> <element name="previousPage" type="button" selector="div.admin__data-grid-pager > button.action-previous" timeout="30"/> <element name="currentPage" type="input" selector="div.admin__data-grid-pager > input[data-ui-id='current-page-input']"/> <element name="totalPages" type="text" selector="div.admin__data-grid-pager > label"/> + <element name="perPageDropDownValue" type="input" selector=".selectmenu-value input" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index abd79e797e413..8e6f1496495c7 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -19,7 +19,9 @@ define([ defaults: { template: 'ui/grid/paging/paging', totalTmpl: 'ui/grid/paging-total', + totalRecords: 0, pageSize: 20, + pages: 1, current: 1, selectProvider: 'ns = ${ $.ns }, index = ids', @@ -35,7 +37,8 @@ define([ imports: { pageSize: '${ $.sizesConfig.name }:value', totalSelected: '${ $.selectProvider }:totalSelected', - totalRecords: '${ $.provider }:data.totalRecords' + totalRecords: '${ $.provider }:data.totalRecords', + filters: '${ $.provider }:params.filters' }, exports: { @@ -43,6 +46,11 @@ define([ current: '${ $.provider }:params.paging.current' }, + statefull: { + pageSize: true, + current: true + }, + listens: { 'pages': 'onPagesChange', 'pageSize': 'onPageSizeChange', @@ -173,7 +181,9 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - this.current = 1; + if (!_.isUndefined(this.filters)) { + this.current = 1; + } return this; }, @@ -219,13 +229,11 @@ define([ /** * Calculates new page cursor based on the * previous and current page size values. - * - * @returns {Number} Updated cursor value. */ updateCursor: function () { var cursor = this.current - 1, size = this.pageSize, - oldSize = this.previousSize, + oldSize = _.isUndefined(this.previousSize) ? this.pageSize : this.previousSize, delta = cursor * (oldSize - size) / size; delta = size > oldSize ? diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml b/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml deleted file mode 100644 index 0738b17d6e0f0..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/MetaData/url_rewrite-meta.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="CreateUrlRewrite" dataType="urlRewrite" type="create" auth="adminFormKey" url="/admin/url_rewrite/save/" method="POST" successRegex="/messages-message-success/"> - <field key="store_id">integer</field> - <field key="redirect_type">integer</field> - <field key="request_path">string</field> - <field key="target_path">string</field> - <field key="description">string</field> - </operation> -</operations> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 7453b7b5a43f3..7e1b9acbc47ab 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -10,7 +10,7 @@ <test name="AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest"> <annotations> <stories value="Update URL rewrite"/> - <title value="Update Category URL Rewrites, no redirect type"/> + <title value="Update Category URL Rewrites, Temporary redirect type"/> <description value="Login as Admin and update category UrlRewrite and add Temporary redirect type"/> <testCaseId value="MC-5356"/> <severity value="CRITICAL"/> diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index cc2af4996d562..32bae10c801c8 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -91,7 +91,7 @@ public function getMainFieldset() if ($this->_getData('main_fieldset') instanceof \Magento\Framework\Data\Form\Element\Fieldset) { return $this->_getData('main_fieldset'); } - $mainFieldsetHtmlId = 'options_fieldset' . md5($this->getWidgetType()); + $mainFieldsetHtmlId = 'options_fieldset' . hash('sha256', $this->getWidgetType()); $this->setMainFieldsetHtmlId($mainFieldsetHtmlId); $fieldset = $this->getForm()->addFieldset( $mainFieldsetHtmlId, @@ -141,7 +141,6 @@ protected function _addField($parameter) { $form = $this->getForm(); $fieldset = $this->getMainFieldset(); - //$form->getElement('options_fieldset'); // prepare element data with values (either from request of from default values) $fieldName = $parameter->getKey(); @@ -166,9 +165,13 @@ protected function _addField($parameter) if (is_array($data['value'])) { foreach ($data['value'] as &$value) { - $value = html_entity_decode($value); + if (is_string($value)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $value = html_entity_decode($value); + } } } else { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $data['value'] = html_entity_decode($data['value']); } diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 52dc8e7837a3c..d07e84186b2c9 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -248,17 +248,13 @@ public function getWidgets($filters = []) $result = $widgets; // filter widgets by params - if (is_array($filters) && count($filters) > 0) { + if (is_array($filters) && !empty($filters)) { foreach ($widgets as $code => $widget) { - try { - foreach ($filters as $field => $value) { - if (!isset($widget[$field]) || (string)$widget[$field] != $value) { - throw new \Exception(); - } + foreach ($filters as $field => $value) { + if (!isset($widget[$field]) || (string)$widget[$field] != $value) { + unset($result[$code]); + break; } - } catch (\Exception $e) { - unset($result[$code]); - continue; } } } @@ -323,8 +319,6 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) $directive .= $this->getWidgetPageVarName($params); - $directive .= sprintf(' type_name="%s"', $widget['name']); - $directive .= '}}'; if ($asIs) { diff --git a/app/code/Magento/Widget/Model/Widget/Config.php b/app/code/Magento/Widget/Model/Widget/Config.php index 4f81ef33f47f7..00b055b35a69d 100644 --- a/app/code/Magento/Widget/Model/Widget/Config.php +++ b/app/code/Magento/Widget/Model/Widget/Config.php @@ -120,6 +120,7 @@ public function getWidgetPlaceholderImageUrls() /** * Return url to error image + * * @return string */ public function getErrorImageUrl() @@ -129,6 +130,7 @@ public function getErrorImageUrl() /** * Return url to wysiwyg plugin + * * @return string */ public function getWysiwygJsPluginSrc() @@ -157,7 +159,7 @@ public function getWidgetWindowUrl($config) } } - if (count($skipped) > 0) { + if (!empty($skipped)) { $params['skip_widgets'] = $this->encodeWidgetsToQuery($skipped); } return $this->_backendUrl->getUrl('adminhtml/widget/index', $params); @@ -189,6 +191,8 @@ public function decodeWidgetsFromQuery($queryParam) } /** + * Get available widgets. + * * @param \Magento\Framework\DataObject $config Editor element config * @return array */ @@ -202,7 +206,7 @@ public function getAvailableWidgets($config) if (is_array($skipped) && in_array($widget['type'], $skipped)) { continue; } - $result[] = $widget['name']->getText(); + $result[$widget['type']] = $widget['name']->getText(); } } diff --git a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php index 5c546d7e2435c..850a3fbe83211 100644 --- a/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php +++ b/app/code/Magento/Widget/Test/Unit/Model/WidgetTest.php @@ -224,7 +224,6 @@ public function testGetWidgetDeclaration() $this->assertContains('title="my "widget""', $result); $this->assertContains('conditions_encoded="encoded-conditions-string"', $result); $this->assertContains('page_var_name="pasdf"', $result); - $this->assertContains('type_name=""}}', $result); } /** @@ -275,7 +274,6 @@ public function testGetWidgetDeclarationWithZeroValueParam() ); $this->assertContains('{{widget type="Magento\CatalogWidget\Block\Product\ProductsList"', $result); $this->assertContains('page_var_name="pasdf"', $result); - $this->assertContains('type_name=""}}', $result); $this->assertContains('products_count=""', $result); } } 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 e5915969c91b9..77fb53a2ab02a 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 @@ -34,12 +34,12 @@ .product { &-items { - font-size: 0; + .lib-inline-block-space-container(); &:extend(.abs-reset-list all); } &-item { - font-size: 1.4rem; + .lib-inline-block-space-item(); vertical-align: top; .products-grid & { diff --git a/app/etc/di.xml b/app/etc/di.xml index d0b45ea16c855..47e7e419e3b50 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -210,6 +210,7 @@ <preference for="Magento\Framework\MessageQueue\Bulk\ExchangeFactoryInterface" type="Magento\Framework\MessageQueue\Bulk\ExchangeFactory" /> <preference for="Magento\Framework\MessageQueue\QueueFactoryInterface" type="Magento\Framework\MessageQueue\QueueFactory" /> <preference for="Magento\Framework\Search\Request\IndexScopeResolverInterface" type="Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver"/> + <preference for="Magento\Framework\HTTP\ClientInterface" type="Magento\Framework\HTTP\Client\Curl" /> <type name="Magento\Framework\Model\ResourceModel\Db\TransactionManager" shared="false" /> <type name="Magento\Framework\Acl\Data\Cache"> <arguments> diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php index add0510c6b40c..e18a8c8e97c79 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php @@ -51,7 +51,7 @@ public function __construct( * @return array|string|int|float|bool * @throws \Exception */ - public function postQuery(string $query, array $variables = [], string $operationName = '', array $headers = []) + public function post(string $query, array $variables = [], string $operationName = '', array $headers = []) { $url = $this->getEndpointUrl(); $headers = array_merge($headers, ['Accept: application/json', 'Content-Type: application/json']); @@ -63,19 +63,57 @@ public function postQuery(string $query, array $variables = [], string $operatio $postData = $this->json->jsonEncode($requestArray); $responseBody = $this->curlClient->post($url, $postData, $headers); - $responseBodyArray = $this->json->jsonDecode($responseBody); + return $this->processResponse($responseBody); + } + + /** + * Perform HTTP GET request for query + * + * @param string $query + * @param array $variables + * @param string $operationName + * @param array $headers + * @return mixed + * @throws \Exception + */ + public function get(string $query, array $variables = [], string $operationName = '', array $headers = []) + { + $url = $this->getEndpointUrl(); + $requestArray = [ + 'query' => $query, + 'variables' => $variables ? $this->json->jsonEncode($variables) : null, + 'operationName' => $operationName ?? null + ]; + array_filter($requestArray); + + $responseBody = $this->curlClient->get($url, $requestArray, $headers); + return $this->processResponse($responseBody); + } + + /** + * Process response from GraphQl server + * + * @param string $response + * @return mixed + * @throws \Exception + */ + private function processResponse(string $response) + { + $responseArray = $this->json->jsonDecode($response); - if (!is_array($responseBodyArray)) { - throw new \Exception('Unknown GraphQL response body: ' . json_encode($responseBodyArray)); + if (!is_array($responseArray)) { + //phpcs:ignore Magento2.Exceptions.DirectThrow + throw new \Exception('Unknown GraphQL response body: ' . $response); } - $this->processErrors($responseBodyArray); + $this->processErrors($responseArray); - if (!isset($responseBodyArray['data'])) { - throw new \Exception('Unknown GraphQL response body: ' . json_encode($responseBodyArray)); - } else { - return $responseBodyArray['data']; + if (!isset($responseArray['data'])) { + //phpcs:ignore Magento2.Exceptions.DirectThrow + throw new \Exception('Unknown GraphQL response body: ' . $response); } + + return $responseArray['data']; } /** @@ -107,6 +145,7 @@ private function processErrors($responseBodyArray) $responseBodyArray ); } + //phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('GraphQL responded with an unknown error: ' . json_encode($responseBodyArray)); } } diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php index 790581c476da1..8abd97b4b744d 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php @@ -6,6 +6,7 @@ namespace Magento\TestFramework\TestCase; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\App\Request\Http; /** * Test case for Web API functional tests for Graphql. @@ -27,7 +28,7 @@ abstract class GraphQlAbstract extends WebapiAbstract private $appCache; /** - * Perform GraphQL call to the system under test. + * Perform GraphQL query call via GET to the system under test. * * @see \Magento\TestFramework\TestCase\GraphQl\Client::call() * @param string $query @@ -43,7 +44,32 @@ public function graphQlQuery( string $operationName = '', array $headers = [] ) { - return $this->getGraphQlClient()->postQuery( + return $this->getGraphQlClient()->get( + $query, + $variables, + $operationName, + $this->composeHeaders($headers) + ); + } + + /** + * Perform GraphQL mutations call via POST to the system under test. + * + * @see \Magento\TestFramework\TestCase\GraphQl\Client::call() + * @param string $query + * @param array $variables + * @param string $operationName + * @param array $headers + * @return array|int|string|float|bool GraphQL call results + * @throws \Exception + */ + public function graphQlMutation( + string $query, + array $variables = [], + string $operationName = '', + array $headers = [] + ) { + return $this->getGraphQlClient()->post( $query, $variables, $operationName, diff --git a/dev/tests/api-functional/testsuite/Magento/Analytics/Api/LinkProviderTest.php b/dev/tests/api-functional/testsuite/Magento/Analytics/Api/LinkProviderTest.php index 6fd7551676660..4bf1335f20667 100644 --- a/dev/tests/api-functional/testsuite/Magento/Analytics/Api/LinkProviderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Analytics/Api/LinkProviderTest.php @@ -95,6 +95,6 @@ public function testGetAll() */ private function isTestBaseUrlSecure() { - return strpos('https://', TESTS_BASE_URL) !== false; + return strpos(TESTS_BASE_URL, 'https://') !== false; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/AddProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/AddProductToCartTest.php index 17c2af8dc59d0..6616b40257f7d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/AddProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/AddProductToCartTest.php @@ -13,6 +13,9 @@ use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +/** + * Test for adding products to cart + */ class AddProductToCartTest extends GraphQlAbstract { /** @@ -54,7 +57,7 @@ public function testAddProductIfQuantityIsNotAvailable() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddSimpleProductQuery($maskedQuoteId, $sku, $qty); - $this->graphQlQuery($query); + $this->graphQlMutation($query); self::fail('Should be "The requested qty is not available" error message.'); } @@ -74,7 +77,7 @@ public function testAddMoreProductsThatAllowed() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddSimpleProductQuery($maskedQuoteId, $sku, $qty); - $this->graphQlQuery($query); + $this->graphQlMutation($query); self::fail('Should be "The most you may purchase is 5." error message.'); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php index 84c111bd25fd4..ab6a1d4b464dd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php @@ -14,6 +14,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Test change customer password + */ class ChangeCustomerPasswordTest extends GraphQlAbstract { /** @@ -50,7 +53,7 @@ public function testChangePassword() $query = $this->getChangePassQuery($oldCustomerPassword, $newCustomerPassword); $headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword); - $response = $this->graphQlQuery($query, [], '', $headerMap); + $response = $this->graphQlMutation($query, [], '', $headerMap); $this->assertEquals($customerEmail, $response['changeCustomerPassword']['email']); try { @@ -69,7 +72,7 @@ public function testChangePassword() public function testChangePasswordIfUserIsNotAuthorizedTest() { $query = $this->getChangePassQuery('currentpassword', 'newpassword'); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -87,7 +90,7 @@ public function testChangeWeakPassword() $this->expectException(\Exception::class); $this->expectExceptionMessageRegExp('/Minimum of different classes of characters in password is.*/'); - $this->graphQlQuery($query, [], '', $headerMap); + $this->graphQlMutation($query, [], '', $headerMap); } /** @@ -105,7 +108,7 @@ public function testChangePasswordIfPasswordIsInvalid() $query = $this->getChangePassQuery($incorrectPassword, $newCustomerPassword); $headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword); - $this->graphQlQuery($query, [], '', $headerMap); + $this->graphQlMutation($query, [], '', $headerMap); } private function getChangePassQuery($currentPassword, $newPassword) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 602d969924fbd..891c74ca3c1e2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -13,6 +13,9 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Integration\Api\CustomerTokenServiceInterface; +/** + * Create customer address tests + */ class CreateCustomerAddressTest extends GraphQlAbstract { /** @@ -117,7 +120,7 @@ public function testCreateCustomerAddress() $userName = 'customer@example.com'; $password = 'password'; - $response = $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); $this->assertArrayHasKey('createCustomerAddress', $response); $this->assertArrayHasKey('customer_id', $response['createCustomerAddress']); $this->assertEquals($customerId, $response['createCustomerAddress']['customer_id']); @@ -158,7 +161,7 @@ public function testCreateCustomerAddressIfUserIsNotAuthorized() } } MUTATION; - $this->graphQlQuery($mutation); + $this->graphQlMutation($mutation); } /** @@ -195,7 +198,73 @@ public function testCreateCustomerAddressWithMissingAttribute() $userName = 'customer@example.com'; $password = 'password'; - $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithRedundantStreetLine() + { + $newAddress = [ + 'region' => [ + 'region' => 'Arizona', + 'region_id' => 4, + 'region_code' => 'AZ' + ], + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2', 'Line 3'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<<MUTATION +mutation { + createCustomerAddress(input: { + region: { + region: "{$newAddress['region']['region']}" + region_id: {$newAddress['region']['region_id']} + region_code: "{$newAddress['region']['region_code']}" + } + country_id: {$newAddress['country_id']} + street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}","{$newAddress['street'][2]}"] + company: "{$newAddress['company']}" + telephone: "{$newAddress['telephone']}" + fax: "{$newAddress['fax']}" + postcode: "{$newAddress['postcode']}" + city: "{$newAddress['city']}" + firstname: "{$newAddress['firstname']}" + lastname: "{$newAddress['lastname']}" + middlename: "{$newAddress['middlename']}" + prefix: "{$newAddress['prefix']}" + suffix: "{$newAddress['suffix']}" + vat_id: "{$newAddress['vat_id']}" + default_shipping: true + default_billing: false + }) { + id + } +} +MUTATION; + + $userName = 'customer@example.com'; + $password = 'password'; + + self::expectExceptionMessage('"Street Address" cannot contain more than 2 lines.'); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index 388028c4ca750..fc51f57a83a76 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -66,7 +66,7 @@ public function testCreateCustomerAccountWithPassword() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertEquals($newFirstname, $response['createCustomer']['customer']['firstname']); $this->assertEquals($newLastname, $response['createCustomer']['customer']['lastname']); @@ -103,7 +103,7 @@ public function testCreateCustomerAccountWithoutPassword() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertEquals($newFirstname, $response['createCustomer']['customer']['firstname']); $this->assertEquals($newLastname, $response['createCustomer']['customer']['lastname']); @@ -134,7 +134,7 @@ public function testCreateCustomerIfInputDataIsEmpty() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -167,7 +167,7 @@ public function testCreateCustomerIfEmailMissed() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -202,7 +202,7 @@ public function testCreateCustomerIfEmailIsNotValid() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -238,7 +238,7 @@ public function testCreateCustomerIfPassedAttributeDosNotExistsInCustomerInput() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } public function tearDown() diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php index 1153b9662b41a..bdfd428a78c20 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php @@ -13,6 +13,9 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Integration\Api\CustomerTokenServiceInterface; +/** + * Delete customer address tests + */ class DeleteCustomerAddressTest extends GraphQlAbstract { /** @@ -55,7 +58,7 @@ public function testDeleteCustomerAddress() deleteCustomerAddress(id: {$addressId}) } MUTATION; - $response = $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); $this->assertArrayHasKey('deleteCustomerAddress', $response); $this->assertEquals(true, $response['deleteCustomerAddress']); } @@ -73,7 +76,7 @@ public function testDeleteCustomerAddressIfUserIsNotAuthorized() deleteCustomerAddress(id: {$addressId}) } MUTATION; - $this->graphQlQuery($mutation); + $this->graphQlMutation($mutation); } /** @@ -99,7 +102,7 @@ public function testDeleteDefaultShippingCustomerAddress() deleteCustomerAddress(id: {$addressId}) } MUTATION; - $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } /** @@ -125,7 +128,7 @@ public function testDeleteDefaultBillingCustomerAddress() deleteCustomerAddress(id: {$addressId}) } MUTATION; - $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } /** @@ -144,7 +147,7 @@ public function testDeleteNonExistCustomerAddress() deleteCustomerAddress(id: 9999) } MUTATION; - $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GenerateCustomerTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GenerateCustomerTokenTest.php index ae28e23a28bf1..88eaeaa8f9dd5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GenerateCustomerTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GenerateCustomerTokenTest.php @@ -38,7 +38,7 @@ public function testGenerateCustomerValidToken() } MUTATION; - $response = $this->graphQlQuery($mutation); + $response = $this->graphQlMutation($mutation); $this->assertArrayHasKey('generateCustomerToken', $response); $this->assertInternalType('array', $response['generateCustomerToken']); } @@ -66,6 +66,6 @@ public function testGenerateCustomerTokenWithInvalidCredentials() $this->expectException(\Exception::class); $this->expectExceptionMessage('GraphQL response contains errors: The account sign-in' . ' ' . 'was incorrect or your account is disabled temporarily. Please wait and try again later.'); - $this->graphQlQuery($mutation); + $this->graphQlMutation($mutation); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php index 9bdbf3059eeaf..fc0c02bae5508 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/RevokeCustomerTokenTest.php @@ -36,7 +36,7 @@ public function testRevokeCustomerTokenValidCredentials() $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($query, [], '', $headerMap); + $response = $this->graphQlMutation($query, [], '', $headerMap); $this->assertTrue($response['revokeCustomerToken']['result']); } @@ -53,6 +53,6 @@ public function testRevokeCustomerTokenForGuestCustomer() } } QUERY; - $this->graphQlQuery($query, [], ''); + $this->graphQlMutation($query, [], ''); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php index 191ea1ae6b877..2b54c97cd1e97 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php @@ -12,6 +12,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Tests for subscription status + */ class SubscriptionStatusTest extends GraphQlAbstract { /** @@ -88,7 +91,12 @@ public function testChangeSubscriptionStatusTest() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $response = $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders($currentEmail, $currentPassword) + ); $this->assertTrue($response['updateCustomer']['customer']['is_subscribed']); } @@ -111,7 +119,7 @@ public function testChangeSubscriptionStatuIfUserIsNotAuthorizedTest() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php index 6a9708b4f86a2..e7a7eda2897b2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php @@ -14,6 +14,9 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Integration\Api\CustomerTokenServiceInterface; +/** + * Update customer address tests + */ class UpdateCustomerAddressTest extends GraphQlAbstract { /** @@ -128,7 +131,7 @@ public function testUpdateCustomerAddress() } MUTATION; - $response = $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); $this->assertArrayHasKey('updateCustomerAddress', $response); $this->assertArrayHasKey('customer_id', $response['updateCustomerAddress']); $this->assertEquals($customerId, $response['updateCustomerAddress']['customer_id']); @@ -158,7 +161,7 @@ public function testUpdateCustomerAddressIfUserIsNotAuthorized() } } MUTATION; - $this->graphQlQuery($mutation); + $this->graphQlMutation($mutation); } /** @@ -187,7 +190,7 @@ public function testUpdateCustomerAddressWithMissingAttribute() } } MUTATION; - $this->graphQlQuery($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php index df45e1de771d9..08933f47191b9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php @@ -13,6 +13,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Tests for update customer + */ class UpdateCustomerTest extends GraphQlAbstract { /** @@ -87,7 +90,12 @@ public function testUpdateCustomer() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $response = $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders($currentEmail, $currentPassword) + ); $this->assertEquals($newPrefix, $response['updateCustomer']['customer']['prefix']); $this->assertEquals($newFirstname, $response['updateCustomer']['customer']['firstname']); @@ -123,7 +131,7 @@ public function testUpdateCustomerIfInputDataIsEmpty() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** @@ -147,7 +155,7 @@ public function testUpdateCustomerIfUserIsNotAuthorized() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -176,7 +184,7 @@ public function testUpdateCustomerIfAccountIsLocked() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** @@ -203,7 +211,7 @@ public function testUpdateEmailIfPasswordIsMissed() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** @@ -232,7 +240,7 @@ public function testUpdateEmailIfPasswordIsInvalid() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** @@ -260,7 +268,7 @@ public function testUpdateEmailIfEmailAlreadyExists() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php index 352947714360a..e784061d5562f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php @@ -393,7 +393,8 @@ public function testQueryComplexityIsLimited() QUERY; self::expectExceptionMessageRegExp('/Max query complexity should be 300 but got 302/'); - $this->graphQlQuery($query); + //Use POST request because request uri is too large for some servers + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php index b3f16c8734203..f585469177bcd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php @@ -13,6 +13,9 @@ use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +/** + * Add configurable product to cart tests + */ class AddConfigurableProductToCartTest extends GraphQlAbstract { /** @@ -54,7 +57,7 @@ public function testAddConfigurableProductToCart() $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $cartItems = $response['addConfigurableProductsToCart']['cart']['items']; self::assertEquals($qty, $cartItems[0]['qty']); self::assertEquals($variantSku, $cartItems[0]['product']['sku']); @@ -74,7 +77,7 @@ public function testAddProductIfQuantityIsNotAvailable() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -90,7 +93,7 @@ public function testAddOutOfStockProduct() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php index d9ab8db62a195..65ed38ae90f5b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php @@ -13,6 +13,9 @@ use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +/** + * Add simple product to cart tests + */ class AddSimpleProductToCartTest extends GraphQlAbstract { /** @@ -52,7 +55,7 @@ public function testAddSimpleProductToCart() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddSimpleProductQuery($maskedQuoteId, $sku, $qty); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['addSimpleProductsToCart']); self::assertEquals($qty, $response['addSimpleProductsToCart']['cart']['items'][0]['qty']); @@ -72,7 +75,7 @@ public function testAddSimpleProductToCartWithNegativeQty() $maskedQuoteId = $this->getMaskedQuoteId(); $query = $this->getAddSimpleProductQuery($maskedQuoteId, $sku, $qty); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -158,6 +161,6 @@ public function testAddProductWithWrongCartHash() } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php deleted file mode 100644 index 316fe157993b9..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\Quote; - -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test for adding/removing shopping cart coupon codes - */ -class CouponTest extends GraphQlAbstract -{ - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var Quote - */ - private $quote; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php - */ - public function testApplyCouponToGuestCartWithItems() - { - $couponCode = '2?ds5!2d'; - - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareAddCouponRequestQuery($maskedQuoteId, $couponCode); - $response = $this->graphQlQuery($query); - - self::assertArrayHasKey('applyCouponToCart', $response); - self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php - */ - public function testApplyCouponTwice() - { - $couponCode = '2?ds5!2d'; - - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareAddCouponRequestQuery($maskedQuoteId, $couponCode); - $response = $this->graphQlQuery($query); - - self::assertArrayHasKey("applyCouponToCart", $response); - self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); - - self::expectExceptionMessage('A coupon is already applied to the cart. Please remove it to apply another'); - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php - * @expectedException \Exception - * @expectedExceptionMessage Cart does not contain products. - */ - public function testApplyCouponToCartWithNoItems() - { - $couponCode = '2?ds5!2d'; - - $this->quoteResource->load($this->quote, 'test_order_1', 'reserved_order_id'); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareAddCouponRequestQuery($maskedQuoteId, $couponCode); - - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testGuestCustomerAttemptToChangeCustomerCart() - { - $couponCode = '2?ds5!2d'; - - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); - $query = $this->prepareAddCouponRequestQuery($maskedQuoteId, $couponCode); - - self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); - $this->graphQlQuery($query); - } - - /** - * @param string $maskedQuoteId - * @param string $couponCode - * @return string - */ - private function prepareAddCouponRequestQuery(string $maskedQuoteId, string $couponCode): string - { - return <<<QUERY -mutation { - applyCouponToCart(input: {cart_id: "$maskedQuoteId", coupon_code: "$couponCode"}) { - cart { - applied_coupon { - code - } - } - } -} -QUERY; - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php new file mode 100644 index 0000000000000..5a2221a184294 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php @@ -0,0 +1,283 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Framework\Exception\AuthenticationException; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Apply Coupon to Cart functionality for customer + */ +class ApplyCouponToCartTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testApplyCouponToCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('applyCouponToCart', $response); + self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage A coupon is already applied to the cart. Please remove it to apply another + */ + public function testApplyCouponTwice() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey("applyCouponToCart", $response); + self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @expectedException \Exception + * @expectedExceptionMessage Cart does not contain products. + */ + public function testApplyCouponToCartWithoutItems() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * _security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @expectedException \Exception + */ + public function testApplyCouponToGuestCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * _security + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/two_customers.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + */ + public function testApplyCouponToAnotherCustomerCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer_two@example.com')); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyNonExistentCouponToCart() + { + $couponCode = 'non_existent_coupon_code'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @expectedException \Exception + */ + public function testApplyCouponToNonExistentCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getQuery($maskedQuoteId, $couponCode); + + self::expectExceptionMessage('Could not find a cart with ID "' . $maskedQuoteId . '"'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_coupon_expired.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyExpiredCoupon() + { + $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/574'); + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * Products in cart don't fit to the coupon + * + * @magentoApiDataFixture Magento/Checkout/_files/discount_10percent_generalusers.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyCouponWhichIsNotApplicable() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @param string $input + * @param string $message + * @dataProvider dataProviderUpdateWithMissedRequiredParameters + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @expectedException \Exception + */ + public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) + { + $query = <<<QUERY +mutation { + applyCouponToCart(input: {{$input}}) { + cart { + applied_coupon { + code + } + } + } +} +QUERY; + + $this->expectExceptionMessage($message); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @return array + */ + public function dataProviderUpdateWithMissedRequiredParameters(): array + { + return [ + 'missed_cart_id' => [ + 'coupon_code: "test"', + 'Required parameter "cart_id" is missing' + ], + 'missed_coupon_code' => [ + 'cart_id: "test"', + 'Required parameter "coupon_code" is missing' + ], + ]; + } + + /** + * Retrieve customer authorization headers + * + * @param string $username + * @param string $password + * @return array + * @throws AuthenticationException + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } + + /** + * @param string $maskedQuoteId + * @param string $couponCode + * @return string + */ + private function getQuery(string $maskedQuoteId, string $couponCode): string + { + return <<<QUERY +mutation { + applyCouponToCart(input: {cart_id: "$maskedQuoteId", coupon_code: "$couponCode"}) { + cart { + applied_coupon { + code + } + } + } +} +QUERY; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php index 0cb8a38b0cb5e..fc66e4647e576 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php @@ -8,6 +8,10 @@ namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Quote\Api\GuestCartRepositoryInterface; @@ -27,11 +31,40 @@ class CreateEmptyCartTest extends GraphQlAbstract */ private $customerTokenService; + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var string + */ + private $maskedQuoteId; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->maskedQuoteIdToQuoteId = $objectManager->get(MaskedQuoteIdToQuoteIdInterface::class); + $this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class); } /** @@ -39,23 +72,83 @@ protected function setUp() */ public function testCreateEmptyCart() { - $query = <<<QUERY + $query = $this->getQuery(); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMapWithCustomerToken()); + + self::assertArrayHasKey('createEmptyCart', $response); + self::assertNotEmpty($response['createEmptyCart']); + + $guestCart = $this->guestCartRepository->get($response['createEmptyCart']); + $this->maskedQuoteId = $response['createEmptyCart']; + + self::assertNotNull($guestCart->getId()); + self::assertEquals(1, $guestCart->getCustomer()->getId()); + self::assertEquals('default', $guestCart->getStore()->getCode()); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/second_store.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testCreateEmptyCartWithNotDefaultStore() + { + $query = $this->getQuery(); + + $headerMap = $this->getHeaderMapWithCustomerToken(); + $headerMap['Store'] = 'fixture_second_store'; + $response = $this->graphQlMutation($query, [], '', $headerMap); + + self::assertArrayHasKey('createEmptyCart', $response); + self::assertNotEmpty($response['createEmptyCart']); + + /* guestCartRepository is used for registered customer to get the cart hash */ + $guestCart = $this->guestCartRepository->get($response['createEmptyCart']); + $this->maskedQuoteId = $response['createEmptyCart']; + + self::assertNotNull($guestCart->getId()); + self::assertEquals(1, $guestCart->getCustomer()->getId()); + self::assertEquals('fixture_second_store', $guestCart->getStore()->getCode()); + } + + /** + * @return string + */ + private function getQuery(): string + { + return <<<QUERY mutation { createEmptyCart } QUERY; + } - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + /** + * @param string $username + * @param string $password + * @return array + */ + private function getHeaderMapWithCustomerToken( + string $username = 'customer@example.com', + string $password = 'password' + ): array { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } - $response = $this->graphQlQuery($query, [], '', $headerMap); - - self::assertArrayHasKey('createEmptyCart', $response); + public function tearDown() + { + if (null !== $this->maskedQuoteId) { + $quoteId = $this->maskedQuoteIdToQuoteId->execute($this->maskedQuoteId); - $maskedCartId = $response['createEmptyCart']; - $guestCart = $this->guestCartRepository->get($maskedCartId); + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $quoteId); + $this->quoteResource->delete($quote); - self::assertNotNull($guestCart->getId()); - self::assertEquals(1, $guestCart->getCustomer()->getId()); + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->setQuoteId($quoteId) + ->delete(); + } + parent::tearDown(); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index 2cac1f8c4fbf0..19b72b9e3ca4c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -124,6 +124,58 @@ public function testGetInactiveCart() $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote_customer_not_default_store.php + */ + public function testGetCartWithNotDefaultStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store'); + $query = $this->getQuery($maskedQuoteId); + + $headerMap = $this->getHeaderMap(); + $headerMap['Store'] = 'fixture_second_store'; + + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('items', $response['cart']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @magentoApiDataFixture Magento/Store/_files/second_store.php + * + * @expectedException \Exception + * @expectedExceptionMessage Wrong store code specified for cart + */ + public function testGetCartWithWrongStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $query = $this->getQuery($maskedQuoteId); + + $headerMap = $this->getHeaderMap(); + $headerMap['Store'] = 'fixture_second_store'; + + $this->graphQlQuery($query, [], '', $headerMap); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote_customer_not_default_store.php + * + * @expectedException \Exception + * @expectedExceptionMessage Store code not_existing_store does not exist + */ + public function testGetCartWithNotExistingStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store'); + $query = $this->getQuery($maskedQuoteId); + + $headerMap = $this->getHeaderMap(); + $headerMap['Store'] = 'not_existing_store'; + + $this->graphQlQuery($query, [], '', $headerMap); + } + /** * @param string $maskedQuoteId * @return string diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php index 4220f8932caa1..b7d3b546ba194 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php @@ -77,7 +77,7 @@ public function testPlaceOrder() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); $query = $this->getQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('placeOrder', $response); self::assertArrayHasKey('order_id', $response['placeOrder']['order']); @@ -98,7 +98,7 @@ public function testPlaceOrderWithNoItemsInCart() 'Unable to place order: A server error stopped your order from being placed. ' . 'Please try to place your order again' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -116,7 +116,7 @@ public function testPlaceOrderWithNoShippingAddress() self::expectExceptionMessage( 'Unable to place order: Some addresses can\'t be used due to the configurations for specific countries' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -135,7 +135,7 @@ public function testPlaceOrderWithNoShippingMethod() self::expectExceptionMessage( 'Unable to place order: The shipping method is missing. Select the shipping method and try again' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -156,7 +156,7 @@ public function testPlaceOrderWithNoBillingAddress() self::expectExceptionMessageRegExp( '/Unable to place order: Please check the billing address information*/' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -176,7 +176,7 @@ public function testPlaceOrderWithNoPaymentMethod() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessage('Unable to place order: Enter a valid payment method and try again'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -197,7 +197,7 @@ public function testPlaceOrderWithOutOfStockProduct() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessage('Unable to place order: Some of the products are out of stock'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -220,7 +220,7 @@ public function testPlaceOrderOfGuestCart() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessageRegExp('/The current user cannot perform operations on cart*/'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -243,7 +243,7 @@ public function testPlaceOrderOfAnotherCustomerCart() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessageRegExp('/The current user cannot perform operations on cart*/'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer3@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer3@search.example.com')); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php index feba8c5c64259..ce1c85417b165 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php @@ -50,7 +50,7 @@ public function testRemoveCouponFromCart() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('removeCouponFromCart', $response); self::assertNull($response['removeCouponFromCart']['cart']['applied_coupon']['code']); @@ -66,7 +66,7 @@ public function testRemoveCouponFromNonExistentCart() $maskedQuoteId = 'non_existent_masked_id'; $query = $this->getQuery($maskedQuoteId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -80,7 +80,7 @@ public function testRemoveCouponFromEmptyCart() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -94,7 +94,7 @@ public function testRemoveCouponFromCartIfCouponWasNotSet() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('removeCouponFromCart', $response); self::assertNull($response['removeCouponFromCart']['cart']['applied_coupon']['code']); @@ -115,7 +115,7 @@ public function testRemoveCouponFromGuestCart() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -132,7 +132,7 @@ public function testRemoveCouponFromAnotherCustomerCart() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer3@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer3@search.example.com')); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php index e80a2127ad420..39803f8d58447 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php @@ -7,11 +7,9 @@ namespace Magento\GraphQl\Quote\Customer; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\GraphQl\Quote\GetQuoteItemIdByReservedQuoteIdAndSku; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -26,47 +24,38 @@ class RemoveItemFromCartTest extends GraphQlAbstract private $customerTokenService; /** - * @var QuoteResource + * @var GetMaskedQuoteIdByReservedOrderId */ - private $quoteResource; + private $getMaskedQuoteIdByReservedOrderId; /** - * @var QuoteFactory + * @var GetQuoteItemIdByReservedQuoteIdAndSku */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @var ProductRepositoryInterface - */ - private $productRepository; + private $getQuoteItemIdByReservedQuoteIdAndSku; protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getQuoteItemIdByReservedQuoteIdAndSku = $objectManager->get( + GetQuoteItemIdByReservedQuoteIdAndSku::class + ); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testRemoveItemFromCart() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id'); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); - $itemId = (int)$quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $itemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); - $query = $this->prepareMutationQuery($maskedQuoteId, $itemId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $query = $this->getQuery($maskedQuoteId, $itemId); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); $this->assertArrayHasKey('removeItemFromCart', $response); $this->assertArrayHasKey('cart', $response['removeItemFromCart']); @@ -80,106 +69,25 @@ public function testRemoveItemFromCart() */ public function testRemoveItemFromNonExistentCart() { - $query = $this->prepareMutationQuery('non_existent_masked_id', 1); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $query = $this->getQuery('non_existent_masked_id', 1); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testRemoveNonExistentItem() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id'); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $notExistentItemId = 999; $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); - $query = $this->prepareMutationQuery($maskedQuoteId, $notExistentItemId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php - */ - public function testRemoveItemIfItemIsNotBelongToCart() - { - $firstQuote = $this->quoteFactory->create(); - $this->quoteResource->load($firstQuote, 'test_order_1', 'reserved_order_id'); - $firstQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); - - $secondQuote = $this->quoteFactory->create(); - $this->quoteResource->load( - $secondQuote, - 'test_order_with_virtual_product_without_address', - 'reserved_order_id' - ); - $secondQuote->setCustomerId(1); - $this->quoteResource->save($secondQuote); - $secondQuoteItemId = (int)$secondQuote - ->getItemByProduct($this->productRepository->get('virtual-product')) - ->getId(); - - $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); - - $query = $this->prepareMutationQuery($firstQuoteMaskedId, $secondQuoteItemId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php - */ - public function testRemoveItemFromGuestCart() - { - $guestQuote = $this->quoteFactory->create(); - $this->quoteResource->load( - $guestQuote, - 'test_order_with_virtual_product_without_address', - 'reserved_order_id' - ); - $guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId()); - $guestQuoteItemId = (int)$guestQuote - ->getItemByProduct($this->productRepository->get('virtual-product')) - ->getId(); - - $this->expectExceptionMessage( - "The current user cannot perform operations on cart \"$guestQuoteMaskedId\"" - ); - - $query = $this->prepareMutationQuery($guestQuoteMaskedId, $guestQuoteItemId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/three_customers.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php - */ - public function testRemoveItemFromAnotherCustomerCart() - { - $anotherCustomerQuote = $this->quoteFactory->create(); - $this->quoteResource->load( - $anotherCustomerQuote, - 'test_order_with_virtual_product_without_address', - 'reserved_order_id' - ); - $anotherCustomerQuote->setCustomerId(2); - $this->quoteResource->save($anotherCustomerQuote); - - $anotherCustomerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$anotherCustomerQuote->getId()); - $anotherCustomerQuoteItemId = (int)$anotherCustomerQuote - ->getItemByProduct($this->productRepository->get('virtual-product')) - ->getId(); - - $this->expectExceptionMessage( - "The current user cannot perform operations on cart \"$anotherCustomerQuoteMaskedId\"" - ); - - $query = $this->prepareMutationQuery($anotherCustomerQuoteMaskedId, $anotherCustomerQuoteItemId); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $query = $this->getQuery($maskedQuoteId, $notExistentItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -206,7 +114,7 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -226,12 +134,77 @@ public function dataProviderUpdateWithMissedRequiredParameters(): array ]; } + /** + * _security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + */ + public function testRemoveItemIfItemIsNotBelongToCart() + { + $firstQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $secondQuoteItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute( + 'test_order_with_virtual_product', + 'virtual-product' + ); + + $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + + $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * _security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemFromGuestCart() + { + $guestQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $guestQuoteItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$guestQuoteMaskedId\"" + ); + + $query = $this->getQuery($guestQuoteMaskedId, $guestQuoteItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * _security + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemFromAnotherCustomerCart() + { + $anotherCustomerQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $anotherCustomerQuoteItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute( + 'test_quote', + 'simple_product' + ); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$anotherCustomerQuoteMaskedId\"" + ); + + $query = $this->getQuery($anotherCustomerQuoteMaskedId, $anotherCustomerQuoteItemId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + } + /** * @param string $maskedQuoteId * @param int $itemId * @return string */ - private function prepareMutationQuery(string $maskedQuoteId, int $itemId): string + private function getQuery(string $maskedQuoteId, int $itemId): string { return <<<QUERY mutation { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index af9e0210aa9d9..553408880baa0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -105,7 +105,7 @@ public function testSetNewBillingAddress() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -179,7 +179,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -230,7 +230,7 @@ public function testSetBillingAddressFromAddressBook() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -270,7 +270,7 @@ public function testSetNotExistedBillingAddressFromAddressBook() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -318,7 +318,7 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() self::expectExceptionMessage( 'The billing address cannot contain "customer_address_id" and "address" at the same time.' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -355,7 +355,7 @@ public function testSetBillingAddressToGuestCart() "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -390,7 +390,7 @@ public function testSetBillingAddressToAnotherCustomerCart() "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer@search.example.com')); } /** @@ -424,7 +424,7 @@ public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -454,7 +454,7 @@ public function testSetBillingAddressOnNonExistentCart() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -490,7 +490,50 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewBillingAddressWithRedundantStreetLine() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2", "test street 3"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + firstname + } + } + } +} +QUERY; + self::expectExceptionMessage('"Street Address" cannot contain more than 2 lines.'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflineShippingMethodsOnCartTest.php index b7b7823263106..20462220ff6f7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetOfflineShippingMethodsOnCartTest.php @@ -72,7 +72,7 @@ public function testSetOfflineShippingMethod(string $carrierCode, string $method $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index 73feefe2b094b..0f6f371177f92 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -52,7 +52,7 @@ public function testSetPaymentOnCartWithSimpleProduct() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); @@ -75,7 +75,7 @@ public function testSetPaymentOnCartWithSimpleProductAndWithoutAddress() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -90,7 +90,7 @@ public function testSetPaymentOnCartWithVirtualProduct() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); @@ -114,7 +114,7 @@ public function testSetNonExistentPaymentMethod() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -129,7 +129,7 @@ public function testSetPaymentOnNonExistentCart() $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -149,7 +149,7 @@ public function testSetPaymentMethodToGuestCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -169,7 +169,7 @@ public function testSetPaymentMethodToAnotherCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -201,7 +201,7 @@ public function testSetPaymentMethodWithoutRequiredParameters(string $input, str } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -240,7 +240,7 @@ public function testReSetPayment() $methodCode = Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE; $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index fe8c90d651879..6b097e028ffe5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -107,7 +107,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); $cartResponse = $response['setShippingAddressesOnCart']['cart']; @@ -160,7 +160,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -200,7 +200,7 @@ public function testSetShippingAddressFromAddressBook() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); $cartResponse = $response['setShippingAddressesOnCart']['cart']; @@ -242,7 +242,7 @@ public function testSetNonExistentShippingAddressFromAddressBook() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -291,7 +291,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() self::expectExceptionMessage( 'The shipping address cannot contain "customer_address_id" and "address" at the same time.' ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -328,7 +328,7 @@ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -366,7 +366,7 @@ public function testSetShippingAddressToAnotherCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -405,7 +405,7 @@ public function testSetNewShippingAddressWithMissedRequiredParameters(string $in } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -483,7 +483,53 @@ public function testSetMultipleNewShippingAddresses() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewShippingAddressOnCartWithRedundantStreetLine() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2", "test street 3"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + firstname + } + } + } +} +QUERY; + + self::expectExceptionMessage('"Street Address" cannot contain more than 2 lines.'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index 9219b5a67022c..ecad31a8bd123 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -66,7 +66,7 @@ public function testSetShippingMethodOnCartWithSimpleProduct() $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); @@ -105,7 +105,7 @@ public function testReSetShippingMethod() $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); @@ -156,7 +156,7 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -296,7 +296,7 @@ public function testSetMultipleShippingMethods() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -325,7 +325,7 @@ public function testSetShippingMethodToGuestCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -354,7 +354,7 @@ public function testSetShippingMethodToAnotherCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -382,7 +382,7 @@ public function testSetShippingMethodIfCustomerIsNotOwnerOfAddress() $this->expectExceptionMessage( "Cart does not contain address with ID \"{$anotherQuoteAddressId}\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php index 74e7aa8b5d0a4..35e2d62214fb2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php @@ -67,7 +67,7 @@ public function testUpdateCartItemQty() $qty = 2; $query = $this->getQuery($maskedQuoteId, $itemId, $qty); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); $this->assertArrayHasKey('updateCartItems', $response); $this->assertArrayHasKey('cart', $response['updateCartItems']); @@ -91,7 +91,7 @@ public function testRemoveCartItemIfQuantityIsZero() $qty = 0; $query = $this->getQuery($maskedQuoteId, $itemId, $qty); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); $this->assertArrayHasKey('updateCartItems', $response); $this->assertArrayHasKey('cart', $response['updateCartItems']); @@ -108,7 +108,7 @@ public function testRemoveCartItemIfQuantityIsZero() public function testUpdateItemInNonExistentCart() { $query = $this->getQuery('non_existent_masked_id', 1, 2); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -124,7 +124,7 @@ public function testUpdateNonExistentItem() $this->expectExceptionMessage("Could not find cart item with id: {$notExistentItemId}."); $query = $this->getQuery($maskedQuoteId, $notExistentItemId, 2); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -152,7 +152,7 @@ public function testUpdateItemIfItemIsNotBelongToCart() $this->expectExceptionMessage("Could not find cart item with id: {$secondQuoteItemId}."); $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId, 2); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -177,7 +177,7 @@ public function testUpdateItemInGuestCart() ); $query = $this->getQuery($guestQuoteMaskedId, $guestQuoteItemId, 2); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -206,7 +206,7 @@ public function testUpdateItemInAnotherCustomerCart() ); $query = $this->getQuery($anotherCustomerQuoteMaskedId, $anotherCustomerQuoteItemId, 2); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -235,7 +235,7 @@ public function testUpdateWithMissedCartItemId() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -266,7 +266,7 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetQuoteItemIdByReservedQuoteIdAndSku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetQuoteItemIdByReservedQuoteIdAndSku.php new file mode 100644 index 0000000000000..6f027babc0e27 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetQuoteItemIdByReservedQuoteIdAndSku.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\QuoteFactory; + +/** + * Get quote item id by reserved order id and product sku + */ +class GetQuoteItemIdByReservedQuoteIdAndSku +{ + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @param QuoteFactory $quoteFactory + * @param QuoteResource $quoteResource + * @param ProductRepositoryInterface $productRepository + */ + public function __construct( + QuoteFactory $quoteFactory, + QuoteResource $quoteResource, + ProductRepositoryInterface $productRepository + ) { + $this->quoteFactory = $quoteFactory; + $this->quoteResource = $quoteResource; + $this->productRepository = $productRepository; + } + + /** + * Get quote item id by reserved order id and product sku + * + * @param string $reservedOrderId + * @param string $sku + * @return int + * @throws NoSuchEntityException + */ + public function execute(string $reservedOrderId, string $sku): int + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); + $product = $this->productRepository->get($sku); + + return (int)$quote->getItemByProduct($product)->getId(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php new file mode 100644 index 0000000000000..affe36ea8617d --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php @@ -0,0 +1,232 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test Apply Coupon to Cart functionality for guest + */ +class ApplyCouponToCartTest extends GraphQlAbstract +{ + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + */ + public function testApplyCouponToCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('applyCouponToCart', $response); + self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @expectedException \Exception + * @expectedExceptionMessage A coupon is already applied to the cart. Please remove it to apply another + */ + public function testApplyCouponTwice() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey("applyCouponToCart", $response); + self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); + + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @expectedException \Exception + * @expectedExceptionMessage Cart does not contain products. + */ + public function testApplyCouponToCartWithoutItems() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query); + } + + /** + * _security + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @expectedException \Exception + */ + public function testApplyCouponToCustomerCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyNonExistentCouponToCart() + { + $couponCode = 'non_existent_coupon_code'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @expectedException \Exception + */ + public function testApplyCouponToNonExistentCart() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getQuery($maskedQuoteId, $couponCode); + + self::expectExceptionMessage('Could not find a cart with ID "' . $maskedQuoteId . '"'); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_coupon_expired.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyExpiredCoupon() + { + $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/574'); + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query); + } + + /** + * Products in cart don't fit to the coupon + * + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/SalesRule/_files/coupon_code_with_wildcard.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The coupon code isn't valid. Verify the code and try again. + */ + public function testApplyCouponWhichIsNotApplicable() + { + $couponCode = '2?ds5!2d'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId, $couponCode); + + $this->graphQlMutation($query); + } + + /** + * @param string $input + * @param string $message + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @dataProvider dataProviderUpdateWithMissedRequiredParameters + * @expectedException \Exception + */ + public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) + { + $query = <<<QUERY +mutation { + applyCouponToCart(input: {{$input}}) { + cart { + applied_coupon { + code + } + } + } +} +QUERY; + + $this->expectExceptionMessage($message); + $this->graphQlMutation($query); + } + + /** + * @return array + */ + public function dataProviderUpdateWithMissedRequiredParameters(): array + { + return [ + 'missed_cart_id' => [ + 'coupon_code: "test"', + 'Required parameter "cart_id" is missing' + ], + 'missed_coupon_code' => [ + 'cart_id: "test"', + 'Required parameter "coupon_code" is missing' + ], + ]; + } + + /** + * @param string $maskedQuoteId + * @param string $couponCode + * @return string + */ + private function getQuery(string $maskedQuoteId, string $couponCode): string + { + return <<<QUERY +mutation { + applyCouponToCart(input: {cart_id: "$maskedQuoteId", coupon_code: "$couponCode"}) { + cart { + applied_coupon { + code + } + } + } +} +QUERY; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php index 4fd398439913e..adb2879e186b1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php @@ -7,6 +7,10 @@ namespace Magento\GraphQl\Quote\Guest; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Quote\Api\GuestCartRepositoryInterface; @@ -21,27 +25,102 @@ class CreateEmptyCartTest extends GraphQlAbstract */ private $guestCartRepository; + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var string + */ + private $maskedQuoteId; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); $this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->maskedQuoteIdToQuoteId = $objectManager->get(MaskedQuoteIdToQuoteIdInterface::class); + $this->quoteIdMaskFactory = $objectManager->get(QuoteIdMaskFactory::class); } public function testCreateEmptyCart() { - $query = <<<QUERY + $query = $this->getQuery(); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('createEmptyCart', $response); + self::assertNotEmpty($response['createEmptyCart']); + + $guestCart = $this->guestCartRepository->get($response['createEmptyCart']); + $this->maskedQuoteId = $response['createEmptyCart']; + + self::assertNotNull($guestCart->getId()); + self::assertNull($guestCart->getCustomer()->getId()); + self::assertEquals('default', $guestCart->getStore()->getCode()); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/second_store.php + */ + public function testCreateEmptyCartWithNotDefaultStore() + { + $query = $this->getQuery(); + $headerMap = ['Store' => 'fixture_second_store']; + $response = $this->graphQlMutation($query, [], '', $headerMap); + + self::assertArrayHasKey('createEmptyCart', $response); + self::assertNotEmpty($response['createEmptyCart']); + + $guestCart = $this->guestCartRepository->get($response['createEmptyCart']); + $this->maskedQuoteId = $response['createEmptyCart']; + + self::assertNotNull($guestCart->getId()); + self::assertNull($guestCart->getCustomer()->getId()); + self::assertSame('fixture_second_store', $guestCart->getStore()->getCode()); + } + + /** + * @return string + */ + private function getQuery(): string + { + return <<<QUERY mutation { createEmptyCart } QUERY; - $response = $this->graphQlQuery($query); + } - self::assertArrayHasKey('createEmptyCart', $response); + public function tearDown() + { + if (null !== $this->maskedQuoteId) { + $quoteId = $this->maskedQuoteIdToQuoteId->execute($this->maskedQuoteId); - $maskedCartId = $response['createEmptyCart']; - $guestCart = $this->guestCartRepository->get($maskedCartId); + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $quoteId); + $this->quoteResource->delete($quote); - self::assertNotNull($guestCart->getId()); - self::assertNull($guestCart->getCustomer()->getId()); + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->setQuoteId($quoteId) + ->delete(); + } + parent::tearDown(); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php index 654b0b963af15..832e15058a4ee 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -97,6 +97,54 @@ public function testGetInactiveCart() $this->graphQlQuery($query); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote_guest_not_default_store.php + */ + public function testGetCartWithNotDefaultStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store_guest'); + $query = $this->getQuery($maskedQuoteId); + + $headerMap = ['Store' => 'fixture_second_store']; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('items', $response['cart']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @magentoApiDataFixture Magento/Store/_files/second_store.php + * + * @expectedException \Exception + * @expectedExceptionMessage Wrong store code specified for cart + */ + public function testGetCartWithWrongStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $query = $this->getQuery($maskedQuoteId); + + $headerMap = ['Store' => 'fixture_second_store']; + $this->graphQlQuery($query, [], '', $headerMap); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote_guest_not_default_store.php + * + * @expectedException \Exception + * @expectedExceptionMessage Store code not_existing_store does not exist + */ + public function testGetCartWithNotExistingStore() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store_guest'); + + $headerMap['Store'] = 'not_existing_store'; + $query = $this->getQuery($maskedQuoteId); + + $this->graphQlQuery($query, [], '', $headerMap); + } + /** * @param string $maskedQuoteId * @return string diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php index edb5f9cbf267c..5adb6ce65db6f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php @@ -42,7 +42,7 @@ public function testRemoveCouponFromCart() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('removeCouponFromCart', $response); self::assertNull($response['removeCouponFromCart']['cart']['applied_coupon']['code']); @@ -57,7 +57,7 @@ public function testRemoveCouponFromNonExistentCart() $maskedQuoteId = 'non_existent_masked_id'; $query = $this->getQuery($maskedQuoteId); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -70,7 +70,7 @@ public function testRemoveCouponFromEmptyCart() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -83,7 +83,7 @@ public function testRemoveCouponFromCartIfCouponWasNotSet() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('removeCouponFromCart', $response); self::assertNull($response['removeCouponFromCart']['cart']['applied_coupon']['code']); @@ -104,7 +104,7 @@ public function testRemoveCouponFromCustomerCart() $query = $this->getQuery($maskedQuoteId); self::expectExceptionMessage('The current user cannot perform operations on cart "' . $maskedQuoteId . '"'); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php index a306b29e51197..27f3f6367f662 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php @@ -7,10 +7,8 @@ namespace Magento\GraphQl\Quote\Guest; -use Magento\Quote\Model\QuoteFactory; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\GraphQl\Quote\GetQuoteItemIdByReservedQuoteIdAndSku; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -20,46 +18,36 @@ class RemoveItemFromCartTest extends GraphQlAbstract { /** - * @var QuoteResource + * @var GetMaskedQuoteIdByReservedOrderId */ - private $quoteResource; + private $getMaskedQuoteIdByReservedOrderId; /** - * @var QuoteFactory + * @var GetQuoteItemIdByReservedQuoteIdAndSku */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @var ProductRepositoryInterface - */ - private $productRepository; + private $getQuoteItemIdByReservedQuoteIdAndSku; protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); - $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->getQuoteItemIdByReservedQuoteIdAndSku = $objectManager->get( + GetQuoteItemIdByReservedQuoteIdAndSku::class + ); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testRemoveItemFromCart() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); - $itemId = (int)$quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $itemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); - $query = $this->prepareMutationQuery($maskedQuoteId, $itemId); - $response = $this->graphQlQuery($query); + $query = $this->getQuery($maskedQuoteId, $itemId); + $response = $this->graphQlMutation($query); $this->assertArrayHasKey('removeItemFromCart', $response); $this->assertArrayHasKey('cart', $response['removeItemFromCart']); @@ -72,70 +60,24 @@ public function testRemoveItemFromCart() */ public function testRemoveItemFromNonExistentCart() { - $query = $this->prepareMutationQuery('non_existent_masked_id', 1); - $this->graphQlQuery($query); + $query = $this->getQuery('non_existent_masked_id', 1); + $this->graphQlMutation($query); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php */ public function testRemoveNonExistentItem() { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $notExistentItemId = 999; $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); - $query = $this->prepareMutationQuery($maskedQuoteId, $notExistentItemId); - $this->graphQlQuery($query); - } - - /** - * Test mutation is only able to remove quote item belonging to the requested cart - * - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php - */ - public function testRemoveItemIfItemIsNotBelongToCart() - { - $firstQuote = $this->quoteFactory->create(); - $this->quoteResource->load($firstQuote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); - $firstQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); - - $secondQuote = $this->quoteFactory->create(); - $this->quoteResource->load( - $secondQuote, - 'test_order_with_virtual_product_without_address', - 'reserved_order_id' - ); - $secondQuoteItemId = (int)$secondQuote - ->getItemByProduct($this->productRepository->get('virtual-product')) - ->getId(); - - $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); - - $query = $this->prepareMutationQuery($firstQuoteMaskedId, $secondQuoteItemId); - $this->graphQlQuery($query); - } - - /** - * Test mutation is only able to remove quote item belonging to the requested cart - * - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testRemoveItemFromCustomerCart() - { - $customerQuote = $this->quoteFactory->create(); - $this->quoteResource->load($customerQuote, 'test_order_1', 'reserved_order_id'); - $customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId()); - $customerQuoteItemId = (int)$customerQuote->getItemByProduct($this->productRepository->get('simple'))->getId(); - - $this->expectExceptionMessage("The current user cannot perform operations on cart \"$customerQuoteMaskedId\""); - - $query = $this->prepareMutationQuery($customerQuoteMaskedId, $customerQuoteItemId); - $this->graphQlQuery($query); + $query = $this->getQuery($maskedQuoteId, $notExistentItemId); + $this->graphQlMutation($query); } /** @@ -161,7 +103,7 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -181,12 +123,51 @@ public function dataProviderUpdateWithMissedRequiredParameters(): array ]; } + /** + * _security + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testRemoveItemIfItemIsNotBelongToCart() + { + $firstQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $secondQuoteItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute( + 'test_order_with_virtual_product_without_address', + 'virtual-product' + ); + + $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + + $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId); + $this->graphQlMutation($query); + } + + /** + * _security + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testRemoveItemFromCustomerCart() + { + $customerQuoteMaskedId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $customerQuoteItemId = $this->getQuoteItemIdByReservedQuoteIdAndSku->execute('test_quote', 'simple_product'); + + $this->expectExceptionMessage("The current user cannot perform operations on cart \"$customerQuoteMaskedId\""); + + $query = $this->getQuery($customerQuoteMaskedId, $customerQuoteItemId); + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param int $itemId * @return string */ - private function prepareMutationQuery(string $maskedQuoteId, int $itemId): string + private function getQuery(string $maskedQuoteId, int $itemId): string { return <<<QUERY mutation { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php index 6965cd03f86f5..5b0e4cfdb6c52 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -76,7 +76,7 @@ public function testSetNewBillingAddress() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -149,7 +149,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -203,7 +203,7 @@ public function testSetBillingAddressToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -237,7 +237,7 @@ public function testSetBillingAddressFromAddressBook() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -276,7 +276,7 @@ public function testSetBillingAddressOnNonExistentCart() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -310,7 +310,50 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewBillingAddressRedundantStreetLine() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2", "test street 3"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + firstname + } + } + } +} +QUERY; + + self::expectExceptionMessage('"Street Address" cannot contain more than 2 lines.'); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflineShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflineShippingMethodsOnCartTest.php index 477c93efd31d0..2c1333aa77326 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflineShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetOfflineShippingMethodsOnCartTest.php @@ -64,7 +64,7 @@ public function testSetOfflineShippingMethod(string $carrierCode, string $method $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 879d0fd917291..7f9aaf4296c30 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -44,7 +44,7 @@ public function testSetPaymentOnCartWithSimpleProduct() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); @@ -66,7 +66,7 @@ public function testSetPaymentOnCartWithSimpleProductAndWithoutAddress() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -80,7 +80,7 @@ public function testSetPaymentOnCartWithVirtualProduct() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); @@ -103,7 +103,7 @@ public function testSetNonExistentPaymentMethod() $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -116,7 +116,7 @@ public function testSetPaymentOnNonExistentCart() $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $query = $this->getQuery($maskedQuoteId, $methodCode); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -137,7 +137,7 @@ public function testSetPaymentMethodToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -169,7 +169,7 @@ public function testSetPaymentMethodWithoutRequiredParameters(string $input, str } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -207,7 +207,7 @@ public function testReSetPayment() $methodCode = Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE; $query = $this->getQuery($maskedQuoteId, $methodCode); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 18523feb261b9..888b0e87734b6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -78,7 +78,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); $cartResponse = $response['setShippingAddressesOnCart']['cart']; @@ -130,7 +130,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -166,7 +166,7 @@ public function testSetShippingAddressFromAddressBook() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -205,7 +205,7 @@ public function testSetShippingAddressToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -243,7 +243,51 @@ public function testSetNewShippingAddressWithMissedRequiredParameters(string $in } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetNewShippingAddressOnCartWithRedundantStreetLine() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2", "test street 3"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + firstname + } + } + } +} +QUERY; + self::expectExceptionMessage('"Street Address" cannot contain more than 2 lines.'); + $this->graphQlMutation($query); } /** @@ -320,7 +364,7 @@ public function testSetMultipleNewShippingAddresses() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php index 2eac002253ff0..2f3c109b99523 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php @@ -58,7 +58,7 @@ public function testSetShippingMethodOnCartWithSimpleProduct() $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); @@ -101,7 +101,7 @@ public function testSetShippingMethodOnCartWithSimpleProductAndWithoutAddress() $carrierCode, $quoteAddressId ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -125,7 +125,7 @@ public function testReSetShippingMethod() $carrierCode, $quoteAddressId ); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertArrayHasKey('setShippingMethodsOnCart', $response); self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); @@ -175,7 +175,7 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -314,7 +314,7 @@ public function testSetMultipleShippingMethods() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -343,7 +343,7 @@ public function testSetShippingMethodToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -370,7 +370,7 @@ public function testSetShippingMethodIfGuestIsNotOwnerOfAddress() $this->expectExceptionMessage( "Cart does not contain address with ID \"{$anotherQuoteAddressId}\"" ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php index fca7a4287620b..1b8cf2e1c57f7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php @@ -60,7 +60,7 @@ public function testUpdateCartItemQty() $qty = 2; $query = $this->getQuery($maskedQuoteId, $itemId, $qty); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertArrayHasKey('updateCartItems', $response); $this->assertArrayHasKey('cart', $response['updateCartItems']); @@ -84,7 +84,7 @@ public function testRemoveCartItemIfQuantityIsZero() $qty = 0; $query = $this->getQuery($maskedQuoteId, $itemId, $qty); - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertArrayHasKey('updateCartItems', $response); $this->assertArrayHasKey('cart', $response['updateCartItems']); @@ -100,7 +100,7 @@ public function testRemoveCartItemIfQuantityIsZero() public function testUpdateItemInNonExistentCart() { $query = $this->getQuery('non_existent_masked_id', 1, 2); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -116,7 +116,7 @@ public function testUpdateNonExistentItem() $this->expectExceptionMessage("Could not find cart item with id: {$notExistentItemId}."); $query = $this->getQuery($maskedQuoteId, $notExistentItemId, 2); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -142,7 +142,7 @@ public function testUpdateItemIfItemIsNotBelongToCart() $this->expectExceptionMessage("Could not find cart item with id: {$secondQuoteItemId}."); $query = $this->getQuery($firstQuoteMaskedId, $secondQuoteItemId, 2); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -158,7 +158,7 @@ public function testUpdateItemFromCustomerCart() $this->expectExceptionMessage("The current user cannot perform operations on cart \"$customerQuoteMaskedId\""); $query = $this->getQuery($customerQuoteMaskedId, $customerQuoteItemId, 2); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -186,7 +186,7 @@ public function testUpdateWithMissedCartItemId() } } QUERY; - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -217,7 +217,7 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string } QUERY; $this->expectExceptionMessage($message); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php index 05e3e608c5e52..ae6faae7650b9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php @@ -12,6 +12,9 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Tests for send email to friend + */ class SendFriendTest extends GraphQlAbstract { @@ -66,7 +69,7 @@ public function testSendFriend() } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); self::assertEquals('Name', $response['sendEmailToFriend']['sender']['name']); self::assertEquals('e@mail.com', $response['sendEmailToFriend']['sender']['email']); self::assertEquals('Lorem Ipsum', $response['sendEmailToFriend']['sender']['message']); @@ -117,7 +120,7 @@ public function testSendWithoutExistProduct() $this->expectExceptionMessage( 'The product that was requested doesn\'t exist. Verify the product and try again.' ); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -181,7 +184,7 @@ public function testMaxSendEmailToFriend() QUERY; $this->expectException(\Exception::class); $this->expectExceptionMessage("No more than {$sendFriend->getMaxRecipients()} emails can be sent at a time."); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -214,7 +217,7 @@ public function testErrors(string $input, string $errorMessage) QUERY; $this->expectException(\Exception::class); $this->expectExceptionMessage($errorMessage); - $this->graphQlQuery($query); + $this->graphQlMutation($query); } /** @@ -269,8 +272,9 @@ public function testLimitMessagesPerHour() "You can't send messages more than {$sendFriend->getMaxSendsToFriend()} times an hour." ); - for ($i = 0; $i <= $sendFriend->getMaxSendsToFriend() + 1; $i++) { - $this->graphQlQuery($query); + $maxSendToFriends = $sendFriend->getMaxSendsToFriend(); + for ($i = 0; $i <= $maxSendToFriends + 1; $i++) { + $this->graphQlMutation($query); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php index b6e1a61f0357c..c85f63c083700 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlMutationTest.php @@ -28,10 +28,31 @@ public function testMutation() } MUTATION; - $response = $this->graphQlQuery($query); + $response = $this->graphQlMutation($query); $this->assertArrayHasKey('testItem', $response); $testItem = $response['testItem']; $this->assertArrayHasKey('integer_list', $testItem); $this->assertEquals([4, 5, 6], $testItem['integer_list']); } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Mutation requests allowed only for POST requests + */ + public function testMutationIsNotAllowedViaGetRequest() + { + $id = 3; + + $query = <<<MUTATION +mutation { + testItem(id: {$id}) { + item_id, + name, + integer_list + } +} +MUTATION; + + $this->graphQlQuery($query, [], '', []); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php index d59e255daa109..2db06e383758f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php @@ -58,4 +58,46 @@ public function testQueryTestModuleExtensionAttribute() $this->assertArrayHasKey('integer_list', $testItem); $this->assertEquals([3, 4, 5], $testItem['integer_list']); } + + public function testQueryViaGetRequestReturnsResults() + { + $id = 1; + + $query = <<<QUERY +{ + testItem(id: {$id}) + { + item_id + name + } +} +QUERY; + + $response = $this->graphQlQuery($query, [], '', []); + + $this->assertArrayHasKey('testItem', $response); + } + + public function testQueryViaGetRequestWithVariablesReturnsResults() + { + $id = 1; + + $query = <<<QUERY +query getTestItem(\$id: Int!) +{ + testItem(id: \$id) + { + item_id + name + } +} +QUERY; + $variables = [ + "id" => $id + ]; + + $response = $this->graphQlQuery($query, $variables, '', []); + + $this->assertArrayHasKey('testItem', $response); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php index 463f2c4af101f..6861bb8dbb240 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Ups/SetUpsShippingMethodsOnCartTest.php @@ -142,6 +142,6 @@ private function sendRequestWithToken(string $query): array $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - return $this->graphQlQuery($query, [], '', $headerMap); + return $this->graphQlMutation($query, [], '', $headerMap); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Vault/CustomerPaymentTokensTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Vault/CustomerPaymentTokensTest.php index 89fbbb9c49ed3..45c82906d255d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Vault/CustomerPaymentTokensTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Vault/CustomerPaymentTokensTest.php @@ -14,6 +14,9 @@ use Magento\Vault\Model\ResourceModel\PaymentToken as TokenResource; use Magento\Vault\Model\ResourceModel\PaymentToken\CollectionFactory; +/** + * Tests for customer payment tokens + */ class CustomerPaymentTokensTest extends GraphQlAbstract { /** @@ -139,7 +142,12 @@ public function testDeletePaymentToken() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $response = $this->graphQlMutation( + $query, + [], + '', + $this->getCustomerAuthHeaders($currentEmail, $currentPassword) + ); $this->assertTrue($response['deletePaymentToken']['result']); $this->assertEquals(1, count($response['deletePaymentToken']['customerPaymentTokens']['items'])); @@ -168,7 +176,7 @@ public function testDeletePaymentTokenIfUserIsNotAuthorized() } } QUERY; - $this->graphQlQuery($query, [], ''); + $this->graphQlMutation($query, [], ''); } /** @@ -190,7 +198,7 @@ public function testDeletePaymentTokenInvalidPublicHash() } } QUERY; - $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } /** diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml index 9a6a66091d427..dc91608f361c2 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/TestCase/SearchEntityResultsTest.xml @@ -57,11 +57,9 @@ <variation name="SearchEntityResultsTestVariation12" summary="Search for simple product name using 2 symbols query length" ticketId="MAGETWO-36542"> <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::default::name</data> <data name="queryLength" xsi:type="string">2</data> - <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResultMessage" /> - <constraint name="Magento\CatalogSearch\Test\Constraint\AssertCatalogSearchNoResult" /> + <constraint name="Magento\CatalogSearch\Test\Constraint\AssertProductCanBeOpenedFromSearchResult" /> </variation> <variation name="SearchEntityResultsTestVariation13" summary="Search for simple product name using 3 symbols query length" ticketId="MAGETWO-36542"> - <data name="issue" xsi:type="string">MAGETWO-65509: [FT] Magento\CatalogSearch\Test\TestCase\SearchEntityResultsTest fails on Jenkins</data> <data name="tag" xsi:type="string">stable:no</data> <data name="catalogSearch/data/query_text/value" xsi:type="string">catalogProductSimple::default::name</data> <data name="queryLength" xsi:type="string">3</data> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php index 595a33344c7e8..bd4dd0c8daf0c 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php @@ -4,8 +4,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Backend\Controller\Adminhtml\Dashboard; +/** + * Test product viewed backend controller. + */ class ProductsViewedTest extends \Magento\TestFramework\TestCase\AbstractBackendController { /** @@ -14,6 +18,7 @@ class ProductsViewedTest extends \Magento\TestFramework\TestCase\AbstractBackend */ public function testExecute() { + $this->getRequest()->setMethod("POST"); $this->dispatch('backend/admin/dashboard/productsViewed/'); $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php new file mode 100644 index 0000000000000..91cea7dc96602 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; + +$store = $storeManager->getStore(); +$quote->setReservedOrderId('multishipping_quote_id_braintree') + ->setStoreId($store->getId()) + ->setCustomerEmail('customer001@test.com'); + +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +$quote->collectTotals(); +$quoteRepository->save($quote); + +$items = $quote->getAllItems(); +$addressList = $quote->getAllShippingAddresses(); + +foreach ($addressList as $key => $address) { + $item = $items[$key]; + // set correct quantity per shipping address + $item->setQty(1); + $address->setTotalQty(1); + $address->addItem($item); +} + +// assign virtual product to the billing address +$billingAddress = $quote->getBillingAddress(); +$virtualItem = $items[sizeof($items) - 1]; +$billingAddress->setTotalQty(1); +$billingAddress->addItem($virtualItem); + +// need to recollect totals +$quote->setTotalsCollectedFlag(false); +$quote->collectTotals(); +$quoteRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php new file mode 100644 index 0000000000000..3e1db90f1f2c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Braintree\Model\Ui\ConfigProvider; + +/** + * @var Magento\Quote\Model\Quote $quote + */ + +if (empty($quote)) { + throw new \Exception('$quote should be defined in the parent fixture'); +} + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var PaymentInterface $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::CODE); +$quote->setPayment($payment); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php new file mode 100644 index 0000000000000..e4bba222078b0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/payment_braintree_paypal.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote\Payment; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; + +/** + * @var Magento\Quote\Model\Quote $quote + */ + +if (empty($quote)) { + throw new \Exception('$quote should be defined in the parent fixture'); +} + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var PaymentInterface $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod(ConfigProvider::PAYPAL_CODE); +$quote->setPayment($payment); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php new file mode 100644 index 0000000000000..1c56e611dd6db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); + +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php'; +require __DIR__ . '/payment_braintree.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php'; +require __DIR__ . '/assign_items_per_address.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php new file mode 100644 index 0000000000000..4bd8e926abb76 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Model\Quote; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); + +/** @var Quote $quote */ +$quote = $objectManager->create(Quote::class); + +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/shipping_address_list.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/billing_address.php'; +require __DIR__ . '/payment_braintree_paypal.php'; +require __DIR__ . '/../../../Magento/Multishipping/Fixtures/items.php'; +require __DIR__ . '/assign_items_per_address.php'; diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php new file mode 100644 index 0000000000000..91bc0388d8551 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Model/MultishippingTest.php @@ -0,0 +1,254 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Braintree\Model; + +use Braintree\Result\Successful; +use Braintree\Transaction; +use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Braintree\Model\Adapter\BraintreeAdapter; +use Magento\Braintree\Model\Adapter\BraintreeAdapterFactory; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Multishipping\Model\Checkout\Type\Multishipping; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use \PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Payment\Gateway\Command\ResultInterface as CommandResultInterface; + +/** + * Tests Magento\Multishipping\Model\Checkout\Type\Multishipping with Braintree and BraintreePayPal payments. + * + * @magentoAppArea frontend + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MultishippingTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var BraintreeAdapter|MockObject + */ + private $adapter; + + /** + * @var Multishipping + */ + private $model; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $orderSender = $this->getMockBuilder(OrderSender::class) + ->disableOriginalConstructor() + ->getMock(); + + $adapterFactory = $this->getMockBuilder(BraintreeAdapterFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + $adapterFactory->method('create') + ->willReturn($this->adapter); + + $this->objectManager->addSharedInstance($adapterFactory, BraintreeAdapterFactory::class); + $this->objectManager->addSharedInstance($this->getPaymentNonceMock(), GetPaymentNonceCommand::class); + + $this->model = $this->objectManager->create( + Multishipping::class, + ['orderSender' => $orderSender] + ); + } + + /** + * Checks a case when multiple orders are created successfully using Braintree payment method. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree.php + * @magentoConfigFixture current_store payment/braintree/active 1 + * @return void + */ + public function testCreateOrdersWithBraintree() + { + $this->adapter->method('sale') + ->willReturn( + $this->getTransactionStub() + ); + $this->createOrders(); + } + + /** + * Checks a case when multiple orders are created successfully using Braintree PayPal payment method. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Braintree/Fixtures/quote_with_split_items_braintree_paypal.php + * @magentoConfigFixture current_store payment/braintree_paypal/active 1 + * @return void + */ + public function testCreateOrdersWithBraintreePaypal() + { + $this->adapter->method('sale') + ->willReturn( + $this->getTransactionPaypalStub() + ); + $this->createOrders(); + } + + /** + * Creates orders for multishipping checkout flow. + * + * @return void + */ + private function createOrders() + { + $expectedPlacedOrdersNumber = 3; + $quote = $this->getQuote('multishipping_quote_id_braintree'); + + /** @var CheckoutSession $session */ + $session = $this->objectManager->get(CheckoutSession::class); + $session->replaceQuote($quote); + + $this->model->createOrders(); + + $orderList = $this->getOrderList((int)$quote->getId()); + self::assertCount( + $expectedPlacedOrdersNumber, + $orderList, + 'Total successfully placed orders number mismatch' + ); + } + + /** + * Creates stub for Braintree capture Transaction. + * + * @return Successful + */ + private function getTransactionStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $transaction->creditCard = [ + 'last4' => '1111', + 'cardType' => 'Visa', + 'expirationMonth' => '12', + 'expirationYear' => '2021' + ]; + + $creditCardDetails = new \stdClass(); + $creditCardDetails->token = '4fdg'; + $creditCardDetails->expirationMonth = '12'; + $creditCardDetails->expirationYear = '2021'; + $creditCardDetails->cardType = 'Visa'; + $creditCardDetails->last4 = '1111'; + $creditCardDetails->expirationDate = '12/2021'; + $transaction->creditCardDetails = $creditCardDetails; + + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Creates stub for BraintreePaypal capture Transaction. + * + * @return Successful + */ + private function getTransactionPaypalStub(): Successful + { + $transaction = $this->getMockBuilder(Transaction::class) + ->disableOriginalConstructor() + ->getMock(); + $transaction->status = 'submitted_for_settlement'; + $transaction->paypal = [ + 'token' => 'fchxqx', + 'payerEmail' => 'payer@example.com', + 'paymentId' => 'PAY-33ac47a28e7f54791f6cda45', + ]; + $paypalDetails = new \stdClass(); + $paypalDetails->token = 'fchxqx'; + $paypalDetails->payerEmail = 'payer@example.com'; + $paypalDetails->paymentId = '33ac47a28e7f54791f6cda45'; + $transaction->paypalDetails = $paypalDetails; + + $response = new Successful(); + $response->success = true; + $response->transaction = $transaction; + + return $response; + } + + /** + * Retrieves quote by reserved order id. + * + * @param string $reservedOrderId + * @return Quote + */ + private function getQuote(string $reservedOrderId): Quote + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) + ->create(); + + /** @var CartRepositoryInterface $quoteRepository */ + $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + $items = $quoteRepository->getList($searchCriteria)->getItems(); + + return array_pop($items); + } + + /** + * Get list of orders by quote id. + * + * @param int $quoteId + * @return array + */ + private function getOrderList(int $quoteId): array + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $quoteId) + ->create(); + + /** @var OrderRepositoryInterface $orderRepository */ + $orderRepository = $this->objectManager->get(OrderRepositoryInterface::class); + return $orderRepository->getList($searchCriteria)->getItems(); + } + + /** + * Returns GetPaymentNonceCommand command mock. + * + * @return MockObject + */ + private function getPaymentNonceMock(): MockObject + { + $commandResult = $this->createMock(CommandResultInterface::class); + $commandResult->method('get') + ->willReturn(['paymentMethodNonce' => 'testNonce']); + $paymentNonce = $this->createMock(GetPaymentNonceCommand::class); + $paymentNonce->method('execute') + ->willReturn($commandResult); + + return $paymentNonce; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store.php new file mode 100644 index 0000000000000..6448a570424e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +include 'testsuite/Magento/Store/_files/second_store.php'; +include 'testsuite/Magento/Customer/_files/customer.php'; + +$quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); +$quote->setStoreId($store->getId()) + ->setIsActive(true) + ->setIsMultiShipping(false) + ->setReservedOrderId('test_order_1_not_default_store') + ->setCustomerId($customer->getId()) + ->save(); + +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($quote->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php new file mode 100644 index 0000000000000..e3e1513cb6144 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$quote = $objectManager->create(\Magento\Quote\Model\Quote::class); +$quote->load('test_order_1_not_default_store', 'reserved_order_id')->delete(); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store.php new file mode 100644 index 0000000000000..bbd3d5efbe8c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +include 'testsuite/Magento/Store/_files/second_store.php'; + +$quote = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Quote\Model\Quote::class); +$quote->setStoreId($store->getId()) + ->setIsActive(true) + ->setIsMultiShipping(false) + ->setReservedOrderId('test_order_1_not_default_store_guest') + ->save(); + +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($quote->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store_rollback.php new file mode 100644 index 0000000000000..f511133280e7f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_guest_not_default_store_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$quote = $objectManager->create(\Magento\Quote\Model\Quote::class); +$quote->load('test_order_1_not_default_store_guest', 'reserved_order_id')->delete(); diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php similarity index 63% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php index 223ef35c0dcd3..7493d31f02b31 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ConverterStub.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ConverterStub.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Paypal\Model\Config\Structure\Reader; +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\Config\Model\Config\Structure\Converter; /** - * Class ConverterStub + * Class ConverterStub used for ReaderTest. */ -class ConverterStub extends \Magento\Config\Model\Config\Structure\Converter +class ConverterStub extends Converter { /** + * Convert dom document wrapper. + * * @param \DOMDocument $document * @return array|null */ @@ -20,7 +26,7 @@ public function getArrayData(\DOMDocument $document) } /** - * Convert dom document + * Convert dom document. * * @param \DOMNode $source * @return array diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php similarity index 53% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php index ed1366ad737f9..866ff91678ec4 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderStub.php +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderStub.php @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Paypal\Model\Config\Structure\Reader; +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\Config\Model\Config\Structure\Reader; /** - * Class ReaderStub + * Class ReaderStub used for testing protected Reader::_readFiles() method. */ -class ReaderStub extends \Magento\Config\Model\Config\Structure\Reader +class ReaderStub extends Reader { /** + * Wrapper for protected Reader::_readFiles() method. + * * @param array $fileList * @return array * @throws \Magento\Framework\Exception\LocalizedException diff --git a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php new file mode 100644 index 0000000000000..eef8e68458d91 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/ReaderTest.php @@ -0,0 +1,146 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Config\Model\Config\Structure\Reader; + +use Magento\Config\Model\Config\SchemaLocator; +use Magento\Framework\App\Utility\Files; +use Magento\Framework\Config\Dom; +use Magento\Framework\Config\FileResolverInterface; +use Magento\Framework\Config\ValidationStateInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\TemplateEngine\Xhtml\CompilerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** + * Class ReaderTest check Magento\Config\Model\Config\Structure\Reader::_readFiles() method. + */ +class ReaderTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test config location. + * + * @string + */ + const CONFIG = '/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/'; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Files + */ + private $fileUtility; + + /** + * @var ValidationStateInterface + */ + private $validationStateMock; + + /** + * @var \Magento\Framework\Config\SchemaLocatorInterface + */ + private $schemaLocatorMock; + + /** + * @var FileResolverInterface + */ + private $fileResolverMock; + + /** + * @var ReaderStub + */ + private $reader; + + /** + * @var ConverterStub + */ + private $converter; + + /** + * @var CompilerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $compiler; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->fileUtility = Files::init(); + + $this->validationStateMock = $this->getMockBuilder(ValidationStateInterface::class) + ->setMethods(['isValidationRequired']) + ->getMockForAbstractClass(); + $this->schemaLocatorMock = $this->getMockBuilder(SchemaLocator::class) + ->disableOriginalConstructor() + ->setMethods(['getPerFileSchema']) + ->getMock(); + $this->fileResolverMock = $this->getMockBuilder(FileResolverInterface::class) + ->getMockForAbstractClass(); + + $this->validationStateMock->expects($this->atLeastOnce()) + ->method('isValidationRequired') + ->willReturn(false); + $this->schemaLocatorMock->expects($this->atLeastOnce()) + ->method('getPerFileSchema') + ->willReturn(false); + + $this->converter = $this->objectManager->create(ConverterStub::class); + + //Isolate test from actual configuration, and leave only sample data. + $this->compiler = $this->getMockBuilder(CompilerInterface::class) + ->disableOriginalConstructor() + ->setMethods(['compile']) + ->getMockForAbstractClass(); + + $this->reader = $this->objectManager->create( + ReaderStub::class, + [ + 'fileResolver' => $this->fileResolverMock, + 'converter' => $this->converter, + 'schemaLocator' => $this->schemaLocatorMock, + 'validationState' => $this->validationStateMock, + 'fileName' => 'no_existing_file.xml', + 'compiler' => $this->compiler, + 'domDocumentClass' => Dom::class + ] + ); + } + + /** + * The test checks the file structure after processing the nodes responsible for inserting content. + * + * @return void + */ + public function testXmlConvertedConfigurationAndCompereStructure() + { + $actual = $this->reader->readFiles(['actual' => $this->getContent()]); + + $document = new \DOMDocument(); + $document->loadXML($this->getContent()); + + $expected = $this->converter->getArrayData($document); + + $this->assertEquals($expected, $actual); + } + + /** + * Get config sample data for test. + * + * @return string + */ + protected function getContent() + { + $files = $this->fileUtility->getFiles([BP . static::CONFIG], 'config.xml'); + + return file_get_contents(reset($files)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual/config.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml similarity index 100% rename from dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual/config.xml rename to dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml diff --git a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php index 7f8996daa6e97..10a6b9d8caae4 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/GraphQl/Config/GraphQlReaderTest.php @@ -8,6 +8,7 @@ namespace Magento\Framework\GraphQl\Config; use Magento\Framework\App\Cache; +use Magento\Framework\App\Request\Http; use Magento\Framework\GraphQl\Config; use Magento\Framework\GraphQl\Schema\SchemaGenerator; use Magento\Framework\ObjectManagerInterface; @@ -175,8 +176,9 @@ enumValues(includeDeprecated: true) { 'operationName' => 'IntrospectionQuery' ]; /** @var Http $request */ - $request = $this->objectManager->get(\Magento\Framework\App\Request\Http::class); + $request = $this->objectManager->get(Http::class); $request->setPathInfo('/graphql'); + $request->setMethod('POST'); $request->setContent(json_encode($postData)); $headers = $this->objectManager->create(\Zend\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php index 384892d6fd5d2..d0d746812ec44 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Controller/GraphQlControllerTest.php @@ -38,6 +38,9 @@ class GraphQlControllerTest extends \Magento\TestFramework\Indexer\TestCase /** @var MetadataPool */ private $metadataPool; + /** @var Http */ + private $request; + public static function setUpBeforeClass() { $db = Bootstrap::getInstance()->getBootstrap() @@ -57,6 +60,7 @@ protected function setUp() : void $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); $this->metadataPool = $this->objectManager->get(MetadataPool::class); + $this->request = $this->objectManager->get(Http::class); } /** @@ -86,27 +90,120 @@ public function testDispatch() : void } QUERY; $postData = [ - 'query' => $query, - 'variables' => null, + 'query' => $query, + 'variables' => null, 'operationName' => null ]; - /** @var Http $request */ - $request = $this->objectManager->get(\Magento\Framework\App\Request\Http::class); - $request->setPathInfo('/graphql'); - $request->setContent(json_encode($postData)); + + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent(json_encode($postData)); $headers = $this->objectManager->create(\Zend\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); - $request->setHeaders($headers); - $response = $this->graphql->dispatch($request); + $this->request->setHeaders($headers); + $response = $this->graphql->dispatch($this->request); $output = $this->jsonSerializer->unserialize($response->getContent()); $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertTrue(!empty($output['data']['products']['items']), 'Products array has items'); - $this->assertTrue(!empty($output['data']['products']['items'][0]), 'Products array has items'); - $this->assertEquals($output['data']['products']['items'][0]['id'], $product->getData($linkField)); - $this->assertEquals($output['data']['products']['items'][0]['sku'], $product->getSku()); - $this->assertEquals($output['data']['products']['items'][0]['name'], $product->getName()); + $this->assertNotEmpty($output['data']['products']['items'], 'Products array has items'); + $this->assertNotEmpty($output['data']['products']['items'][0], 'Products array has items'); + $this->assertEquals($product->getData($linkField), $output['data']['products']['items'][0]['id']); + $this->assertEquals($product->getSku(), $output['data']['products']['items'][0]['sku']); + $this->assertEquals($product->getName(), $output['data']['products']['items'][0]['name']); + } + + /** + * Test request is dispatched and response generated when using GET request with query string + * + * @return void + */ + public function testDispatchWithGet() : void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + /** @var ProductInterface $product */ + $product = $productRepository->get('simple1'); + + $query + = <<<QUERY + { + products(filter: {sku: {eq: "simple1"}}) + { + items { + id + name + sku + } + } + } +QUERY; + + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('GET'); + $this->request->setQueryValue('query', $query); + $response = $this->graphql->dispatch($this->request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertNotEmpty($output['data']['products']['items'], 'Products array has items'); + $this->assertNotEmpty($output['data']['products']['items'][0], 'Products array has items'); + $this->assertEquals($product->getData($linkField), $output['data']['products']['items'][0]['id']); + $this->assertEquals($product->getSku(), $output['data']['products']['items'][0]['sku']); + $this->assertEquals($product->getName(), $output['data']['products']['items'][0]['name']); + } + + /** Test request is dispatched and response generated when using GET request with parameterized query string + * + * @return void + */ + public function testDispatchGetWithParameterizedVariables() : void + { + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + + /** @var ProductInterface $product */ + $product = $productRepository->get('simple1'); + $query = <<<QUERY +query GetProducts(\$filterInput:ProductFilterInput){ + products( + filter:\$filterInput + ){ + items{ + id + name + sku + } + } +} +QUERY; + + $variables = [ + 'filterInput' => [ + 'sku' => ['eq' => 'simple1'] + ] + ]; + $queryParams = [ + 'query' => $query, + 'variables' => json_encode($variables), + 'operationName' => 'GetProducts' + ]; + + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('GET'); + $this->request->setParams($queryParams); + $response = $this->graphql->dispatch($this->request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertNotEmpty($output['data']['products']['items'], 'Products array has items'); + $this->assertNotEmpty($output['data']['products']['items'][0], 'Products array has items'); + $this->assertEquals($product->getData($linkField), $output['data']['products']['items'][0]['id']); + $this->assertEquals($product->getSku(), $output['data']['products']['items'][0]['sku']); + $this->assertEquals($product->getName(), $output['data']['products']['items'][0]['name']); } /** @@ -136,25 +233,25 @@ public function testError() : void QUERY; $postData = [ - 'query' => $query, - 'variables' => null, + 'query' => $query, + 'variables' => null, 'operationName' => null ]; - /** @var Http $request */ - $request = $this->objectManager->get(\Magento\Framework\App\Request\Http::class); - $request->setPathInfo('/graphql'); - $request->setContent(json_encode($postData)); + + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent(json_encode($postData)); $headers = $this->objectManager->create(\Zend\Http\Headers::class) ->addHeaders(['Content-Type' => 'application/json']); - $request->setHeaders($headers); - $response = $this->graphql->dispatch($request); + $this->request->setHeaders($headers); + $response = $this->graphql->dispatch($this->request); $outputResponse = $this->jsonSerializer->unserialize($response->getContent()); if (isset($outputResponse['errors'][0])) { if (is_array($outputResponse['errors'][0])) { foreach ($outputResponse['errors'] as $error) { $this->assertEquals( - $error['category'], - \Magento\Framework\GraphQl\Exception\GraphQlInputException::EXCEPTION_CATEGORY + \Magento\Framework\GraphQl\Exception\GraphQlInputException::EXCEPTION_CATEGORY, + $error['category'] ); if (isset($error['message'])) { $this->assertEquals($error['message'], 'Invalid entity_type specified: invalid'); @@ -168,12 +265,4 @@ public function testError() : void } } } - - /** - * teardown - */ - public function tearDown() - { - parent::tearDown(); - } } diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired.php new file mode 100644 index 0000000000000..5316b184ecd15 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SalesRule\Model\CouponFactory; +use Magento\SalesRule\Model\Spi\CouponResourceInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var CouponResourceInterface $couponResource */ +$couponResource = Bootstrap::getObjectManager()->get(CouponResourceInterface::class); +/** @var CouponFactory $couponFactory */ +$couponFactory = Bootstrap::getObjectManager()->get(CouponFactory::class); + +$coupon = $couponFactory->create(); +$coupon->loadByCode('2?ds5!2d'); +$yesterday = new \DateTime(); +$yesterday->add(\DateInterval::createFromDateString('-1 day')); +$coupon->setExpirationDate($yesterday->format('Y-m-d')); +$couponResource->save($coupon); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired_rollback.php new file mode 100644 index 0000000000000..32c3d78bafd09 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/make_coupon_expired_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SalesRule\Model\CouponFactory; +use Magento\SalesRule\Model\Spi\CouponResourceInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var CouponResourceInterface $couponResource */ +$couponResource = Bootstrap::getObjectManager()->get(CouponResourceInterface::class); +/** @var CouponFactory $couponFactory */ +$couponFactory = Bootstrap::getObjectManager()->get(CouponFactory::class); + +$coupon = $couponFactory->create(); +$coupon->loadByCode('2?ds5!2d'); + +if ($coupon->getId()) { + $coupon->setExpirationDate(null); + $couponResource->save($coupon); +} diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product.php new file mode 100644 index 0000000000000..e58c6b21d8d23 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SalesRule\Api\Data\ConditionInterface; +use Magento\SalesRule\Api\Data\ConditionInterfaceFactory; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Model\CouponFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var CouponFactory $couponFactory */ +$couponFactory = Bootstrap::getObjectManager()->get(CouponFactory::class); +/** @var ConditionInterfaceFactory $conditionFactory */ +$conditionFactory = Bootstrap::getObjectManager()->get(ConditionInterfaceFactory::class); +/** @var RuleRepositoryInterface $ruleRepository */ +$ruleRepository = Bootstrap::getObjectManager()->get(RuleRepositoryInterface::class); + +$couponCode = '2?ds5!2d'; +$sku = 'simple_product'; + +$coupon = $couponFactory->create(); +$coupon->loadByCode($couponCode); +$ruleId = $coupon->getRuleId(); +$salesRule = $ruleRepository->getById($ruleId); + +/** @var ConditionInterface $conditionProductSku */ +$conditionProductSku = $conditionFactory->create(); +$conditionProductSku->setConditionType(\Magento\SalesRule\Model\Rule\Condition\Product::class); +$conditionProductSku->setAttributeName('sku'); +$conditionProductSku->setValue('1'); +$conditionProductSku->setOperator('!='); +$conditionProductSku->setValue($sku); + +/** @var ConditionInterface $conditionProductFound */ +$conditionProductFound = $conditionFactory->create(); +$conditionProductFound->setConditionType(\Magento\SalesRule\Model\Rule\Condition\Product\Found::class); +$conditionProductFound->setValue('1'); +$conditionProductFound->setAggregatorType('all'); +$conditionProductFound->setConditions([$conditionProductSku]); + +/** @var ConditionInterface $conditionCombine */ +$conditionCombine = $conditionFactory->create(); +$conditionCombine->setConditionType(\Magento\SalesRule\Model\Rule\Condition\Combine::class); +$conditionCombine->setValue('1'); +$conditionCombine->setAggregatorType('all'); +$conditionCombine->setConditions([$conditionProductFound]); + +$salesRule->setCondition($conditionCombine); +$ruleRepository->save($salesRule); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product_rollback.php new file mode 100644 index 0000000000000..86ab253f1d3c0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/restrict_coupon_usage_for_simple_product_rollback.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\SalesRule\Api\Data\ConditionInterface; +use Magento\SalesRule\Api\Data\ConditionInterfaceFactory; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Model\CouponFactory; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var CouponFactory $couponFactory */ +$couponFactory = Bootstrap::getObjectManager()->get(CouponFactory::class); +/** @var ConditionInterfaceFactory $conditionFactory */ +$conditionFactory = Bootstrap::getObjectManager()->get(ConditionInterfaceFactory::class); +/** @var RuleRepositoryInterface $ruleRepository */ +$ruleRepository = Bootstrap::getObjectManager()->get(RuleRepositoryInterface::class); + +$couponCode = '2?ds5!2d'; +$sku = 'simple_product'; + +$coupon = $couponFactory->create(); +$coupon->loadByCode($couponCode); + +if ($coupon->getId()) { + $ruleId = $coupon->getRuleId(); + $salesRule = $ruleRepository->getById($ruleId); + + /** @var ConditionInterface $conditionCombine */ + $conditionCombine = $conditionFactory->create(); + $conditionCombine->setConditions([]); + + $salesRule->setCondition($conditionCombine); + $ruleRepository->save($salesRule); +} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php deleted file mode 100644 index 6b966a045c982..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/ReaderTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Paypal\Model\Config\Structure\Reader; - -use Magento\Framework\ObjectManagerInterface; - -/** - * Class ReaderTest - */ -class ReaderTest extends \PHPUnit\Framework\TestCase -{ - const EXPECTED = '/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected'; - - const ACTUAL = '/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/actual'; - - /** - * @var ObjectManagerInterface - */ - protected $objectManager; - - /** - * @var \Magento\Framework\App\Utility\Files - */ - protected $fileUtility; - - /** - * @var \Magento\Framework\Config\ValidationStateInterface - */ - protected $validationStateMock; - - /** - * @var \Magento\Framework\Config\SchemaLocatorInterface - */ - protected $schemaLocatorMock; - - /** - * @var \Magento\Framework\Config\FileResolverInterface - */ - protected $fileResolverMock; - - /** - * @var \Magento\Paypal\Model\Config\Structure\Reader\ReaderStub - */ - protected $reader; - - /** - * @var \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub - */ - protected $converter; - - /** - * Set up - * - * @return void - */ - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->fileUtility = \Magento\Framework\App\Utility\Files::init(); - - $this->validationStateMock = $this->getMockBuilder(\Magento\Framework\Config\ValidationStateInterface::class) - ->setMethods(['isValidationRequired']) - ->getMockForAbstractClass(); - $this->schemaLocatorMock = $this->getMockBuilder(\Magento\Config\Model\Config\SchemaLocator::class) - ->disableOriginalConstructor() - ->setMethods(['getPerFileSchema']) - ->getMock(); - $this->fileResolverMock = $this->getMockBuilder(\Magento\Framework\Config\FileResolverInterface::class) - ->getMockForAbstractClass(); - - $this->validationStateMock->expects($this->atLeastOnce()) - ->method('isValidationRequired') - ->willReturn(false); - $this->schemaLocatorMock->expects($this->atLeastOnce()) - ->method('getPerFileSchema') - ->willReturn(false); - - /** @var \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub $converter */ - $this->converter = $this->objectManager->create( - \Magento\Paypal\Model\Config\Structure\Reader\ConverterStub::class - ); - - $this->reader = $this->objectManager->create( - \Magento\Paypal\Model\Config\Structure\Reader\ReaderStub::class, - [ - 'fileResolver' => $this->fileResolverMock, - 'converter' => $this->converter, - 'schemaLocator' => $this->schemaLocatorMock, - 'validationState' => $this->validationStateMock, - 'fileName' => 'no_existing_file.xml', - 'domDocumentClass' => \Magento\Framework\Config\Dom::class - ] - ); - } - - /** - * The test checks the file structure after processing the nodes responsible for inserting content - * - * @return void - */ - public function testXmlConvertedConfigurationAndCompereStructure() - { - $actual = $this->reader->readFiles(['actual' => $this->getActualContent()]); - - $document = new \DOMDocument(); - $document->loadXML($this->getExpectedContent()); - - $expected = $this->converter->getArrayData($document); - - $this->assertEquals($expected, $actual); - } - - /** - * @return string - */ - protected function getActualContent() - { - $files = $this->fileUtility->getFiles([BP . static::ACTUAL], 'config.xml'); - - return file_get_contents(reset($files)); - } - - /** - * @return string - */ - protected function getExpectedContent() - { - $files = $this->fileUtility->getFiles([BP . static::EXPECTED], 'config.xml'); - - return file_get_contents(reset($files)); - } -} diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml b/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml deleted file mode 100644 index 222b9974177de..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Config/Structure/Reader/_files/expected/config.xml +++ /dev/null @@ -1,2626 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> - <system> - <section id="payment"> - <group id="account" translate="label" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Merchant Location</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Merchant Country</label> - <comment>If not specified, Default Country from General Config will be used</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Country</frontend_model> - <source_model>Magento\Paypal\Model\System\Config\Source\MerchantCountry</source_model> - <backend_model>Magento\Paypal\Model\System\Config\Backend\MerchantCountry</backend_model> - <config_path>paypal/general/merchant_country</config_path> - </field> - </group> - <group id="recommended_solutions" translate="label" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Recommended Solutions:</label> - <fieldset_css>paypal-top-section paypal-recommended-header</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - </group> - <group id="other_paypal_payment_solutions" translate="label" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Other PayPal Payment Solutions:</label> - <fieldset_css>paypal-top-section paypal-other-header</fieldset_css> - <frontend_model>\Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - </group> - <group id="other_payment_methods" translate="label" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Other Payment Methods:</label> - <fieldset_css>paypal-top-section payments-other-header</fieldset_css> - <frontend_model>\Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> - </group> - </section> - <section id="payment_all_paypal" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_payflowpro" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="10"> - <label>Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment><![CDATA[Connect your merchant account with a fully customizable gateway that lets customers pay without leaving your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflowpro/active</attribute> - <attribute type="paypal_ec_separate">1</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payflow-pro.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="paypal_payflow_api_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="partner" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflowpro/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>User</label> - <config_path>payment/payflowpro/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflowpro/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflowpro/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflowpro/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflowpro/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflowpro/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflowpro/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_paypal_payflow" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/payflowpro/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="paypal_payflow_api_settings"/> - </requires> - </field> - <field id="payflowpro_cc_vault_active" translate="label" type="select" sortOrder="22" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Vault Enabled</label> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <config_path>payment/payflowpro_cc_vault/active</config_path> - <attribute type="shared">1</attribute> - <requires> - <group id="paypal_payflow_api_settings"/> - </requires> - </field> - </group> - <group id="settings_paypal_payflow" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payflow Pro</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflowpro/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="payflowpro_cc_vault_title" translate="label" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Vault Title</label> - <config_path>payment/payflowpro_cc_vault/title</config_path> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflowpro/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflowpro/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="heading_cc" translate="label" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Credit Card Settings</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="cctypes" translate="label comment" type="multiselect" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Allowed Credit Card Types</label> - <comment> - <![CDATA[Supporting of American Express cards require additional agreement. Learn more at <a href="http://www.paypal.com/amexupdate">http://www.paypal.com/amexupdate</a>.]]> - </comment> - <config_path>payment/payflowpro/cctypes</config_path> - <source_model>Magento\Paypal\Model\Config::getPayflowproCcTypesAsOptionArray</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_paypal_payflow_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflowpro/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflowpro/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflowpro/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflowpro/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="useccv" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflowpro/useccv</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="paypal_payflow_avs_check" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>CVV and AVS Settings</label> - <field id="heading_avs_settings" translate="label" sortOrder="0" showInDefault="1" showInWebsite="1"> - <label>Reject Transaction if:</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_street" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1"> - <label>AVS Street Does Not Match</label> - <config_path>payment/payflowpro/avs_street</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_zip" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1"> - <label>AVS Zip Does Not Match</label> - <config_path>payment/payflowpro/avs_zip</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_international" translate="label" type="select" sortOrder="3" showInDefault="1" showInWebsite="1"> - <label>Card Issuer Is Outside The United States</label> - <config_path>payment/payflowpro/avs_international</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="avs_security_code" translate="label" type="select" sortOrder="4" showInDefault="1" showInWebsite="1"> - <label>Card Security Code Does Not Match</label> - <config_path>payment/payflowpro/avs_security_code</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">0</attribute> - </field> - </group> - <group id="paypal_payflow_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="90"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - </group> - </group> - </group> - <group id="payflow_link" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Payflow Link</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment><![CDATA[Connect your merchant account with a PCI-compliant gateway that lets customers pay without leaving your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflow_link/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payflow-link.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="payflow_link_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="payflow_link_payflow_link" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payflow Link and Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account" translate="label" sortOrder="5"> - <frontend_class>not-required</frontend_class> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <attribute type="shared">1</attribute> - </field> - <field id="partner" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflow_link/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflow_link/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label comment" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>User</label> - <comment>If you do not have multiple users set up on your account, please re-enter your Vendor/Merchant Login here.</comment> - <config_path>payment/payflow_link/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflow_link/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflow_link/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflow_link/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflow_link/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflow_link/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="payflowlink_info" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Payflowlink\Info</frontend_model> - <attribute type="shared">1</attribute> - </field> - </group> - - - - <field id="enable_payflow_link" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable Payflow Link</label> - <config_path>payment/payflow_link/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="payflow_link_payflow_link"/> - </requires> - </field> - <field id="enable_express_checkout_basic" translate="label" type="select" sortOrder="40"> - <label>Enable Express Checkout</label> - <config_path>payment/payflow_express/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Express</frontend_model> - </field> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <requires> - <field id="enable_payflow_link"/> - </requires> - </field> - <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="41" showInDefault="1" showInWebsite="1"> - <comment><![CDATA[Payflow Link lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="50" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order" showInDefault="1" showInWebsite="1"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - - - - <group id="payflow_link_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="60"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="payflow_link_settings_bml_homepage" translate="label" showInWebsite="1" sortOrder="20" showInDefault="1" showInStore="1"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="payflow_link_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="payflow_link_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_homepage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_homepage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_homepage/payflow_link_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="payflow_link_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_categorypage" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="payflow_link_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="payflow_link_bml_categorypage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="40"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_categorypage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_categorypage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_categorypage/payflow_link_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="payflow_link_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_productpage" translate="label" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="50"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="payflow_link_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="payflow_link_bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_productpage_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_productpage_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_productpage/payflow_link_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="payflow_link_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="payflow_link_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="payflow_link_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="payflow_link_bml_checkout_size1" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_checkout_position">0</field> - </depends> - </field> - <field id="payflow_link_bml_checkout_size2" extends="payment_all_paypal/payflow_link/payflow_link_required/payflow_link_advertise_bml/payflow_link_settings_bml_checkout/payflow_link_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="payflow_link_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_payflow_link" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payflow Link</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflow_link/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_link/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_link/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_payflow_link_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_link/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_link/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_link/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_link/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_editable" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>CVV Entry is Editable</label> - <config_path>payment/payflow_link/csc_editable</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_required" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflow_link/csc_required</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="csc_editable">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="email_confirmation" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Send Email Confirmation</label> - <config_path>payment/payflow_link/email_confirmation</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="url_method" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>URL method for Cancel URL and Return URL</label> - <config_path>payment/payflow_link/url_method</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\UrlMethod</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="payflow_link_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - <group id="payflow_link_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="settings_payflow_link_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/title" /> - <field id="sort_order" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/sort_order" /> - <field id="payment_action" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/payment_action" /> - <field id="visible_on_product" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/visible_on_product" /> - <group id="settings_payflow_link_express_checkout_advanced" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/paypal_payflow_express_checkout_advanced"/> - </group> - </group> - <group id="express_checkout" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <fieldset_css>paypal-other-section</fieldset_css> - <comment>Add PayPal as an additional payment method to your checkout page.</comment> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-express-checkout.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="express_checkout_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="express_checkout_required_express_checkout" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="5"> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <frontend_class>not-required</frontend_class> - <comment> - <![CDATA[<a href="http://www.magentocommerce.com/paypal">Start accepting payments via PayPal!</a>]]> - </comment> - <tooltip>Don't have a PayPal account? Simply enter your email address.</tooltip> - <config_path>paypal/general/business_account</config_path> - <validate>validate-email</validate> - <attribute type="shared">1</attribute> - </field> - <field id="api_authentication" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>API Authentication Methods</label> - <config_path>paypal/wpp/api_authentication</config_path> - <source_model>Magento\Paypal\Model\Config::getApiAuthenticationMethods</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>API Username</label> - <config_path>paypal/wpp/api_username</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_password" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>API Password</label> - <config_path>paypal/wpp/api_password</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="api_signature" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>API Signature</label> - <config_path>paypal/wpp/api_signature</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - <depends> - <field id="api_authentication">0</field> - </depends> - </field> - <field id="api_cert" translate="label" type="file" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>API Certificate</label> - <config_path>paypal/wpp/api_cert</config_path> - <backend_model>Magento\Paypal\Model\System\Config\Backend\Cert</backend_model> - <attribute type="shared">1</attribute> - <depends> - <field id="api_authentication">1</field> - </depends> - </field> - <field id="api_wizard" translate="button_label attribute sandbox_button_label" sortOrder="70" showInDefault="1" showInWebsite="1"> - - <attribute type="button_label">Get Credentials from PayPal</attribute> - <attribute type="button_url"> - <![CDATA[https://www.paypal.com/webapps/merchantboarding/webflow/externalpartnerflow]]> - </attribute> - - <attribute type="sandbox_button_label">Sandbox Credentials</attribute> - <attribute type="sandbox_button_url"> - <![CDATA[https://www.sandbox.paypal.com/webapps/merchantboarding/webflow/externalpartnerflow]]> - </attribute> - - <!-- partnerId --> - <attribute type="partner_id">NB9WWHYEMVUMS</attribute> - <!-- partnerLogoUrl --> - <attribute type="partner_logo_url">Magento_Backend/web/images/logo-magento.png</attribute> - <!-- receiveCredentials --> - <attribute type="receive_credentials">FALSE</attribute> - <!-- showPermissions --> - <attribute type="show_permissions">FALSE</attribute> - <!-- displayMode --> - <attribute type="display_mode">embedded</attribute> - <!-- productIntentID --> - <attribute type="product_intent_id">pp_express</attribute> - - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\ApiWizard</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Sandbox Mode</label> - <config_path>paypal/wpp/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>API Uses Proxy</label> - <config_path>paypal/wpp/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>paypal/wpp/proxy_host</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="use_proxy">1</field> - </depends> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="110" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>paypal/wpp/proxy_port</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="use_proxy">1</field> - </depends> - </field> - </group> - <field id="enable_express_checkout" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/paypal_express/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="express_checkout_required_express_checkout"/> - </requires> - </field> - <field id="enable_in_context_checkout" translate="label comment" type="select" sortOrder="21" showInDefault="1" showInWebsite="1"> - <label>Enable In-Context Checkout Experience</label> - <comment> - <![CDATA[See PayPal Feature Support details and list of supported regions - <a href="https://developer.paypal.com/docs/classic/express-checkout/in-context/" target="_blank">here</a>.]]> - </comment> - <config_path>payment/paypal_express/in_context</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\InContextApi</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="merchant_id" translate="label" type="text" sortOrder="22" showInDefault="1" showInWebsite="1"> - <label>Merchant Account ID</label> - <tooltip>You can look up your merchant ID by logging into https://www.paypal.com/. Click the profile icon on the top right side of the page and then select Profile and settings in the Business Profile menu. (If you do not see the profile icon at the top of the page, click Profile, which appears in the top menu when the My Account tab is selected.) Click My business info on the left, and the Merchant account ID is displayed in the list of profile items on the right.</tooltip> - <config_path>payment/paypal_express/merchant_id</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\MerchantId</frontend_model> - <depends> - <field id="enable_in_context_checkout">1</field> - </depends> - <validate>required-entry</validate> - </field> - <field id="enable_express_checkout_bml" translate="label comment" type="select" sortOrder="23" showInDefault="0" showInWebsite="0"> - <label>Enable PayPal Credit</label> - <comment><![CDATA[PayPal Express Checkout lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/paypal_express_bml/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\BmlApi</frontend_model> - <requires> - <field id="enable_express_checkout"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" translate="label" type="text" sortOrder="25" showInDefault="0" showInWebsite="0" showInStore="0"> - <label>Sort Order PayPal Credit</label> - <config_path>payment/paypal_express_bml/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlApiSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - <group id="advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" translate="label comment tooltip" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Publisher ID</label> - <comment><![CDATA[Required to display a banner]]></comment> - <config_path>payment/paypal_express_bml/publisher_id</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="bml_wizard" translate="button_label" sortOrder="15" showInDefault="1" showInWebsite="1"> - <button_label>Get Publisher ID from PayPal</button_label> - <button_url><![CDATA[https://financing.paypal.com/ppfinportal/cart/index?dcp=4eff8563b9cc505e0b9afaff3256705081553c79]]></button_url> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\BmlApiWizard</frontend_model> - </field> - <group id="settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/homepage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_homepage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/homepage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsHP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="bml_homepage_position">0</field> - </depends> - </field> - <field id="bml_homepage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="settings_bml_categorypage" translate="label" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/categorypage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_categorypage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/categorypage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCCP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_categorypage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_categorypage_position">0</field></depends> - </field> - <field id="bml_categorypage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends><field id="bml_categorypage_position">1</field></depends> - </field> - </group> - <group id="settings_bml_productpage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/productpage_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_productpage_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/productpage_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCPP</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_productpage_position">0</field></depends> - </field> - <field id="bml_productpage_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends><field id="bml_productpage_position">1</field></depends> - </field> - </group> - <group id="settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Display</label> - <config_path>payment/paypal_express_bml/checkout_display</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_checkout_position" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="20"> - <label>Position</label> - <config_path>payment/paypal_express_bml/checkout_position</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlPosition::getBmlPositionsCheckout</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="bml_checkout_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_checkout_position">0</field></depends> - </field> - <field id="bml_checkout_size2" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends><field id="bml_checkout_position">1</field></depends> - </field> - </group> - </group> - </group> - <group id="settings_ec" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "PayPal" per store views.</comment> - <config_path>payment/paypal_express/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/paypal_express/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/paypal_express/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions\Express</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="visible_on_product" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Product Details Page</label> - <config_path>payment/paypal_express/visible_on_product</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="authorization_honor_period" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Authorization Honor Period (days)</label> - <comment>Specifies what the Authorization Honor Period is on the merchant’s PayPal account. It must mirror the setting in PayPal.</comment> - <config_path>payment/paypal_express/authorization_honor_period</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <field id="order_valid_period" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Order Valid Period (days)</label> - <comment>Specifies what the Order Valid Period is on the merchant’s PayPal account. It must mirror the setting in PayPal.</comment> - <config_path>payment/paypal_express/order_valid_period</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <field id="child_authorization_number" translate="label comment" type="text" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Number of Child Authorizations</label> - <comment>The default number of child authorizations in your PayPal account is 1. To do multiple authorizations please contact PayPal to request an increase.</comment> - <config_path>payment/paypal_express/child_authorization_number</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="payment_action">Order</field> - </depends> - </field> - <group id="settings_ec_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="80"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Shopping Cart</label> - <config_path>payment/paypal_express/visible_on_cart</config_path> - <comment>Also affects mini-shopping cart.</comment> - <source_model>Magento\Paypal\Model\System\Config\Source\Yesnoshortcut</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/paypal_express/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/paypal_express/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/paypal_express/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/paypal_express/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/paypal_express/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="transfer_shipping_options" translate="label tooltip comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Transfer Shipping Options</label> - <config_path>payment/paypal_express/transfer_shipping_options</config_path> - <tooltip>If this option is enabled, customer can change shipping address and shipping method on PayPal website. In live mode works via HTTPS protocol only.</tooltip> - <comment>Notice that PayPal can handle up to 10 shipping options. That is why Magento will transfer only first 10 cheapest shipping options if there are more than 10 available.</comment> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="line_items_enabled">1</field> - </depends> - </field> - <field id="button_flavor" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Shortcut Buttons Flavor</label> - <config_path>paypal/wpp/button_flavor</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutButtonFlavors</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="solution_type" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Enable PayPal Guest Checkout</label> - <comment>Ability for buyer to purchase without PayPal account.</comment> - <config_path>payment/paypal_express/solution_type</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutSolutionTypes</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="require_billing_address" translate="label comment" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Require Customer's Billing Address</label> - <comment>This feature needs be enabled first for the merchant account through PayPal technical support.</comment> - <config_path>payment/paypal_express/require_billing_address</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\RequireBillingAddress</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allow_ba_signup" translate="label comment tooltip" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Billing Agreement Signup</label> - <comment>Whether to create a billing agreement, if there are no active billing agreements available.</comment> - <tooltip> - <![CDATA[Merchants need to apply to PayPal for enabling billing agreements feature. Do not enable this option until PayPal confirms that billing agreements are enabled for your merchant account.]]> - </tooltip> - <config_path>payment/paypal_express/allow_ba_signup</config_path> - <source_model>Magento\Paypal\Model\Config::getExpressCheckoutBASignupOptions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="skip_order_review_step" translate="label" type="select" sortOrder="95" showInDefault="1" showInWebsite="1"> - <label>Skip Order Review Step</label> - <config_path>payment/paypal_express/skip_order_review_step</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="express_checkout_billing_agreement" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <label>PayPal Billing Agreement Settings</label> - <field id="active" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Enabled</label> - <comment> - <![CDATA[Will appear as a payment option only for customers who have at least one active billing agreement.]]> - </comment> - <config_path>payment/paypal_billing_agreement/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <config_path>payment/paypal_billing_agreement/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/paypal_billing_agreement/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/paypal_billing_agreement/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/paypal_billing_agreement/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/paypal_billing_agreement/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/paypal_billing_agreement/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="75" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/paypal_billing_agreement/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/paypal_billing_agreement/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allow_billing_agreement_wizard" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Allow in Billing Agreement Wizard</label> - <config_path>payment/paypal_billing_agreement/allow_billing_agreement_wizard</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - </group> - <group id="express_checkout_settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="110"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>SFTP Credentials</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_login" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Login</label> - <config_path>paypal/fetch_reports/ftp_login</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>paypal/fetch_reports/ftp_password</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_sandbox" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Sandbox Mode</label> - <config_path>paypal/fetch_reports/ftp_sandbox</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_ftp_ip" translate="label comment tooltip" type="text" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Custom Endpoint Hostname or IP-Address</label> - <comment>By default it is "reports.paypal.com".</comment> - <tooltip>Use colon to specify port. For example: "test.example.com:5224".</tooltip> - <config_path>paypal/fetch_reports/ftp_ip</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="settlement_reports_ftp_sandbox">0</field> - </depends> - </field> - <field id="settlement_reports_ftp_path" translate="label comeent" type="text" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Custom Path</label> - <comment>By default it is "/ppreports/outgoing".</comment> - <config_path>paypal/fetch_reports/ftp_path</config_path> - <attribute type="shared">1</attribute> - <depends> - <field id="settlement_reports_ftp_sandbox">0</field> - </depends> - </field> - <field id="heading_schedule" translate="label" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Scheduled Fetching</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_active" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Enable Automatic Fetching</label> - <config_path>paypal/fetch_reports/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_schedule" translate="label comment" type="select" sortOrder="90" showInDefault="1"> - <label>Schedule</label> - <comment>PayPal retains reports for 45 days.</comment> - <config_path>paypal/fetch_reports/schedule</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\FetchingSchedule</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="settlement_reports_time" translate="label" type="time" sortOrder="100" showInDefault="1"> - <label>Time of Day</label> - <config_path>paypal/fetch_reports/time</config_path> - <attribute type="shared">1</attribute> - </field> - </group> - <group id="express_checkout_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="120"> - <label>Frontend Experience Settings</label> - <field id="logo" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Product Logo</label> - <comment>Displays on catalog pages and homepage.</comment> - <config_path>paypal/style/logo</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\Logo</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_pages" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Merchant Pages Style</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <field id="page_style" translate="label tooltip" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Page Style</label> - <config_path>paypal/style/page_style</config_path> - <tooltip> - <![CDATA[Allowable values: "paypal", "primary" (default), your_custom_value (a custom payment page style from your merchant account profile).]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrimg" translate="label tooltip" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Image URL</label> - <config_path>paypal/style/paypal_hdrimg</config_path> - <tooltip> - <![CDATA[The image at the top left of the checkout page. Max size is 750x90-pixel. <strong style="color:red">https</strong> is highly encouraged.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrbackcolor" translate="label tooltip" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Background Color</label> - <config_path>paypal/style/paypal_hdrbackcolor</config_path> - <tooltip> - <![CDATA[The background color for the header of the checkout page. Case-insensitive six-character HTML hexadecimal color code in ASCII.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_hdrbordercolor" translate="label tooltip" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Header Border Color</label> - <config_path>paypal/style/paypal_hdrbordercolor</config_path> - <tooltip>2-pixel perimeter around the header space.</tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="paypal_payflowcolor" translate="label tooltip" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Page Background Color</label> - <config_path>paypal/style/paypal_payflowcolor</config_path> - <tooltip> - <![CDATA[The background color for the checkout page around the header and payment form.]]> - </tooltip> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_display" translate="label" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Customize Smart Buttons</label> - <frontend_model>Magento\Config\Block\System\Config\Form\Field\Heading</frontend_model> - <attribute type="shared">1</attribute> - </field> - <group id="checkout_page_button" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <label>Checkout Page</label> - <field id="checkout_page_button_customize" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="10"> - <label>Customize Button</label> - <config_path>paypal/style/checkout_page_button_customize</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_label" translate="label comment" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Label</label> - <comment><![CDATA[The installment feature is available only in these locales: en_MX, es_MX, en_BR, pt_BR.]]></comment> - <config_path>paypal/style/checkout_page_button_label</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\ButtonStylesLabel</frontend_model> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getLabel</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_mx_installment_period" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Mexico Installment Period</label> - <config_path>paypal/style/checkout_page_button_mx_installment_period</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getMxInstallmentPeriod</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - <field id="checkout_page_button_label">installment</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_br_installment_period" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Brazil Installment Period</label> - <config_path>paypal/style/checkout_page_button_br_installment_period</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getBrInstallmentPeriod</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - <field id="checkout_page_button_label">installment</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_layout" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Layout</label> - <config_path>paypal/style/checkout_page_button_layout</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getLayout</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - <field id="checkout_page_button_label" negative="1">credit</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_size" translate="label tooltip" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Size</label> - <config_path>paypal/style/checkout_page_button_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getSize</source_model> - <tooltip>Select Responsive to ensure the PayPal button renders correctly on mobile devices.</tooltip> - <depends> - <field id="checkout_page_button_customize">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_shape" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Shape</label> - <config_path>paypal/style/checkout_page_button_shape</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getShape</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="checkout_page_button_color" translate="label" type="select" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Color</label> - <config_path>paypal/style/checkout_page_button_color</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\ButtonStyles::getColor</source_model> - <depends> - <field id="checkout_page_button_customize">1</field> - <field id="checkout_page_button_label" negative="1">credit</field> - </depends> - <attribute type="shared">1</attribute> - </field> - </group> - <group id="product_page_button" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <label>Product Pages</label> - <field id="product_page_button_customize" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_customize"> - <config_path>paypal/style/product_page_button_customize</config_path> - </field> - <field id="product_page_button_label" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_label"> - <config_path>paypal/style/product_page_button_label</config_path> - <depends> - <field id="product_page_button_customize">1</field> - </depends> - </field> - <field id="product_page_button_mx_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_mx_installment_period"> - <config_path>paypal/style/product_page_button_mx_installment_period</config_path> - <depends> - <field id="product_page_button_customize">1</field> - <field id="product_page_button_label">installment</field> - </depends> - </field> - <field id="product_page_button_br_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_br_installment_period"> - <config_path>paypal/style/product_page_button_br_installment_period</config_path> - <depends> - <field id="product_page_button_customize">1</field> - <field id="product_page_button_label">installment</field> - </depends> - </field> - <field id="product_page_button_layout" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_layout"> - <config_path>paypal/style/product_page_button_layout</config_path> - <depends> - <field id="product_page_button_customize">1</field> - <field id="product_page_button_label" negative="1">credit</field> - </depends> - </field> - <field id="product_page_button_size" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_size"> - <config_path>paypal/style/product_page_button_size</config_path> - <depends> - <field id="product_page_button_customize">1</field> - </depends> - </field> - <field id="product_page_button_shape" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_shape"> - <config_path>paypal/style/product_page_button_shape</config_path> - <depends> - <field id="product_page_button_customize">1</field> - </depends> - </field> - <field id="product_page_button_color" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_color"> - <config_path>paypal/style/product_page_button_color</config_path> - <depends> - <field id="product_page_button_customize">1</field> - <field id="product_page_button_label" negative="1">credit</field> - </depends> - </field> - </group> - <group id="cart_page_button" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="110"> - <label>Cart Page</label> - <field id="cart_page_button_customize" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_customize"> - <config_path>paypal/style/cart_page_button_customize</config_path> - </field> - <field id="cart_page_button_label" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_label"> - <config_path>paypal/style/cart_page_button_label</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - </depends> - </field> - <field id="cart_page_button_mx_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_mx_installment_period"> - <config_path>paypal/style/cart_page_button_mx_installment_period</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - <field id="cart_page_button_label">installment</field> - </depends> - </field> - <field id="cart_page_button_br_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_br_installment_period"> - <config_path>paypal/style/cart_page_button_br_installment_period</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - <field id="cart_page_button_label">installment</field> - </depends> - </field> - <field id="cart_page_button_layout" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_layout"> - <config_path>paypal/style/cart_page_button_layout</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - <field id="cart_page_button_label" negative="1">credit</field> - </depends> - </field> - <field id="cart_page_button_size" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_size"> - <config_path>paypal/style/cart_page_button_size</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - </depends> - </field> - <field id="cart_page_button_shape" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_shape"> - <config_path>paypal/style/cart_page_button_shape</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - </depends> - </field> - <field id="cart_page_button_color" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_color"> - <config_path>paypal/style/cart_page_button_color</config_path> - <depends> - <field id="cart_page_button_customize">1</field> - <field id="cart_page_button_label" negative="1">credit</field> - </depends> - </field> - </group> - <group id="mini_cart_page_button" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="120"> - <label>Mini Cart</label> - <field id="mini_cart_page_button_customize" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_customize"> - <config_path>paypal/style/mini_cart_page_button_customize</config_path> - </field> - <field id="mini_cart_page_button_label" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_label"> - <config_path>paypal/style/mini_cart_page_button_label</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - </depends> - </field> - <field id="mini_cart_page_button_mx_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_mx_installment_period"> - <config_path>paypal/style/mini_cart_page_button_mx_installment_period</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - <field id="mini_cart_page_button_label">installment</field> - </depends> - </field> - <field id="mini_cart_page_button_br_installment_period" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_br_installment_period"> - <config_path>paypal/style/mini_cart_page_button_br_installment_period</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - <field id="mini_cart_page_button_label">installment</field> - </depends> - </field> - <field id="mini_cart_page_button_layout" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_layout"> - <config_path>paypal/style/mini_cart_page_button_layout</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - <field id="mini_cart_page_button_label" negative="1">credit</field> - </depends> - </field> - <field id="mini_cart_page_button_size" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_size"> - <config_path>paypal/style/mini_cart_page_button_size</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - </depends> - </field> - <field id="mini_cart_page_button_shape" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_shape"> - <config_path>paypal/style/mini_cart_page_button_shape</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - </depends> - </field> - <field id="mini_cart_page_button_color" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/checkout_page_button/checkout_page_button_color"> - <config_path>paypal/style/mini_cart_page_button_color</config_path> - <depends> - <field id="mini_cart_page_button_customize">1</field> - <field id="mini_cart_page_button_label" negative="1">credit</field> - </depends> - </field> - </group> - <group id="features" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="130"> - <label>Features</label> - <field id="disable_funding_options" translate="label comment" type="multiselect" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Disable Funding Options</label> - <comment> - <![CDATA[PayPal will automatically display each enabled funding option to eligible buyers. - For example, PayPal Credit is only shown to buyers in countries where PayPal Credit is - offered and the currency offered by the merchant is USD.]]> - </comment> - <config_path>paypal/style/disable_funding_options</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\MultiSelect\DisabledFundingOptions</frontend_model> - <source_model>Magento\Paypal\Model\System\Config\Source\DisableFundingOptions</source_model> - <attribute type="shared">1</attribute> - <can_be_empty>1</can_be_empty> - </field> - </group> - </group> - </group> - </group> - </group> - <group id="payments_pro_hosted_solution" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Website Payments Pro Hosted Solution</label> - <fieldset_css>paypal-other-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <attribute type="activity_path">payment/hosted_pro/active</attribute> - <comment><![CDATA[Accept payments with a PCI compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="paypal_ec_separate">1</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="pphs_required_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="pphs_required_settings_pphs" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payments Pro Hosted Solution</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account"/> - <field id="api_authentication" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_authentication"/> - <field id="api_username" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_username" /> - <field id="api_password" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_password" /> - <field id="api_signature" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_signature" /> - <field id="api_cert" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_cert" /> - <field id="api_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/api_wizard" /> - <field id="sandbox_flag" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/sandbox_flag" /> - <field id="use_proxy" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/use_proxy" /> - <field id="proxy_host" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/proxy_host" /> - <field id="proxy_port" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/proxy_port" /> - </group> - <field id="pphs_enable" type="select" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/hosted_pro/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="pphs_required_settings_pphs"/> - </requires> - <frontend_class>paypal-enabler paypal-ec-separate</frontend_class> - </field> - - <field id="enable_express_checkout_bml" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" sortOrder="21" showInDefault="1" showInWebsite="1"> - <comment><![CDATA[Payments Pro Hosted Solution lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <requires> - <field id="pphs_enable"/> - </requires> - </field> - <group id="pphs_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="22"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="pphs_settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="pphs_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="pphs_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_homepage_position">0</field> - </depends> - </field> - <field id="pphs_bml_homepage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_homepage/pphs_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="pphs_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_categorypage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="pphs_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="pphs_bml_categorypage_size1" translate="label" showInDefault="1" showInWebsite="1" sortOrder="30" type="select"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_categorypage_position">0</field> - </depends> - </field> - <field id="pphs_bml_categorypage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_categorypage/pphs_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="pphs_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_productpage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="pphs_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="pphs_bml_productpage_size1" translate="label" type="select" showInWebsite="1" showInDefault="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_productpage_position">0</field> - </depends> - </field> - <field id="pphs_bml_productpage_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_productpage/pphs_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="pphs_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="pphs_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="pphs_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="pphs_bml_checkout_size1" translate="label" type="select" showInWebsite="1" showInDefault="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_checkout_position">0</field> - </depends> - </field> - <field id="pphs_bml_checkout_size2" extends="payment_all_paypal/payments_pro_hosted_solution/pphs_required_settings/pphs_advertise_bml/pphs_settings_bml_checkout/pphs_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="pphs_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="pphs_settings" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payments Pro Hosted Solution</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" type="text" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="10" translate="label comment"> - <label>Title</label> - <comment>It is recommended to set this value to "PayPal" per store views.</comment> - <config_path>payment/hosted_pro/title</config_path> - </field> - <field id="sort_order" type="text" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20" translate="label"> - <label>Sort Order</label> - <config_path>payment/hosted_pro/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - </field> - <field id="payment_action" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Payment Action</label> - <config_path>payment/hosted_pro/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - </field> - <field id="display_ec" type="select" showInDefault="1" showInWebsite="1" sortOrder="40" translate="label"> - <label>Display Express Checkout in the Payment Information step</label> - <config_path>payment/hosted_pro/display_ec</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <group id="pphs_settings_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" type="select" showInDefault="1" showInWebsite="1" sortOrder="10" translate="label"> - <label>Payment Applicable From</label> - <config_path>payment/hosted_pro/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - </field> - <field id="specificcountry" type="multiselect" showInDefault="1" showInWebsite="1" sortOrder="20" translate="label"> - <label>Countries Payment Applicable From</label> - <config_path>payment/hosted_pro/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - </field> - <field id="debug" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Debug Mode</label> - <config_path>payment/hosted_pro/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <field id="verify_peer" type="select" showInDefault="1" showInWebsite="1" sortOrder="35" translate="label"> - <label>Enable SSL verification</label> - <config_path>payment/hosted_pro/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - </field> - <group id="pphs_settlement_report" showInDefault="1" showInWebsite="1" sortOrder="50" translate="label"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login" /> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password" /> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox" /> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip" /> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path" /> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule" /> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active" /> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule" /> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time" /> - </group> - </group> - </group> - </group> - <group id="payments_pro_hosted_solution_without_bml" extends="payments_pro_hosted_solution"> - <group id="pphs_required_settings"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="pphs_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </section> - <section id="payment_us" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="payflow_advanced" translate="label comment" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Payments Advanced</label> - <fieldset_css>paypal-other-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment><![CDATA[Accept payments with a PCI-compliant checkout that keeps customers on your site. (<u>Includes Express Checkout</u>)]]></comment> - <attribute type="activity_path">payment/payflow_advanced/active</attribute> - <group id="configuration_details" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="4"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-advanced.html</comment> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint</frontend_model> - </group> - <group id="required_settings" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <group id="payments_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Payments Advanced and Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account"> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - </field> - <field id="partner" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Partner</label> - <config_path>payment/payflow_advanced/partner</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="vendor" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Vendor</label> - <config_path>payment/payflow_advanced/vendor</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="user" translate="label comment tooltip" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>User</label> - <comment>PayPal recommends that you set up an additional User on your account at manager.paypal.com</comment> - <tooltip>PayPal recommends you set up an additional User on your account at manager.paypal.com, instead of entering your admin username and password here. This will enhance your security and prevent service interruptions if you later change your password. If you do not want to set up an additional User, you can re-enter your Merchant Login here.</tooltip> - <config_path>payment/payflow_advanced/user</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="pwd" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Password</label> - <config_path>payment/payflow_advanced/pwd</config_path> - <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> - <attribute type="shared">1</attribute> - </field> - <field id="sandbox_flag" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Test Mode</label> - <config_path>payment/payflow_advanced/sandbox_flag</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="use_proxy" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>Use Proxy</label> - <config_path>payment/payflow_advanced/use_proxy</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_host" translate="label" type="text" sortOrder="80" showInDefault="1" showInWebsite="1"> - <label>Proxy Host</label> - <config_path>payment/payflow_advanced/proxy_host</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="proxy_port" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1"> - <label>Proxy Port</label> - <config_path>payment/payflow_advanced/proxy_port</config_path> - <depends> - <field id="use_proxy">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="payflow_advanced_info" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Payflowlink\Advanced</frontend_model> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_payflow_advanced" translate="label comment" type="select" sortOrder="41" showInDefault="1" showInWebsite="1"> - <label>Enable this Solution</label> - <config_path>payment/payflow_advanced/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - <requires> - <group id="payments_advanced"/> - </requires> - </field> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Hidden</frontend_model> - <requires> - <field id="enable_payflow_advanced"/> - </requires> - </field> - <field id="enable_express_checkout_bml" sortOrder="42" extends="payment_all_paypal/express_checkout/express_checkout_required/enable_express_checkout_bml" showInDefault="1" showInWebsite="1"> - <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_payflow_advanced"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="50" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order" showInDefault="1" showInWebsite="1"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml">1</field> - </depends> - </field> - <group id="advanced_advertise_bml" showInDefault="1" showInWebsite="1" sortOrder="60" translate="label comment"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="advanced_settings_bml_homepage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20" translate="label"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="advanced_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="advanced_bml_homepage_size1" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <depends> - <field id="advanced_bml_homepage_position">0</field> - </depends> - </field> - <field id="advanced_bml_homepage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_homepage/advanced_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="advanced_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="advanced_settings_bml_categorypage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30" translate="label"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="advanced_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position"/> - <field id="advanced_bml_categorypage_size1" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_categorypage_position">0</field> - </depends> - </field> - <field id="advanced_bml_categorypage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_categorypage/advanced_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="advanced_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="advanced_settings_bml_productpage" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40" translate="label"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="advanced_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="advanced_bml_productpage_size1" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_productpage_position">0</field> - </depends> - </field> - <field id="advanced_bml_productpage_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_productpage/advanced_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="advanced_bml_productpage_position">1</field> - </depends> - </field> - - </group> - <group id="advanced_settings_bml_checkout" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50" translate="label"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display"/> - <field id="advanced_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position"/> - <field id="advanced_bml_checkout_size1" type="select" showInDefault="1" showInWebsite="1" sortOrder="30" translate="label"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_checkout_position">0</field> - </depends> - </field> - <field id="advanced_bml_checkout_size2" extends="payment_us/paypal_group_all_in_one/payflow_advanced/required_settings/advanced_advertise_bml/advanced_settings_bml_checkout/advanced_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="advanced_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_payments_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Basic Settings - PayPal Payments Advanced</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <comment>It is recommended to set this value to "Debit or Credit Card" per store views.</comment> - <config_path>payment/payflow_advanced/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_advanced/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_advanced/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settings_payments_advanced_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="allowspecific" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_advanced/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="25" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_advanced/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_advanced/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_advanced/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_editable" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>CVV Entry is Editable</label> - <config_path>payment/payflow_advanced/csc_editable</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="csc_required" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1"> - <label>Require CVV Entry</label> - <config_path>payment/payflow_advanced/csc_required</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <depends> - <field id="csc_editable">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="email_confirmation" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1"> - <label>Send Email Confirmation</label> - <config_path>payment/payflow_advanced/email_confirmation</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="url_method" translate="label" type="select" sortOrder="70" showInDefault="1" showInWebsite="1"> - <label>URL method for Cancel URL and Return URL</label> - <config_path>payment/payflow_advanced/url_method</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\UrlMethod</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="settlement_report" translate="label" showInDefault="1" showInWebsite="1" sortOrder="80"> - <label>Settlement Report Settings</label> - <field id="heading_sftp" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_sftp"/> - <field id="settlement_reports_ftp_login" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_login"/> - <field id="settlement_reports_ftp_password" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_password"/> - <field id="settlement_reports_ftp_sandbox" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_sandbox"/> - <field id="settlement_reports_ftp_ip" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_ip"/> - <field id="settlement_reports_ftp_path" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_ftp_path"/> - <field id="heading_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/heading_schedule"/> - <field id="settlement_reports_active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_active"/> - <field id="settlement_reports_schedule" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_schedule"/> - <field id="settlement_reports_time" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_settlement_report/settlement_reports_time"/> - </group> - <group id="frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="90"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="settings_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/title" /> - <field id="sort_order" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/sort_order" /> - <field id="payment_action" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/payment_action" /> - <field id="visible_on_product" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/visible_on_product" /> - <group id="settings_express_checkout_advanced" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_express_checkout/paypal_payflow_express_checkout_advanced"/> - </group> - </group> - <group id="wpp_usuk" translate="label" sortOrder="40" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout"> - <label>Payments Pro</label> - <attribute type="activity_path">payment/paypal_payment_pro/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <field id="enable_paypal_payflow"> - <attribute type="shared">0</attribute> - <config_path>payment/paypal_payment_pro/active</config_path> - </field> - <group id="paypal_payflow_api_settings" translate="label"> - <label>Payments Pro and Express Checkout</label> - </group> - </group> - <group id="settings_paypal_payflow" translate="label"> - <label>Basic Settings - PayPal Payments Pro</label> - </group> - </group> - <group id="wps_express" extends="payment_all_paypal/express_checkout"> - <label>Payments Standard</label> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <group id="express_checkout_required_express_checkout"> - <label>Payments Standard</label> - </group> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_express_checkout_bml"> - <config_path>payment/wps_express_bml/active</config_path> - </field> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_payment_gateways" translate="label" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="paypal_payflowpro_with_express_checkout" translate="label comment" extends="payment_all_paypal/paypal_payflowpro"> - <label>Payflow Pro</label> - <attribute type="paypal_ec_separate">0</attribute> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <label>Required PayPal Settings</label> - <field id="enable_paypal_payflow"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - </field> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <group id="paypal_payflow_api_settings" translate="label"> - <label>Payflow Pro and Express Checkout</label> - <field id="business_account" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_required_express_checkout/business_account" translate="label" sortOrder="10"> - <frontend_class>not-required</frontend_class> - <label>Email Associated with PayPal Merchant Account (Optional)</label> - <attribute type="shared">1</attribute> - </field> - </group> - <field id="enable_express_checkout" extends="payment_all_paypal/payflow_link/payflow_link_required/enable_express_checkout_basic" showInDefault="1" showInWebsite="1"> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Hidden</frontend_model> - <requires> - <field id="enable_paypal_payflow"/> - </requires> - </field> - <field id="enable_express_checkout_bml_payflow" translate="label" type="select" sortOrder="21" showInWebsite="1" showInDefault="1"> - <label>Enable PayPal Credit</label> - <comment><![CDATA[PayPal Express Checkout Payflow Edition lets you give customers access to financing through PayPal Credit® - at no additional cost to you. - You get paid up front, even though customers have more time to pay. A pre-integrated payment button lets customers pay quickly with PayPal Credit®. - <a href="https://www.paypal.com/webapps/mpp/promotional-financing" target="_blank">Learn More</a>]]> - </comment> - <config_path>payment/payflow_express_bml/active</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Bml</frontend_model> - <requires> - <field id="enable_paypal_payflow"/> - </requires> - </field> - <field id="express_checkout_bml_sort_order" sortOrder="30" extends="payment_all_paypal/express_checkout/express_checkout_required/express_checkout_bml_sort_order" showInDefault="1" showInWebsite="1"> - <config_path>payment/payflow_express_bml/sort_order</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Depends\BmlSortOrder</frontend_model> - <depends> - <field id="enable_express_checkout_bml_payflow">1</field> - </depends> - </field> - <group id="paypal_payflow_advertise_bml" translate="label comment" showInDefault="1" showInWebsite="1" sortOrder="40"> - <label>Advertise PayPal Credit</label> - <comment> - <![CDATA[<a href="https://financing.paypal.com/ppfinportal/content/whyUseFinancing" target="_blank">Why Advertise Financing?</a><br/> - <strong>Give your sales a boost when you advertise financing.</strong><br/>PayPal helps turn browsers into buyers with financing - from PayPal Credit®. Your customers have more time to pay, while you get paid up front – at no additional cost to you. - Use PayPal’s free banner ads that let you advertise PayPal Credit® financing as a payment option when your customers check out with PayPal. - The PayPal Advertising Program has been shown to generate additional purchases as well as increase consumer's average purchase sizes by 15% - or more. <a href="https://financing.paypal.com/ppfinportal/content/forrester" target="_blank">See Details</a>.]]> - </comment> - <field id="bml_publisher_id" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_publisher_id" /> - <field id="bml_wizard" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/bml_wizard" /> - <group id="paypal_payflow_settings_bml_homepage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="20"> - <label>Home Page</label> - <field id="bml_homepage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_display"/> - <field id="paypal_payflow_bml_homepage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_homepage/bml_homepage_position"/> - <field id="paypal_payflow_bml_homepage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/homepage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPH</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_homepage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_homepage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_homepage/paypal_payflow_bml_homepage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeHPS</source_model> - <depends> - <field id="paypal_payflow_bml_homepage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_categorypage" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Catalog Category Page</label> - <field id="bml_categorypage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_display"/> - <field id="paypal_payflow_bml_categorypage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_categorypage/bml_categorypage_position" /> - <field id="paypal_payflow_bml_categorypage_size1" translate="label" sortOrder="30" showInWebsite="1" showInDefault="1" type="select"> - <label>Size</label> - <config_path>payment/paypal_express_bml/categorypage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_categorypage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_categorypage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_categorypage/paypal_payflow_bml_categorypage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCCPS</source_model> - <depends> - <field id="paypal_payflow_bml_categorypage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_productpage" translate="label" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="40"> - <label>Catalog Product Page</label> - <field id="bml_productpage_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_display" /> - <field id="paypal_payflow_bml_productpage_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_productpage/bml_productpage_position" /> - <field id="paypal_payflow_bml_productpage_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/productpage_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_productpage_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_productpage_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_productpage/paypal_payflow_bml_productpage_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCPPN</source_model> - <depends> - <field id="paypal_payflow_bml_productpage_position">1</field> - </depends> - </field> - </group> - <group id="paypal_payflow_settings_bml_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="50"> - <label>Checkout Cart Page</label> - <field id="bml_checkout_display" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_display" /> - <field id="paypal_payflow_bml_checkout_position" translate="label" extends="payment_all_paypal/express_checkout/express_checkout_required/advertise_bml/settings_bml_checkout/bml_checkout_position" /> - <field id="paypal_payflow_bml_checkout_size1" translate="label" type="select" showInDefault="1" showInWebsite="1" sortOrder="30"> - <label>Size</label> - <config_path>payment/paypal_express_bml/checkout_size</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutC</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_checkout_position">0</field> - </depends> - </field> - <field id="paypal_payflow_bml_checkout_size2" extends="payment_us/paypal_payment_gateways/paypal_payflowpro_with_express_checkout/paypal_payflow_required/paypal_payflow_advertise_bml/paypal_payflow_settings_bml_checkout/paypal_payflow_bml_checkout_size1"> - <source_model>Magento\Paypal\Model\System\Config\Source\BmlSize::getBmlSizeCheckoutN</source_model> - <attribute type="shared">1</attribute> - <depends> - <field id="paypal_payflow_bml_checkout_position">1</field> - </depends> - </field> - </group> - </group> - </group> - <group id="settings_paypal_payflow" translate="label"> - <group id="settings_paypal_payflow_advanced" translate="label"> - <group id="paypal_payflow_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo"/> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages"/> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style"/> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg"/> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor"/> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor"/> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor"/> - </group> - </group> - </group> - <group id="paypal_payflow_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Title</label> - <config_path>payment/payflow_express/title</config_path> - <attribute type="shared">1</attribute> - </field> - <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Sort Order</label> - <config_path>payment/payflow_express/sort_order</config_path> - <frontend_class>validate-number</frontend_class> - <attribute type="shared">1</attribute> - </field> - <field id="payment_action" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Payment Action</label> - <config_path>payment/payflow_express/payment_action</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\PaymentActions</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="visible_on_product" translate="label" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Product Details Page</label> - <config_path>payment/payflow_express/visible_on_product</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <group id="paypal_payflow_express_checkout_advanced" translate="label" showInDefault="1" showInWebsite="1" sortOrder="60"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Display on Shopping Cart</label> - <comment>Also affects mini-shopping cart.</comment> - <config_path>payment/payflow_express/visible_on_cart</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\Yesnoshortcut</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="allowspecific" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1"> - <label>Payment Applicable From</label> - <config_path>payment/payflow_express/allowspecific</config_path> - <source_model>Magento\Payment\Model\Config\Source\Allspecificcountries</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="specificcountry" translate="label" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="1"> - <label>Countries Payment Applicable From</label> - <config_path>payment/payflow_express/specificcountry</config_path> - <source_model>Magento\Paypal\Model\System\Config\Source\BuyerCountry</source_model> - <depends> - <field id="allowspecific">1</field> - </depends> - <attribute type="shared">1</attribute> - </field> - <field id="debug" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1"> - <label>Debug Mode</label> - <config_path>payment/payflow_express/debug</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="verify_peer" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1"> - <label>Enable SSL verification</label> - <config_path>payment/payflow_express/verify_peer</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="line_items_enabled" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1"> - <label>Transfer Cart Line Items</label> - <config_path>payment/payflow_express/line_items_enabled</config_path> - <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <attribute type="shared">1</attribute> - </field> - <field id="skip_order_review_step" sortOrder="50" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/skip_order_review_step"/> - </group> - </group> - </group> - <group id="payflow_link_us" extends="payment_all_paypal/payflow_link"/> - </group> - <group id="paypal_alternative_payment_methods" sortOrder="5" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_us" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="shared">0</attribute> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <attribute type="activity_path">payment/payflow_express/active</attribute> - <attribute type="displayIn">recommended_solutions</attribute> - </group> - </group> - </section> - <section id="payment_gb" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions  <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="wpp_usuk" translate="comment" sortOrder="20"> - <fieldset_css>pp-general-uk</fieldset_css> - <demo_link>http://www.youtube.com/watch?v=LBe-TW87eGI&list=PLF18B1094ABCD7CE8&index=1&feature=plpp_video</demo_link> - <comment>Accept payments with a completely customizable checkout page.</comment> - <group id="wpp_required_settings"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="wpp_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - <group id="payments_pro_hosted_solution_with_express_checkout" translate="label comment" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="30"> - <label>Website Payments Pro Hosted Solution</label> - <attribute type="paypal_ec_separate">0</attribute> - <group id="pphs_required_settings"> - <group id="pphs_required_settings_pphs" translate="label"> - <label>Website Payments Pro Hosted Solution and Express Checkout</label> - </group> - <field id="pphs_enable"> - <requires> - <group id="pphs_required_settings_pphs"/> - </requires> - <frontend_class>paypal-enabler</frontend_class> - </field> - </group> - <group id="pphs_settings" translate="label"> - <label>Basic Settings - PayPal Website Payments Pro Hosted Solution</label> - <group id="pphs_settings_advanced"> - <group id="pphs_billing_agreement" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="40"> - <label>PayPal Billing Agreement Settings</label> - <field id="active" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/active" /> - <field id="title" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/title" /> - <field id="sort_order" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/sort_order" /> - <field id="payment_action" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/payment_action" /> - <field id="allowspecific" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/allowspecific" /> - <field id="specificcountry" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/specificcountry" /> - <field id="debug" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/debug" /> - <field id="verify_peer" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/verify_peer" /> - <field id="line_items_enabled" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/line_items_enabled" /> - <field id="allow_billing_agreement_wizard" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_billing_agreement/allow_billing_agreement_wizard" /> - </group> - <group id="pphs_frontend" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="60"> - <label>Frontend Experience Settings</label> - <field id="logo" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/logo" /> - <field id="paypal_pages" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_pages" /> - <field id="page_style" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/page_style" /> - <field id="paypal_hdrimg" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrimg" /> - <field id="paypal_hdrbackcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbackcolor" /> - <field id="paypal_hdrbordercolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_hdrbordercolor" /> - <field id="paypal_payflowcolor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/express_checkout_frontend/paypal_payflowcolor" /> - </group> - </group> - </group> - <group id="pphs_settings_express_checkout" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="30"> - <label>Basic Settings - PayPal Express Checkout</label> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <field id="title" extends="payment_all_paypal/express_checkout/settings_ec/title" /> - <field id="sort_order" extends="payment_all_paypal/express_checkout/settings_ec/sort_order" /> - <field id="payment_action" extends="payment_all_paypal/express_checkout/settings_ec/payment_action" /> - <field id="visible_on_product" extends="payment_all_paypal/express_checkout/settings_ec/visible_on_product" /> - <field id="authorization_honor_period" extends="payment_all_paypal/express_checkout/settings_ec/authorization_honor_period" /> - <field id="order_valid_period" extends="payment_all_paypal/express_checkout/settings_ec/order_valid_period" /> - <field id="child_authorization_number" extends="payment_all_paypal/express_checkout/settings_ec/child_authorization_number" /> - <group id="pphs_settings_express_checkout_advanced" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="80"> - <label>Advanced Settings</label> - <fieldset_css>config-advanced</fieldset_css> - <field id="visible_on_cart" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/visible_on_cart" /> - <field id="allowspecific" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/allowspecific" /> - <field id="specificcountry" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/specificcountry" /> - <field id="debug" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/debug" /> - <field id="verify_peer" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/verify_peer" /> - <field id="line_items_enabled" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/line_items_enabled" /> - <field id="transfer_shipping_options" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/transfer_shipping_options" /> - <field id="button_flavor" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/button_flavor" /> - <field id="solution_type" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/solution_type" /> - <field id="require_billing_address" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/require_billing_address" /> - <field id="allow_ba_signup" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/allow_ba_signup" /> - <field id="skip_order_review_step" extends="payment_all_paypal/express_checkout/settings_ec/settings_ec_advanced/skip_order_review_step" /> - </group> - </group> - </group> - <group id="wps_express" extends="payment_all_paypal/express_checkout" sortOrder="50"> - <label>Website Payments Standard</label> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> - <label>Website Payments Standard</label> - </group> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_alternative_payment_methods" sortOrder="5" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_gb" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </section> - <section id="payment_de" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="paypal_payment_solutions" showInDefault="0" showInWebsite="0" showInStore="0" sortOrder="5"> - <group id="express_checkout_de" translate="label comment" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <group id="settings_ec_advanced"> - <field id="solution_type" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </group> - </section> - <section id="payment_other" extends="payment" showInDefault="0" showInWebsite="0" showInStore="0"> - <group id="express_checkout_other" translate="label comment" sortOrder="5" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>PayPal Express Checkout</label> - <fieldset_css>complex paypal-express-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Add another payment method to your existing solution or as a stand-alone option.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">recommended_solutions</attribute> - <group id="express_checkout_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - <group id="paypal_group_all_in_one" translate="label comment" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> - <label><![CDATA[PayPal All-in-One Payment Solutions  <i>Accept and process credit cards and PayPal payments.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-all-in-one-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Choose a secure bundled payment solution for your business.</comment> - <help_url>https://www.paypal-marketing.com/emarketing/partner/na/merchantlineup/home.page#mainTab=checkoutlineup&subTab=newlineup</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - <group id="wps_other" extends="payment_all_paypal/express_checkout" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Website Payments Standard</label> - <fieldset_css>complex</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment</frontend_model> - <comment>Accept credit card and PayPal payments securely.</comment> - <attribute type="activity_path">payment/wps_express/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-standard.html</comment> - </group> - <group id="express_checkout_required"> - <group id="express_checkout_required_express_checkout"> - <label>Website Payments Standard</label> - </group> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="enable_express_checkout"> - <config_path>payment/wps_express/active</config_path> - </field> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - <group id="settings_ec"> - <label>Basic Settings - PayPal Website Payments Standard</label> - </group> - </group> - </group> - <group id="paypal_payment_gateways" translate="label comment" sortOrder="8" showInDefault="0" showInWebsite="0" showInStore="0"> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <comment>Process payments using your own internet merchant account.</comment> - <help_url>https://merchant.paypal.com/cgi-bin/marketingweb?cmd=_render-content</help_url> - <attribute type="displayIn">other_paypal_payment_solutions</attribute> - </group> - </section> - <section id="payment_ca" extends="payment_other"> - <group id="express_checkout_other"> - <attribute type="activity_path">payment/paypal_express/active</attribute> - <attribute type="activity_path">payment/payflow_express/active</attribute> - </group> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="20"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <fieldset_css>complex paypal-other-section paypal-gateways-section</fieldset_css> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> - <label><![CDATA[PayPal Payment Gateways <i>Process payments using your own internet merchant account.</i>]]></label> - <group id="wpp_ca" translate="label" extends="payment_all_paypal/paypal_payflowpro" sortOrder="30"> - <label>Website Payments Pro</label> - <attribute type="activity_path">payment/paypal_payment_pro/active</attribute> - <group id="configuration_details"> - <comment>http://docs.magento.com/m2/ce/user_guide/payment/paypal-payments-pro.html</comment> - </group> - <group id="paypal_payflow_required" translate="label" showInDefault="1" showInWebsite="1" sortOrder="10"> - <group id="paypal_payflow_api_settings"> - <label>Payments Pro</label> - </group> - <field id="enable_in_context_checkout" showInDefault="0" showInWebsite="0"/> - <field id="merchant_id" showInDefault="0" showInWebsite="0"/> - <field id="enable_paypal_payflow"> - <frontend_class>paypal-enabler paypal-ec-pe</frontend_class> - <attribute type="shared">0</attribute> - <config_path>payment/paypal_payment_pro/active</config_path> - <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Enable\Payment</frontend_model> - </field> - </group> - <group id="settings_paypal_payflow"> - <label>Basic Settings - PayPal Payments Pro</label> - </group> - </group> - <group id="paypal_payflowpro_ca" extends="payment_all_paypal/paypal_payflowpro" sortOrder="40"/> - <group id="payflow_link_ca" extends="payment_all_paypal/payflow_link" sortOrder="50"> - <group id="payflow_link_required"> - <field id="enable_express_checkout_bml" showInDefault="0" showInWebsite="0"/> - <field id="express_checkout_bml_sort_order" showInDefault="0" showInWebsite="0"/> - <group id="payflow_link_advertise_bml" showInDefault="0" showInWebsite="0"/> - </group> - </group> - </group> - </section> - <section id="payment_au" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_au" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="paypal_payflowpro_au" extends="payment_all_paypal/paypal_payflowpro" sortOrder="20"/> - </group> - </section> - <section id="payment_jp" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_jp" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Website Payments Plus</label> - </group> - </group> - </section> - <section id="payment_fr" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_fr" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Integral Evolution</label> - </group> - </group> - </section> - <section id="payment_it" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_it" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Pro</label> - </group> - </group> - </section> - <section id="payment_es" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_es" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"> - <label>Pasarela integral</label> - </group> - </group> - </section> - <section id="payment_hk" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other" sortOrder="12"/> - <group id="payments_pro_hosted_solution_hk" extends="payment_all_paypal/payments_pro_hosted_solution_without_bml" sortOrder="10"/> - </group> - </section> - <section id="payment_nz" extends="payment_other"> - <group id="express_checkout_other"/> - <group id="paypal_group_all_in_one"> - <group id="wps_other"/> - </group> - <group id="paypal_payment_gateways" showInDefault="1" showInWebsite="1" showInStore="1"> - <group id="paypal_payflowpro_nz" extends="payment_all_paypal/paypal_payflowpro"/> - </group> - </section> - </system> -</config> diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php index 49acd039a0960..3f477f7ce5033 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php @@ -301,8 +301,8 @@ public function validateDescriptionFormatStructure( $this->validateShortDescriptionFormat( $phpcsFile, (int) $shortPtr, - $commentStartPtr, - $commentEndPtr, + (int)$commentStartPtr, + (int)$commentEndPtr, $emptyTypeTokens ); } diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php index 01834ff81e81b..c37f0b500fe39 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php @@ -97,8 +97,8 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0); - $this->validateAnnotationBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr); - $commentStartPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); + $this->validateAnnotationBlockExists($phpcsFile, (int)$previousCommentClosePtr, (int)$stackPtr); + $commentStartPtr = (int)$phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); $commentCloserPtr = $tokens[$commentStartPtr]['comment_closer']; $emptyTypeTokens = [ T_DOC_COMMENT_WHITESPACE, @@ -111,7 +111,7 @@ public function process(File $phpcsFile, $stackPtr) } else { $this->annotationFormatValidator->validateDescriptionFormatStructure( $phpcsFile, - $commentStartPtr, + (int)$commentStartPtr, (int) $shortPtr, $previousCommentClosePtr, $emptyTypeTokens diff --git a/lib/internal/Magento/Framework/Crontab/CrontabManager.php b/lib/internal/Magento/Framework/Crontab/CrontabManager.php index 6049b7fba6d44..da81540faf477 100644 --- a/lib/internal/Magento/Framework/Crontab/CrontabManager.php +++ b/lib/internal/Magento/Framework/Crontab/CrontabManager.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ + namespace Magento\Framework\Crontab; use Magento\Framework\App\Filesystem\DirectoryList; @@ -40,31 +41,35 @@ public function __construct( } /** + * Build tasks block start text. + * * @return string */ private function getTasksBlockStart() { $tasksBlockStart = self::TASKS_BLOCK_START; if (defined('BP')) { - $tasksBlockStart .= ' ' . md5(BP); + $tasksBlockStart .= ' ' . hash("sha256", BP); } return $tasksBlockStart; } /** + * Build tasks block end text. + * * @return string */ private function getTasksBlockEnd() { $tasksBlockEnd = self::TASKS_BLOCK_END; if (defined('BP')) { - $tasksBlockEnd .= ' ' . md5(BP); + $tasksBlockEnd .= ' ' . hash("sha256", BP); } return $tasksBlockEnd; } /** - * {@inheritdoc} + * @inheritdoc */ public function getTasks() { @@ -82,7 +87,7 @@ public function getTasks() } /** - * {@inheritdoc} + * @inheritdoc */ public function saveTasks(array $tasks) { @@ -118,8 +123,7 @@ public function saveTasks(array $tasks) } /** - * {@inheritdoc} - * @throws LocalizedException + * @inheritdoc */ public function removeTasks() { @@ -182,7 +186,7 @@ private function cleanMagentoSection($content) private function getCrontabContent() { try { - $content = (string)$this->shell->execute('crontab -l'); + $content = (string)$this->shell->execute('crontab -l 2>/dev/null'); } catch (LocalizedException $e) { return ''; } @@ -203,6 +207,7 @@ private function save($content) try { $this->shell->execute('echo "' . $content . '" | crontab -'); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (LocalizedException $e) { throw new LocalizedException( new Phrase('Error during saving of crontab: %1', [$e->getPrevious()->getMessage()]), diff --git a/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php b/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php index b160eb0a7f95e..f6c863d9d9fad 100644 --- a/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php +++ b/lib/internal/Magento/Framework/Crontab/Test/Unit/CrontabManagerTest.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ + namespace Magento\Framework\Crontab\Test\Unit; use Magento\Framework\Crontab\CrontabManager; @@ -16,6 +17,9 @@ use Magento\Framework\Filesystem\Directory\ReadInterface; use Magento\Framework\Filesystem\DriverPool; +/** + * Tests crontab manager functionality. + */ class CrontabManagerTest extends \PHPUnit\Framework\TestCase { /** @@ -58,7 +62,7 @@ public function testGetTasksNoCrontab() $this->shellMock->expects($this->once()) ->method('execute') - ->with('crontab -l', []) + ->with('crontab -l 2>/dev/null', []) ->willThrowException($localizedException); $this->assertEquals([], $this->crontabManager->getTasks()); @@ -74,7 +78,7 @@ public function testGetTasks($content, $tasks) { $this->shellMock->expects($this->once()) ->method('execute') - ->with('crontab -l', []) + ->with('crontab -l 2>/dev/null', []) ->willReturn($content); $this->assertEquals($tasks, $this->crontabManager->getTasks()); @@ -88,17 +92,17 @@ public function getTasksDataProvider() return [ [ 'content' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento cron:run' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, 'tasks' => ['* * * * * /bin/php /var/www/magento/bin/magento cron:run'], ], [ 'content' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento cron:run' . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento setup:cron:run' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, 'tasks' => [ '* * * * * /bin/php /var/www/magento/bin/magento cron:run', '* * * * * /bin/php /var/www/magento/bin/magento setup:cron:run', @@ -127,7 +131,7 @@ public function testRemoveTasksWithException() $this->shellMock->expects($this->at(0)) ->method('execute') - ->with('crontab -l', []) + ->with('crontab -l 2>/dev/null', []) ->willReturn(''); $this->shellMock->expects($this->at(1)) @@ -148,7 +152,7 @@ public function testRemoveTasks($contentBefore, $contentAfter) { $this->shellMock->expects($this->at(0)) ->method('execute') - ->with('crontab -l', []) + ->with('crontab -l 2>/dev/null', []) ->willReturn($contentBefore); $this->shellMock->expects($this->at(1)) @@ -166,17 +170,17 @@ public function removeTasksDataProvider() return [ [ 'contentBefore' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento cron:run' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, 'contentAfter' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL ], [ 'contentBefore' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento cron:run' . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento setup:cron:run' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, 'contentAfter' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL ], [ @@ -276,7 +280,7 @@ public function testSaveTasks($tasks, $content, $contentToSave) $this->shellMock->expects($this->at(0)) ->method('execute') - ->with('crontab -l', []) + ->with('crontab -l 2>/dev/null', []) ->willReturn($content); $this->shellMock->expects($this->at(1)) @@ -292,9 +296,9 @@ public function testSaveTasks($tasks, $content, $contentToSave) public function saveTasksDataProvider() { $content = '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * /bin/php /var/www/magento/bin/magento cron:run' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL; + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL; return [ [ @@ -303,9 +307,9 @@ public function saveTasksDataProvider() ], 'content' => $content, 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * ' . PHP_BINARY . ' run.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], [ 'tasks' => [ @@ -313,9 +317,9 @@ public function saveTasksDataProvider() ], 'content' => $content, 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '1 2 3 4 5 ' . PHP_BINARY . ' run.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], [ 'tasks' => [ @@ -323,10 +327,10 @@ public function saveTasksDataProvider() ], 'content' => $content, 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * ' . PHP_BINARY . ' /var/www/magento2/run.php >>' . ' /var/www/magento2/var/log/cron.log' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], [ 'tasks' => [ @@ -334,10 +338,10 @@ public function saveTasksDataProvider() ], 'content' => $content, 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * ' . PHP_BINARY . ' /var/www/magento2/run.php' . ' %% cron:run | grep -v \"Ran \'jobs\' by schedule\"' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], [ 'tasks' => [ @@ -345,10 +349,10 @@ public function saveTasksDataProvider() ], 'content' => '* * * * * /bin/php /var/www/cron.php', 'contentToSave' => '* * * * * /bin/php /var/www/cron.php' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . md5(BP) . PHP_EOL + . CrontabManagerInterface::TASKS_BLOCK_START . ' ' . hash("sha256", BP) . PHP_EOL . '* * * * * ' . PHP_BINARY . ' /var/www/magento2/run.php' . ' %% cron:run | grep -v \"Ran \'jobs\' by schedule\"' . PHP_EOL - . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . md5(BP) . PHP_EOL, + . CrontabManagerInterface::TASKS_BLOCK_END . ' ' . hash("sha256", BP) . PHP_EOL, ], ]; } diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Label.php b/lib/internal/Magento/Framework/Data/Form/Element/Label.php index 901dcb5289e8d..70b7885e7a0d0 100644 --- a/lib/internal/Magento/Framework/Data/Form/Element/Label.php +++ b/lib/internal/Magento/Framework/Data/Form/Element/Label.php @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Data form abstract class - * - * @author Magento Core Team <core@magentocommerce.com> - */ namespace Magento\Framework\Data\Form\Element; +use Magento\Framework\Phrase; + +/** + * Label form element. + */ class Label extends \Magento\Framework\Data\Form\Element\AbstractElement { /** @@ -37,8 +37,13 @@ public function __construct( public function getElementHtml() { $html = $this->getBold() ? '<div class="control-value special">' : '<div class="control-value">'; - $html .= $this->getEscapedValue() . '</div>'; + if (is_string($this->getValue()) || $this->getValue() instanceof Phrase) { + $html .= $this->getEscapedValue(); + } + + $html .= '</div>'; $html .= $this->getAfterElementHtml(); + return $html; } } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php index 51e7ea9be0c24..d4c98cda17e12 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/Builder/Match.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Search\Adapter\Mysql\Query\Builder; use Magento\Framework\DB\Helper\Mysql\Fulltext; @@ -28,7 +30,7 @@ class Match implements QueryInterface */ const SPECIAL_CHARACTERS = '-+~/\\<>\'":*$#@()!,.?`=%&^'; - const MINIMAL_CHARACTER_LENGTH = 3; + const MINIMAL_CHARACTER_LENGTH = 1; /** * @var string[] diff --git a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd index 39cdec05a65ea..6486b39070788 100755 --- a/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd +++ b/lib/internal/Magento/Framework/View/Layout/etc/elements.xsd @@ -313,6 +313,7 @@ </xs:choice> <xs:attribute type="elementNameType" name="name" use="required"/> <xs:attribute type="xs:string" name="template" use="optional"/> + <xs:attribute type="xs:string" name="class" use="optional"/> <xs:attribute type="xs:boolean" name="display" default="true" use="optional"/> <xs:attribute type="xs:boolean" name="remove" use="optional"/> </xs:complexType> diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js index 18d71aad2071a..cfcdef0b701c9 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentowidget/editor_plugin.js @@ -132,7 +132,7 @@ define([ attributes.type = attributes.type.replace(/\\\\/g, '\\'); imageSrc = config.placeholders[attributes.type]; - if (config.types.indexOf(attributes['type_name']) > -1) { + if (imageSrc) { imageHtml += '<span class="magento-placeholder magento-widget mceNonEditable" ' + 'contenteditable="false">'; } else { @@ -147,8 +147,8 @@ define([ imageHtml += ' src="' + imageSrc + '"'; imageHtml += ' />'; - if (attributes['type_name']) { - imageHtml += attributes['type_name']; + if (config.types[attributes.type]) { + imageHtml += config.types[attributes.type]; } imageHtml += '</span>'; diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index 2ed3f8b0d620c..be78856b21fcd 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -141,7 +141,7 @@ define([ this.setupBreakpoints(); this.initFullscreenSettings(); - this.settings.$element.on('mousedown', '.fotorama__stage__frame', function () { + this.settings.$element.on('click', '.fotorama__stage__frame', function () { if ( !$(this).parents('.fotorama__shadows--left, .fotorama__shadows--right').length && !$(this).hasClass('fotorama-video-container') diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 373708ac35a00..2c3476732caad 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -977,12 +977,9 @@ // While first time init .gallery-placeholder { - .loading-mask { - padding: 0 0 50%; - position: static; - } - .loader img { - position: absolute; + &__image { + display: block; + margin: auto; } } @@ -1003,6 +1000,7 @@ display: block; } } + .fotorama__product-video--loaded { .fotorama__img, .fotorama__img--full { display: none !important; diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index 2035da9d58265..cfabe613a0ea1 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -409,9 +409,9 @@ <stringProp name="Argument.value">${__P(graphqlGetNavigationMenuByCategoryIdPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="graphqlGetProductSearchByProductNamePercentage" elementType="Argument"> - <stringProp name="Argument.name">graphqlGetProductSearchByProductNamePercentage</stringProp> - <stringProp name="Argument.value">${__P(graphqlGetProductSearchByProductNamePercentage,0)}</stringProp> + <elementProp name="graphqlGetProductDetailByProductNamePercentage" elementType="Argument"> + <stringProp name="Argument.name">graphqlGetProductDetailByProductNamePercentage</stringProp> + <stringProp name="Argument.value">${__P(graphqlGetProductDetailByProductNamePercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="graphqlGetProductSearchByTextAndCategoryIdPercentage" elementType="Argument"> @@ -39803,11 +39803,11 @@ vars.put("product_sku", product.get("sku")); </hashTree> - <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Get Product Search by product_name" enabled="true"> + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Get Product Detail by product_name" enabled="true"> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> <intProp name="ThroughputController.maxThroughput">1</intProp> - <stringProp name="ThroughputController.percentThroughput">${graphqlGetProductSearchByProductNamePercentage}</stringProp> + <stringProp name="ThroughputController.percentThroughput">${graphqlGetProductDetailByProductNamePercentage}</stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> <hashTree> <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> @@ -39828,7 +39828,7 @@ if (testLabel <hashTree/> <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> <stringProp name="BeanShellSampler.query"> - vars.put("testLabel", "Get Product Search by product_name"); + vars.put("testLabel", "Get Product Detail by product_name"); </stringProp> <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> </BeanShellSampler> @@ -39866,13 +39866,13 @@ vars.putObject("randomIntGenerator", random); </BeanShellSampler> <hashTree/> - <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> <stringProp name="BeanShellSampler.query"> import java.util.Random; Random random = vars.getObject("randomIntGenerator"); -number = random.nextInt(props.get("simple_products_list").size()); -product = props.get("simple_products_list").get(number); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); vars.put("product_url_key", product.get("url_key")); vars.put("product_id", product.get("id")); @@ -39883,10 +39883,10 @@ vars.put("product_sku", product.get("sku")); <stringProp name="BeanShellSampler.filename"/> <stringProp name="BeanShellSampler.parameters"/> <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> <hashTree/> - <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Product Search by product_name" enabled="true"> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Product Detail by product_name" enabled="true"> <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> <collectionProp name="Arguments.arguments"> @@ -39911,7 +39911,7 @@ vars.put("product_sku", product.get("sku")); <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> <boolProp name="HTTPSampler.monitor">false</boolProp> <stringProp name="HTTPSampler.embedded_url_re"/> - <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/get_product_search_by_product_name.jmx</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/get_product_detail_by_product_name.jmx</stringProp> </HTTPSamplerProxy> <hashTree> <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> @@ -40782,6 +40782,239 @@ vars.put("product_sku", product.get("sku")); <hashTree/> </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Update Simple Product qty In Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/update_simple_product_qty_in_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="664196114">{"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Billing Address On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setBillingAddressOnCart(\n input: {\n cart_id: \"${quote_id}\"\n billing_address: {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n }\n ) {\n cart {\n billing_address {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_billing_address_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1147076914">{"data":{"setBillingAddressOnCart":{"cart":{"billing_address":{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"BILLING"}}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Shipping Address On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setShippingAddressesOnCart(\n input: {\n cart_id: \"${quote_id}\"\n shipping_addresses: [\n {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n ]\n }\n ) {\n cart {\n shipping_addresses {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_shipping_address_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1671866339">{"data":{"setShippingAddressesOnCart":{"cart":{"shipping_addresses":[{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"SHIPPING"}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Payment Method On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setPaymentMethodOnCart(input: {\n cart_id: \"${quote_id}\", \n payment_method: {\n code: \"checkmo\"\n }\n }) {\n cart {\n selected_payment_method {\n code\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_payment_method_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1830199373">{"data":{"setPaymentMethodOnCart":{"cart":{"selected_payment_method":{"code":"checkmo"}}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Current Shipping Address" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"{\n cart(cart_id: \"${quote_id}\") {\n shipping_addresses {\n address_id\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/get_current_shipping_address.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract item id" enabled="true"> + <stringProp name="VAR">address_id</stringProp> + <stringProp name="JSONPATH">$.data.cart.shipping_addresses[0].address_id</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Shipping Method On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setShippingMethodsOnCart(input: \n {\n cart_id: \"${quote_id}\", \n shipping_methods: [{\n cart_address_id: ${address_id}\n carrier_code: \"flatrate\"\n method_code: \"flatrate\"\n }]\n }) {\n cart {\n shipping_addresses {\n selected_shipping_method {\n carrier_code\n method_code\n }\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_shipping_method_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="644143859">{"data":{"setShippingMethodsOnCart":{"cart":{"shipping_addresses":[{"selected_shipping_method":{"carrier_code":"flatrate","method_code":"flatrate"}}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Remove Simple Product From Cart" enabled="true"> <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> @@ -41070,6 +41303,239 @@ vars.put("product_sku", product.get("sku")); <hashTree/> </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Update Configurable Product qty In Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n updateCartItems(input: {\n cart_id: \"${quote_id}\"\n cart_items: [\n {\n cart_item_id: ${item_id}\n quantity: 5\n }\n ]\n }) {\n cart {\n items {\n id\n qty\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/update_configurable_product_qty_in_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="664196114">{"data":{"updateCartItems":{"cart":{"items":[{"id":"${item_id}","qty":5}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Billing Address On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setBillingAddressOnCart(\n input: {\n cart_id: \"${quote_id}\"\n billing_address: {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n }\n ) {\n cart {\n billing_address {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_billing_address_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1147076914">{"data":{"setBillingAddressOnCart":{"cart":{"billing_address":{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"BILLING"}}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Shipping Address On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setShippingAddressesOnCart(\n input: {\n cart_id: \"${quote_id}\"\n shipping_addresses: [\n {\n address: {\n firstname: \"test firstname\"\n lastname: \"test lastname\"\n company: \"test company\"\n street: [\"test street 1\", \"test street 2\"]\n city: \"test city\"\n region: \"test region\"\n postcode: \"887766\"\n country_code: \"US\"\n telephone: \"88776655\"\n save_in_address_book: false\n }\n }\n ]\n }\n ) {\n cart {\n shipping_addresses {\n firstname\n lastname\n company\n street\n city\n postcode\n telephone\n country {\n code\n label\n }\n address_type\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_shipping_address_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1671866339">{"data":{"setShippingAddressesOnCart":{"cart":{"shipping_addresses":[{"firstname":"test firstname","lastname":"test lastname","company":"test company","street":["test street 1","test street 2"],"city":"test city","postcode":"887766","telephone":"88776655","country":{"code":"US","label":"US"},"address_type":"SHIPPING"}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Payment Method On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setPaymentMethodOnCart(input: {\n cart_id: \"${quote_id}\", \n payment_method: {\n code: \"checkmo\"\n }\n }) {\n cart {\n selected_payment_method {\n code\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_payment_method_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1830199373">{"data":{"setPaymentMethodOnCart":{"cart":{"selected_payment_method":{"code":"checkmo"}}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Current Shipping Address" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"{\n cart(cart_id: \"${quote_id}\") {\n shipping_addresses {\n address_id\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/get_current_shipping_address.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract item id" enabled="true"> + <stringProp name="VAR">address_id</stringProp> + <stringProp name="JSONPATH">$.data.cart.shipping_addresses[0].address_id</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Set Shipping Method On Cart" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"query":"mutation {\n setShippingMethodsOnCart(input: \n {\n cart_id: \"${quote_id}\", \n shipping_methods: [{\n cart_address_id: ${address_id}\n carrier_code: \"flatrate\"\n method_code: \"flatrate\"\n }]\n }) {\n cart {\n shipping_addresses {\n selected_shipping_method {\n carrier_code\n method_code\n }\n }\n }\n }\n}","variables":null,"operationName":null}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port">${graphql_port_number}</stringProp> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}graphql</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/graphql/set_shipping_method_on_cart.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="644143859">{"data":{"setShippingMethodsOnCart":{"cart":{"shipping_addresses":[{"selected_shipping_method":{"carrier_code":"flatrate","method_code":"flatrate"}}]}}}}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Remove Configurable Product From Cart" enabled="true"> <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> <elementProp name="HTTPsampler.Arguments" elementType="Arguments">