diff --git a/.travis.yml b/.travis.yml index a99b22b1ebd4f..e078e89011dc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +sudo: required +dist: trusty + language: php php: - 5.5 @@ -44,12 +47,10 @@ before_script: # Install MySQL 5.6, create DB for integration tests - > sh -c "if [ '$TEST_SUITE' = 'integration_part_1' ] || [ '$TEST_SUITE' = 'integration_part_2' ] || [ '$TEST_SUITE' = 'integration_integrity' ]; then - sudo apt-get remove --purge mysql-common mysql-server-5.5 mysql-server-core-5.5 mysql-client-5.5 mysql-client-core-5.5; - sudo apt-get autoremove; - sudo apt-get autoclean; - sudo apt-add-repository ppa:ondrej/mysql-5.6 -y; - sudo apt-get update; - sudo apt-get install mysql-server-5.6 mysql-client-5.6; + sudo apt-get remove -y -qq --purge mysql-common mysql-server-5.5 mysql-server-core-5.5 mysql-client-5.5 mysql-client-core-5.5; + sudo apt-get -y -qq autoremove; + sudo apt-get -y -qq autoclean; + sudo apt-get install -y -qq mysql-server-5.6 mysql-client-5.6; mysql -uroot -e 'SET @@global.sql_mode = NO_ENGINE_SUBSTITUTION; CREATE DATABASE magento_integration_tests;'; mv dev/tests/integration/etc/install-config-mysql.travis.php.dist dev/tests/integration/etc/install-config-mysql.php; fi" diff --git a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Cctypes.php b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Cctypes.php index a4f1da1a7ba6c..87a7870abf79e 100644 --- a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Cctypes.php +++ b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Cctypes.php @@ -11,7 +11,6 @@ /** * Class Cctypes - * @package Magento\BraintreeTwo\Block\Adminhtml\Form\Field */ class Cctypes extends Select { diff --git a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Countries.php b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Countries.php index 10aea6da09efe..ae3ac5809d4ed 100644 --- a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Countries.php +++ b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/Countries.php @@ -11,7 +11,6 @@ /** * Class Countries - * @package Magento\BraintreeTwo\Block\Adminhtml\Form\Field */ class Countries extends Select { diff --git a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/CountryCreditCard.php b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/CountryCreditCard.php index 332e0d90efaf1..1b2b46d015169 100644 --- a/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/CountryCreditCard.php +++ b/app/code/Magento/BraintreeTwo/Block/Adminhtml/Form/Field/CountryCreditCard.php @@ -10,7 +10,6 @@ /** * Class CountryCreditCard - * @package Magento\BraintreeTwo\Block\Adminhtml\Form\Field */ class CountryCreditCard extends AbstractFieldArray { diff --git a/app/code/Magento/BraintreeTwo/Block/Form.php b/app/code/Magento/BraintreeTwo/Block/Form.php index 278b7490b863d..88f7ef3672b8e 100644 --- a/app/code/Magento/BraintreeTwo/Block/Form.php +++ b/app/code/Magento/BraintreeTwo/Block/Form.php @@ -14,7 +14,6 @@ /** * Class Form - * @package Magento\BraintreeTwo\Block */ class Form extends Cc { diff --git a/app/code/Magento/BraintreeTwo/Block/Info.php b/app/code/Magento/BraintreeTwo/Block/Info.php index b18032b805a3d..60127f446b822 100644 --- a/app/code/Magento/BraintreeTwo/Block/Info.php +++ b/app/code/Magento/BraintreeTwo/Block/Info.php @@ -10,7 +10,6 @@ /** * Class Info - * @package Magento\BraintreeTwo\Block */ class Info extends ConfigurableInfo { diff --git a/app/code/Magento/BraintreeTwo/Controller/Payment/GetNonce.php b/app/code/Magento/BraintreeTwo/Controller/Payment/GetNonce.php new file mode 100644 index 0000000000000..b7647971cb6a3 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Controller/Payment/GetNonce.php @@ -0,0 +1,89 @@ +logger = $logger; + $this->session = $session; + $this->command = $command; + } + + /** + * @inheritdoc + */ + public function execute() + { + $response = $this->resultFactory->create(ResultFactory::TYPE_JSON); + + try { + $publicHash = $this->getRequest()->getParam('public_hash'); + $customerId = $this->session->getCustomerId(); + $result = $this->command->execute(['publicHash' => $publicHash, 'customerId' => $customerId]) + ->get(); + $response->setData(['paymentMethodNonce' => $result['paymentMethodNonce']]); + + } catch (\Exception $e) { + $this->logger->critical($e); + return $this->processBadRequest($response); + } + + return $response; + } + + /** + * Return response for bad request + * @param ResultInterface $response + * @return ResultInterface + */ + private function processBadRequest(ResultInterface $response) + { + $response->setHttpResponseCode(Exception::HTTP_BAD_REQUEST); + $response->setData(['message' => __('Sorry, but something went wrong')]); + + return $response; + } +} diff --git a/app/code/Magento/BraintreeTwo/Controller/Token/GetClientToken.php b/app/code/Magento/BraintreeTwo/Controller/Token/GetClientToken.php deleted file mode 100644 index d5291178887e3..0000000000000 --- a/app/code/Magento/BraintreeTwo/Controller/Token/GetClientToken.php +++ /dev/null @@ -1,75 +0,0 @@ -logger = $logger; - $this->config = $config; - } - - /** - * @inheritdoc - */ - public function execute() - { - $controllerResult = $this->resultFactory->create(ResultFactory::TYPE_JSON); - - try { - $controllerResult->setData(['client_token' => $this->config->getClientToken()]); - } catch (\Exception $e) { - $this->logger->critical($e); - return $this->getErrorResponse($controllerResult); - } - - return $controllerResult; - } - - /** - * @param ResultInterface $controllerResult - * @return ResultInterface - */ - private function getErrorResponse(ResultInterface $controllerResult) - { - $controllerResult->setHttpResponseCode(Exception::HTTP_BAD_REQUEST); - $controllerResult->setData(['message' => __('Sorry, but something went wrong')]); - - return $controllerResult; - } -} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/BraintreeTwo/Gateway/Command/CaptureStrategyCommand.php new file mode 100644 index 0000000000000..1743916bf5847 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Command/CaptureStrategyCommand.php @@ -0,0 +1,144 @@ +commandPool = $commandPool; + $this->transactionRepository = $repository; + $this->filterBuilder = $filterBuilder; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function execute(array $commandSubject) + { + /** @var \Magento\Payment\Gateway\Data\PaymentDataObjectInterface $paymentDO */ + $paymentDO = $this->subjectReader->readPayment($commandSubject); + + /** @var \Magento\Sales\Api\Data\OrderPaymentInterface $paymentInfo */ + $paymentInfo = $paymentDO->getPayment(); + ContextHelper::assertOrderPayment($paymentInfo); + + $command = $this->getCommand($paymentInfo); + return $this->commandPool->get($command)->execute($commandSubject); + } + + /** + * Get execution command name + * @param OrderPaymentInterface $payment + * @return string + */ + private function getCommand(OrderPaymentInterface $payment) + { + // if auth transaction is not exists execute authorize&capture command + if (!$payment->getAuthorizationTransaction()) { + return self::SALE; + } + + if (!$this->isExistsCaptureTransaction($payment)) { + return self::CAPTURE; + } + + return self::CLONE_TRANSACTION; + } + + /** + * Check if capture transaction already exists + * + * @param OrderPaymentInterface $payment + * @return bool + */ + private function isExistsCaptureTransaction(OrderPaymentInterface $payment) + { + $filters[] = $this->filterBuilder->setField('payment_id') + ->setValue($payment->getId()) + ->create(); + + $filters[] = $this->filterBuilder->setField('txn_type') + ->setValue(TransactionInterface::TYPE_CAPTURE) + ->create(); + + $searchCriteria = $this->searchCriteriaBuilder->addFilters($filters) + ->create(); + + $count = $this->transactionRepository->getList($searchCriteria)->getTotalCount(); + return (boolean) $count; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Command/GetPaymentNonceCommand.php b/app/code/Magento/BraintreeTwo/Gateway/Command/GetPaymentNonceCommand.php new file mode 100644 index 0000000000000..dea64cafb6f6d --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Command/GetPaymentNonceCommand.php @@ -0,0 +1,92 @@ +tokenManagement = $tokenManagement; + $this->adapter = $adapter; + $this->resultFactory = $resultFactory; + $this->subjectReader = $subjectReader; + $this->responseValidator = $responseValidator; + } + + /** + * @inheritdoc + * @throws \Exception + */ + public function execute(array $commandSubject) + { + $publicHash = $this->subjectReader->readPublicHash($commandSubject); + $customerId = $this->subjectReader->readCustomerId($commandSubject); + $paymentToken = $this->tokenManagement->getByPublicHash($publicHash, $customerId); + if (!$paymentToken) { + throw new Exception('No available payment tokens'); + } + + $data = $this->adapter->createNonce($paymentToken->getGatewayToken()); + $result = $this->responseValidator->validate(['response' => ['object' => $data]]); + + if (!$result->isValid()) { + throw new Exception(__(implode("\n", $result->getFailsDescription()))); + } + + return $this->resultFactory->create(['array' => ['paymentMethodNonce' => $data->paymentMethodNonce->nonce]]); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Config/Config.php b/app/code/Magento/BraintreeTwo/Gateway/Config/Config.php index 651eef51162b4..ca32693b0bf52 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Config/Config.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Config/Config.php @@ -5,14 +5,8 @@ */ namespace Magento\BraintreeTwo\Gateway\Config; -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\BraintreeTwo\Model\Adapter\BraintreeConfiguration; -use Magento\BraintreeTwo\Model\Adapter\BraintreeClientToken; -use Magento\BraintreeTwo\Model\Adminhtml\Source\Environment; - /** * Class Config - * @package Magento\BraintreeTwo\Gateway\Config */ class Config extends \Magento\Payment\Gateway\Config\Config { @@ -27,80 +21,14 @@ class Config extends \Magento\Payment\Gateway\Config\Config const KEY_CC_TYPES_BRAINTREE_MAPPER = 'cctypes_braintree_mapper'; const KEY_SDK_URL = 'sdk_url'; const KEY_USE_CVV = 'useccv'; - - /** - * @var string - */ - private $clientToken = ''; - - /** - * @var BraintreeConfiguration - */ - private $braintreeConfiguration; - - /** - * @var BraintreeClientToken - */ - private $braintreeClientToken; - - /** - * @param ScopeConfigInterface $scopeConfig - * @param BraintreeConfiguration $braintreeConfiguration - * @param BraintreeClientToken $braintreeClientToken - * @param string $methodCode - * @param string $pathPattern - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - BraintreeConfiguration $braintreeConfiguration, - BraintreeClientToken $braintreeClientToken, - $methodCode = '', - $pathPattern = self::DEFAULT_PATH_PATTERN - ) { - parent::__construct( - $scopeConfig, - $methodCode, - $pathPattern - ); - - $this->braintreeConfiguration = $braintreeConfiguration; - $this->braintreeClientToken = $braintreeClientToken; - - if ($this->getValue(self::KEY_ACTIVE)) { - $this->initCredentials(); - } - } - - /** - * Initializes credentials. - * - * @return void - */ - public function initCredentials() - { - if ($this->getValue(self::KEY_ENVIRONMENT) == Environment::ENVIRONMENT_PRODUCTION) { - $this->braintreeConfiguration->environment(Environment::ENVIRONMENT_PRODUCTION); - } else { - $this->braintreeConfiguration->environment(Environment::ENVIRONMENT_SANDBOX); - } - $this->braintreeConfiguration->merchantId($this->getValue(self::KEY_MERCHANT_ID)); - $this->braintreeConfiguration->publicKey($this->getValue(self::KEY_PUBLIC_KEY)); - $this->braintreeConfiguration->privateKey($this->getValue(self::KEY_PRIVATE_KEY)); - } - - /** - * Generate a new client token if necessary - * @TODO method should be moved to adapter - * @return string - */ - public function getClientToken() - { - if (empty($this->clientToken)) { - $this->clientToken = $this->braintreeClientToken->generate(); - } - - return $this->clientToken; - } + const KEY_VERIFY_3DSECURE = 'verify_3dsecure'; + const KEY_THRESHOLD_AMOUNT = 'threshold_amount'; + const KEY_VERIFY_ALLOW_SPECIFIC = 'verify_all_countries'; + const KEY_VERIFY_SPECIFIC = 'verify_specific_countries'; + const VALUE_3DSECURE_ALL = 0; + const CODE_3DSECURE = 'three_d_secure'; + const KEY_KOUNT_MERCHANT_ID = 'kount_merchant_id'; + const FRAUD_PROTECTION = 'fraud_protection'; /** * Return the country specific card type config @@ -149,6 +77,7 @@ public function getCcTypesMapper() public function getCountryAvailableCardTypes($country) { $types = $this->getCountrySpecificCardTypeConfig(); + return (!empty($types[$country])) ? $types[$country] : []; } @@ -160,4 +89,75 @@ public function isCvvEnabled() { return (bool) $this->getValue(self::KEY_USE_CVV); } + + /** + * Check if 3d secure verification enabled + * @return bool + */ + public function isVerify3DSecure() + { + return (bool) $this->getValue(self::KEY_VERIFY_3DSECURE); + } + + /** + * Get threshold amount for 3d secure + * @return float + */ + public function getThresholdAmount() + { + return (double) $this->getValue(self::KEY_THRESHOLD_AMOUNT); + } + + /** + * Get list of specific countries for 3d secure + * @return array + */ + public function get3DSecureSpecificCountries() + { + if ((int) $this->getValue(self::KEY_VERIFY_ALLOW_SPECIFIC) == self::VALUE_3DSECURE_ALL) { + return []; + } + + return explode(',', $this->getValue(self::KEY_VERIFY_SPECIFIC)); + } + + /** + * @return string + */ + public function getEnvironment() + { + return $this->getValue(Config::KEY_ENVIRONMENT); + } + + /** + * @return string + */ + public function getKountMerchantId() + { + return $this->getValue(Config::KEY_KOUNT_MERCHANT_ID); + } + + /** + * @return string + */ + public function getMerchantId() + { + return $this->getValue(Config::KEY_MERCHANT_ID); + } + + /** + * @return string + */ + public function getSdkUrl() + { + return $this->getValue(Config::KEY_SDK_URL); + } + + /** + * @return bool + */ + public function hasFraudProtection() + { + return (bool) $this->getValue(Config::FRAUD_PROTECTION); + } } diff --git a/app/code/Magento/BraintreeTwo/Gateway/Helper/SubjectReader.php b/app/code/Magento/BraintreeTwo/Gateway/Helper/SubjectReader.php index 77304c6cabbe4..aa8a503fa8cde 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Helper/SubjectReader.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Helper/SubjectReader.php @@ -5,10 +5,13 @@ */ namespace Magento\BraintreeTwo\Gateway\Helper; +use Magento\Payment\Gateway\Helper; +use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; + /** * Class SubjectReader */ -class SubjectReader extends \Magento\Payment\Gateway\Helper\SubjectReader +class SubjectReader { /** * Reads response object from subject @@ -16,13 +19,85 @@ class SubjectReader extends \Magento\Payment\Gateway\Helper\SubjectReader * @param array $subject * @return object */ - public static function readResponseObject(array $subject) + public function readResponseObject(array $subject) { - $response = self::readResponse($subject); + $response = Helper\SubjectReader::readResponse($subject); if (!isset($response['object']) || !is_object($response['object'])) { throw new \InvalidArgumentException('Response object does not exist'); } return $response['object']; } + + /** + * Reads payment from subject + * + * @param array $subject + * @return PaymentDataObjectInterface + */ + public function readPayment(array $subject) + { + return Helper\SubjectReader::readPayment($subject); + } + + /** + * Reads transaction from subject + * + * @param array $subject + * @return \Braintree\Transaction + */ + public function readTransaction(array $subject) + { + if (!isset($subject['object']) || !is_object($subject['object'])) { + throw new \InvalidArgumentException('Response object does not exist'); + } + + if (!isset($subject['object']->transaction) + && !$subject['object']->transaction instanceof \Braintree\Transaction + ) { + throw new \InvalidArgumentException('The object is not a class \Braintree\Transaction.'); + } + + return $subject['object']->transaction; + } + + /** + * Reads amount from subject + * + * @param array $subject + * @return mixed + */ + public function readAmount(array $subject) + { + return Helper\SubjectReader::readAmount($subject); + } + + /** + * Reads customer id from subject + * @param array $subject + * @return int + */ + public function readCustomerId(array $subject) + { + if (empty($subject['customerId'])) { + throw new \InvalidArgumentException('The "customerId" field does not exists'); + } + + return (int)$subject['customerId']; + } + + /** + * Reads public hash from subject + * + * @param array $subject + * @return string + */ + public function readPublicHash(array $subject) + { + if (empty($subject['publicHash'])) { + throw new \InvalidArgumentException('The "publicHash" field does not exists'); + } + + return $subject['publicHash']; + } } diff --git a/app/code/Magento/BraintreeTwo/Gateway/Http/Client/AbstractTransaction.php b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/AbstractTransaction.php new file mode 100644 index 0000000000000..3e2fb25881286 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/AbstractTransaction.php @@ -0,0 +1,82 @@ +logger = $logger; + $this->customLogger = $customLogger; + $this->adapter = $adapter; + } + + /** + * @inheritdoc + */ + public function placeRequest(TransferInterface $transferObject) + { + $data = $transferObject->getBody(); + $log = [ + 'request' => $data, + 'client' => static::class + ]; + $response['object'] = []; + + try { + $response['object'] = $this->process($data); + } catch (\Exception $e) { + $message = __($e->getMessage() ?: 'Sorry, but something went wrong'); + $this->logger->critical($message); + throw new ClientException($message); + } finally { + $log['response'] = (array) $response['object']; + $this->customLogger->debug($log); + } + + return $response; + } + + /** + * Process http request + * @param array $data + * @return \Braintree\Result\Error|\Braintree\Result\Successful + */ + abstract protected function process(array $data); +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionClone.php b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionClone.php new file mode 100644 index 0000000000000..ed094aeb184d7 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionClone.php @@ -0,0 +1,25 @@ +adapter->cloneTransaction($transactionId, $data); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSale.php b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSale.php index 4896e81d70600..f906e22fa9629 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSale.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSale.php @@ -5,64 +5,16 @@ */ namespace Magento\BraintreeTwo\Gateway\Http\Client; -use Magento\Payment\Model\Method\Logger; -use Magento\Payment\Gateway\Http\ClientException; -use Magento\Payment\Gateway\Http\ClientInterface; -use Magento\Payment\Gateway\Http\TransferInterface; -use Magento\BraintreeTwo\Model\Adapter\BraintreeTransaction; - /** * Class TransactionSale */ -class TransactionSale implements ClientInterface +class TransactionSale extends AbstractTransaction { - /** - * @var Logger - */ - private $logger; - - /** - * @var BraintreeTransaction - */ - private $braintreeTransaction; - - /** - * Constructor - * - * @param Logger $logger - * @param BraintreeTransaction $braintreeTransaction - */ - public function __construct( - Logger $logger, - BraintreeTransaction $braintreeTransaction - ) { - $this->logger = $logger; - $this->braintreeTransaction = $braintreeTransaction; - } - /** * @inheritdoc */ - public function placeRequest(TransferInterface $transferObject) + protected function process(array $data) { - $data = $transferObject->getBody(); - $log = [ - 'request' => $data, - 'client' => self::class - ]; - $response['object'] = []; - - try { - $response['object'] = $this->braintreeTransaction->sale($data); - } catch (\Exception $e) { - throw new ClientException(__( - $e->getMessage() ?: 'Sorry, but something went wrong' - )); - } finally { - $log['response'] = (array) $response['object']; - $this->logger->debug($log); - } - - return $response; + return $this->adapter->sale($data); } } diff --git a/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSubmitForSettlement.php b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSubmitForSettlement.php new file mode 100644 index 0000000000000..36615c32780e0 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Http/Client/TransactionSubmitForSettlement.php @@ -0,0 +1,26 @@ +adapter->submitForSettlement( + $data[CaptureDataBuilder::TRANSACTION_ID], + $data[PaymentDataBuilder::AMOUNT] + ); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/AddressDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/AddressDataBuilder.php index feff663bb658d..fc9d854da6cc7 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Request/AddressDataBuilder.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/AddressDataBuilder.php @@ -5,8 +5,8 @@ */ namespace Magento\BraintreeTwo\Gateway\Request; -use Magento\Payment\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; /** * Class AddressDataBuilder @@ -75,14 +75,36 @@ class AddressDataBuilder implements BuilderInterface */ const COUNTRY_CODE = 'countryCodeAlpha2'; + /** + * Order ID + */ + const ORDER_ID = 'orderId'; + + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param SubjectReader $subjectReader + */ + public function __construct(SubjectReader $subjectReader) + { + $this->subjectReader = $subjectReader; + } + /** * @inheritdoc */ public function build(array $buildSubject) { - $paymentDO = SubjectReader::readPayment($buildSubject); + $paymentDO = $this->subjectReader->readPayment($buildSubject); $order = $paymentDO->getOrder(); - $result = []; + $result = [ + self::ORDER_ID => $order->getOrderIncrementId() + ]; $billingAddress = $order->getBillingAddress(); if ($billingAddress) { diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/CaptureDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/CaptureDataBuilder.php new file mode 100644 index 0000000000000..11bfdf8142968 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/CaptureDataBuilder.php @@ -0,0 +1,56 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $paymentDO = $this->subjectReader->readPayment($buildSubject); + + $payment = $paymentDO->getPayment(); + + $transactionId = $payment->getCcTransId(); + if (!$transactionId) { + throw new LocalizedException(__('No authorization transaction to proceed capture.')); + } + + return [ + self::TRANSACTION_ID => $transactionId, + PaymentDataBuilder::AMOUNT => $this->formatPrice($this->subjectReader->readAmount($buildSubject)) + ]; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/CustomerDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/CustomerDataBuilder.php index 2fff378d67c17..ee3c188070f35 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Request/CustomerDataBuilder.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/CustomerDataBuilder.php @@ -5,8 +5,8 @@ */ namespace Magento\BraintreeTwo\Gateway\Request; -use Magento\Payment\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; /** * Class CustomerDataBuilder @@ -44,12 +44,27 @@ class CustomerDataBuilder implements BuilderInterface */ const PHONE = 'phone'; + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param SubjectReader $subjectReader + */ + public function __construct(SubjectReader $subjectReader) + { + $this->subjectReader = $subjectReader; + } + /** * @inheritdoc */ public function build(array $buildSubject) { - $paymentDO = SubjectReader::readPayment($buildSubject); + $paymentDO = $this->subjectReader->readPayment($buildSubject); $order = $paymentDO->getOrder(); $billingAddress = $order->getBillingAddress(); diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/KountPaymentDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/KountPaymentDataBuilder.php new file mode 100644 index 0000000000000..4a121b9a2cadb --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/KountPaymentDataBuilder.php @@ -0,0 +1,65 @@ +config = $config; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $result = []; + if (!$this->config->hasFraudProtection()) { + return $result; + } + $paymentDO = $this->subjectReader->readPayment($buildSubject); + + $payment = $paymentDO->getPayment(); + $data = $payment->getAdditionalInformation(); + + if (isset($data[DataAssignObserver::DEVICE_DATA])) { + $result[self::DEVICE_DATA] = $data[DataAssignObserver::DEVICE_DATA]; + } + + return $result; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/PaymentDataBuilder.php index c9d1a743b2e93..807de2c50ab11 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Request/PaymentDataBuilder.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/PaymentDataBuilder.php @@ -5,16 +5,19 @@ */ namespace Magento\BraintreeTwo\Gateway\Request; -use Magento\Payment\Gateway\Helper\SubjectReader; -use Magento\Payment\Gateway\Request\BuilderInterface; use Magento\BraintreeTwo\Gateway\Config\Config; use Magento\BraintreeTwo\Observer\DataAssignObserver; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\Payment\Helper\Formatter; /** - * Class PaymentDataBuilder + * Payment Data Builder */ class PaymentDataBuilder implements BuilderInterface { + use Formatter; + /** * The billing amount of the request. This value must be greater than 0, * and must match the currency format of the merchant account. @@ -45,12 +48,20 @@ class PaymentDataBuilder implements BuilderInterface private $config; /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * * @param Config $config + * @param SubjectReader $subjectReader */ - public function __construct( - Config $config - ) { + public function __construct(Config $config, SubjectReader $subjectReader) + { $this->config = $config; + $this->subjectReader = $subjectReader; } /** @@ -58,16 +69,15 @@ public function __construct( */ public function build(array $buildSubject) { - $paymentDO = SubjectReader::readPayment($buildSubject); + $paymentDO = $this->subjectReader->readPayment($buildSubject); - /** @var \Magento\Sales\Model\Order\Payment $payment */ $payment = $paymentDO->getPayment(); $result = [ - self::AMOUNT => sprintf('%.2F', SubjectReader::readAmount($buildSubject)), + self::AMOUNT => $this->formatPrice($this->subjectReader->readAmount($buildSubject)), self::PAYMENT_METHOD_NONCE => $payment->getAdditionalInformation( DataAssignObserver::PAYMENT_METHOD_NONCE - ) + ), ]; $merchantAccountId = $this->config->getValue(Config::KEY_MERCHANT_ACCOUNT_ID); diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/ThreeDSecureDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/ThreeDSecureDataBuilder.php new file mode 100644 index 0000000000000..12206605a618a --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/ThreeDSecureDataBuilder.php @@ -0,0 +1,79 @@ +config = $config; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $result = []; + + $paymentDO = $this->subjectReader->readPayment($buildSubject); + $amount = $this->formatPrice($this->subjectReader->readAmount($buildSubject)); + + if ($this->is3DSecureEnabled($paymentDO->getOrder(), $amount)) { + $result['options'][Config::CODE_3DSECURE] = ['required' => true]; + } + return $result; + } + + /** + * Check if 3d secure is enabled + * @param OrderAdapterInterface $order + * @param float $amount + * @return bool + */ + private function is3DSecureEnabled(OrderAdapterInterface $order, $amount) + { + if (!$this->config->isVerify3DSecure() || $amount < $this->config->getThresholdAmount()) { + return false; + } + + $billingAddress = $order->getBillingAddress(); + $specificCounties = $this->config->get3DSecureSpecificCountries(); + if (!empty($specificCounties) && !in_array($billingAddress->getCountryId(), $specificCounties)) { + return false; + } + + return true; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Request/VaultDataBuilder.php b/app/code/Magento/BraintreeTwo/Gateway/Request/VaultDataBuilder.php new file mode 100644 index 0000000000000..9e494ffc590a7 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Request/VaultDataBuilder.php @@ -0,0 +1,69 @@ +vaultPayment = $vaultPayment; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $result = []; + + $isActiveVaultModule = $this->vaultPayment->isActiveForPayment(ConfigProvider::CODE); + if ($isActiveVaultModule) { + $result[self::OPTIONS][self::STORE_IN_VAULT_ON_SUCCESS] = true; + } + + return $result; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/CaptureDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/CaptureDetailsHandler.php new file mode 100644 index 0000000000000..a13c0d0e74ced --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/CaptureDetailsHandler.php @@ -0,0 +1,47 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function handle(array $handlingSubject, array $response) + { + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + /** + * @TODO after changes in sales module should be refactored for new interfaces + */ + $payment = $paymentDO->getPayment(); + ContextHelper::assertOrderPayment($payment); + + $payment->setIsTransactionClosed(false); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/CardDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/CardDetailsHandler.php index 975f17b0a1ada..a8bd79cf88329 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Response/CardDetailsHandler.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/CardDetailsHandler.php @@ -5,17 +5,15 @@ */ namespace Magento\BraintreeTwo\Gateway\Response; -use Braintree_Transaction; +use \Braintree\Transaction; use Magento\BraintreeTwo\Gateway\Config\Config; use Magento\Payment\Gateway\Helper\ContextHelper; -use Magento\Payment\Gateway\Helper\SubjectReader; -use Magento\Payment\Gateway\Response\HandlerInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; -use Magento\Sales\Model\Order\Payment; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +use Magento\Payment\Gateway\Response\HandlerInterface; /** * Class CardDetailsHandler - * @package Magento\BraintreeTwo\Gateway\Response */ class CardDetailsHandler implements HandlerInterface { @@ -30,16 +28,27 @@ class CardDetailsHandler implements HandlerInterface const CARD_NUMBER = 'cc_number'; /** - * @var \Magento\BraintreeTwo\Gateway\Config\Config + * @var Config */ private $config; /** - * @param \Magento\BraintreeTwo\Gateway\Config\Config $config + * @var SubjectReader */ - public function __construct(Config $config) - { + private $subjectReader; + + /** + * Constructor + * + * @param Config $config + * @param SubjectReader $subjectReader + */ + public function __construct( + Config $config, + SubjectReader $subjectReader + ) { $this->config = $config; + $this->subjectReader = $subjectReader; } /** @@ -47,13 +56,12 @@ public function __construct(Config $config) */ public function handle(array $handlingSubject, array $response) { - $paymentDO = SubjectReader::readPayment($handlingSubject); - /** @var \Braintree_Transaction $transaction */ - $transaction = $response['object']->transaction; + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + $transaction = $this->subjectReader->readTransaction($response); + /** * @TODO after changes in sales module should be refactored for new interfaces */ - /** @var \Magento\Sales\Model\Order\Payment $payment */ $payment = $paymentDO->getPayment(); ContextHelper::assertOrderPayment($payment); @@ -72,6 +80,7 @@ public function handle(array $handlingSubject, array $response) /** * Get type of credit card mapped from Braintree + * * @param string $type * @return array */ diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/CloneDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/CloneDetailsHandler.php new file mode 100644 index 0000000000000..e153214b2e76c --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/CloneDetailsHandler.php @@ -0,0 +1,49 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function handle(array $handlingSubject, array $response) + { + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + /** + * @TODO after changes in sales module should be refactored for new interfaces + */ + $payment = $paymentDO->getPayment(); + ContextHelper::assertOrderPayment($payment); + + $transaction = $this->subjectReader->readTransaction($response); + + $payment->setTransactionId($transaction->id); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/PaymentDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/PaymentDetailsHandler.php index 80d68bf263477..04f9c644387bf 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Response/PaymentDetailsHandler.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/PaymentDetailsHandler.php @@ -5,16 +5,15 @@ */ namespace Magento\BraintreeTwo\Gateway\Response; -use Braintree_Transaction; +use Braintree\Transaction; use Magento\BraintreeTwo\Observer\DataAssignObserver; use Magento\Payment\Gateway\Helper\ContextHelper; -use Magento\Payment\Gateway\Helper\SubjectReader; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Response\HandlerInterface; -use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Api\Data\OrderPaymentInterface; /** - * Class PaymentDetailsHandler - * @package Magento\BraintreeTwo\Gateway\Response + * Payment Details Handler */ class PaymentDetailsHandler implements HandlerInterface { @@ -43,18 +42,33 @@ class PaymentDetailsHandler implements HandlerInterface self::PROCESSOR_RESPONSE_TEXT, ]; + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param SubjectReader $subjectReader + */ + public function __construct(SubjectReader $subjectReader) + { + $this->subjectReader = $subjectReader; + } + /** * @inheritdoc */ public function handle(array $handlingSubject, array $response) { - $paymentDO = SubjectReader::readPayment($handlingSubject); - /** @var \Braintree_Transaction $transaction */ - $transaction = $response['object']->transaction; + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + /** @var \Braintree\Transaction $transaction */ + $transaction = $this->subjectReader->readTransaction($response); /** * @TODO after changes in sales module should be refactored for new interfaces */ - /** @var \Magento\Sales\Model\Order\Payment $payment */ + /** @var OrderPaymentInterface $payment */ $payment = $paymentDO->getPayment(); ContextHelper::assertOrderPayment($payment); diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/RiskDataHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/RiskDataHandler.php new file mode 100644 index 0000000000000..36678eb371817 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/RiskDataHandler.php @@ -0,0 +1,66 @@ +subjectReader = $subjectReader; + } + + /** + * Handles response + * + * @param array $handlingSubject + * @param array $response + * @return void + */ + public function handle(array $handlingSubject, array $response) + { + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + + /** @var \Braintree\Transaction $transaction */ + $transaction = $this->subjectReader->readTransaction($response); + + if (!isset($transaction->riskData)) { + return; + } + + $payment = $paymentDO->getPayment(); + ContextHelper::assertOrderPayment($payment); + + $payment->setAdditionalInformation(self::RISK_DATA_ID, $transaction->riskData->id); + $payment->setAdditionalInformation(self::RISK_DATA_DECISION, $transaction->riskData->decision); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/ThreeDSecureDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/ThreeDSecureDetailsHandler.php new file mode 100644 index 0000000000000..14005691da7eb --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/ThreeDSecureDetailsHandler.php @@ -0,0 +1,70 @@ +subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function handle(array $handlingSubject, array $response) + { + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + /** + * @TODO after changes in sales module should be refactored for new interfaces + */ + /** @var OrderPaymentInterface $payment */ + $payment = $paymentDO->getPayment(); + ContextHelper::assertOrderPayment($payment); + + /** @var Transaction $transaction */ + $transaction = $this->subjectReader->readTransaction($response); + + if ($payment->hasAdditionalInformation(self::LIABILITY_SHIFTED)) { + // remove 3d secure details for reorder + $payment->unsAdditionalInformation(self::LIABILITY_SHIFTED); + $payment->unsAdditionalInformation(self::LIABILITY_SHIFT_POSSIBLE); + } + + if (empty($transaction->threeDSecureInfo)) { + return; + } + + /** @var \Braintree\ThreeDSecureInfo $info */ + $info = $transaction->threeDSecureInfo; + $payment->setAdditionalInformation(self::LIABILITY_SHIFTED, $info->liabilityShifted ? 'Yes' : 'No'); + $shiftPossible = $info->liabilityShiftPossible ? 'Yes' : 'No'; + $payment->setAdditionalInformation(self::LIABILITY_SHIFT_POSSIBLE, $shiftPossible); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Response/VaultDetailsHandler.php b/app/code/Magento/BraintreeTwo/Gateway/Response/VaultDetailsHandler.php new file mode 100644 index 0000000000000..0debd234909d8 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Response/VaultDetailsHandler.php @@ -0,0 +1,155 @@ +vaultPayment = $vaultPayment; + $this->paymentTokenFactory = $paymentTokenFactory; + $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->config = $config; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function handle(array $handlingSubject, array $response) + { + $isActiveVaultModule = $this->vaultPayment->isActiveForPayment(ConfigProvider::CODE); + if (!$isActiveVaultModule) { + return; + } + + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + $transaction = $this->subjectReader->readTransaction($response); + $payment = $paymentDO->getPayment(); + + // add vault payment token entity to extension attributes + $paymentToken = $this->getVaultPaymentToken($transaction, $payment); + if (null !== $paymentToken) { + $extensionAttributes = $payment->getExtensionAttributes(); + if (null === $extensionAttributes) { + $extensionAttributes = $this->paymentExtensionFactory->create(); + $payment->setExtensionAttributes($extensionAttributes); + } + $extensionAttributes->setVaultPaymentToken($paymentToken); + } + } + + /** + * Get vault payment token entity + * + * @param \Braintree\Transaction $transaction + * @param OrderPaymentInterface $payment + * @return PaymentTokenInterface|null + */ + protected function getVaultPaymentToken(Transaction $transaction, OrderPaymentInterface $payment) + { + // Check token existing in gateway response + $token = $transaction->creditCardDetails->token; + if (empty($token)) { + return null; + } + + $order = $payment->getOrder(); + + /** @var PaymentTokenInterface $paymentToken */ + $paymentToken = $this->paymentTokenFactory->create(); + $paymentToken->setGatewayToken($token); + $paymentToken->setCustomerId($order->getCustomerId()); + $paymentToken->setPaymentMethodCode($payment->getMethod()); + $paymentToken->setCreatedAt($order->getCreatedAt()); + + $paymentToken->setTokenDetails($this->convertDetailsToJSON([ + 'type' => $this->getCreditCardType($transaction->creditCardDetails->cardType), + 'maskedCC' => $transaction->creditCardDetails->last4, + 'expirationDate' => $transaction->creditCardDetails->expirationDate + ])); + + return $paymentToken; + } + + /** + * Convert payment token details to JSON + * @param array $details + * @return string + */ + private function convertDetailsToJSON($details) + { + $json = \Zend_Json::encode($details); + return $json ? $json : '{}'; + } + + /** + * Get type of credit card mapped from Braintree + * + * @param string $type + * @return array + */ + private function getCreditCardType($type) + { + $replaced = str_replace(' ', '-', strtolower($type)); + $mapper = $this->config->getCctypesMapper(); + + return $mapper[$replaced]; + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Validator/PaymentNonceResponseValidator.php b/app/code/Magento/BraintreeTwo/Gateway/Validator/PaymentNonceResponseValidator.php new file mode 100644 index 0000000000000..51c804aeb49eb --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Gateway/Validator/PaymentNonceResponseValidator.php @@ -0,0 +1,39 @@ +subjectReader->readResponseObject($validationSubject); + $result = $this->createResult( + $this->validateSuccess($response) && + $this->validateErrors($response) && + $this->validatePaymentMethodNonce($response), + [__('Payment method nonce can\'t be retrieved.')] + ); + + return $result; + } + + /** + * Validate payment method nonce of response + * + * @param object $response + * @return bool + */ + private function validatePaymentMethodNonce($response) + { + return !empty($response->paymentMethodNonce) && !empty($response->paymentMethodNonce->nonce); + } +} diff --git a/app/code/Magento/BraintreeTwo/Gateway/Validator/ResponseValidator.php b/app/code/Magento/BraintreeTwo/Gateway/Validator/ResponseValidator.php index 48787ed97c899..0ed3b74d25466 100644 --- a/app/code/Magento/BraintreeTwo/Gateway/Validator/ResponseValidator.php +++ b/app/code/Magento/BraintreeTwo/Gateway/Validator/ResponseValidator.php @@ -5,21 +5,39 @@ */ namespace Magento\BraintreeTwo\Gateway\Validator; +use Braintree\Transaction; use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; use Magento\Payment\Gateway\Validator\AbstractValidator; +use Magento\Payment\Gateway\Validator\ResultInterfaceFactory; /** * Class ResponseValidator - * @package Magento\BraintreeTwo\Gateway\Validator */ class ResponseValidator extends AbstractValidator { + /** + * @var SubjectReader + */ + protected $subjectReader; + + /** + * Constructor + * + * @param ResultInterfaceFactory $resultFactory + * @param SubjectReader $subjectReader + */ + public function __construct(ResultInterfaceFactory $resultFactory, SubjectReader $subjectReader) + { + parent::__construct($resultFactory); + $this->subjectReader = $subjectReader; + } + /** * @inheritdoc */ public function validate(array $validationSubject) { - $response = SubjectReader::readResponseObject($validationSubject); + $response = $this->subjectReader->readResponseObject($validationSubject); $result = $this->createResult( $this->validateSuccess($response) @@ -35,7 +53,7 @@ public function validate(array $validationSubject) * @param object $response * @return bool */ - private function validateSuccess($response) + protected function validateSuccess($response) { return property_exists($response, 'success') && $response->success === true; } @@ -44,7 +62,7 @@ private function validateSuccess($response) * @param object $response * @return bool */ - private function validateErrors($response) + protected function validateErrors($response) { return !(property_exists($response, 'errors') && $response->errors->deepSize() > 0); } @@ -57,7 +75,7 @@ private function validateTransactionStatus($response) { return in_array( $response->transaction->status, - [\Braintree_Transaction::AUTHORIZED, \Braintree_Transaction::SUBMITTED_FOR_SETTLEMENT] + [Transaction::AUTHORIZED, Transaction::SUBMITTED_FOR_SETTLEMENT] ); } } diff --git a/app/code/Magento/BraintreeTwo/Helper/CcType.php b/app/code/Magento/BraintreeTwo/Helper/CcType.php index 4f553c4430dd5..427c773a8766b 100644 --- a/app/code/Magento/BraintreeTwo/Helper/CcType.php +++ b/app/code/Magento/BraintreeTwo/Helper/CcType.php @@ -9,7 +9,6 @@ /** * Class CcType - * @package Magento\BraintreeTwo\Helper */ class CcType { diff --git a/app/code/Magento/BraintreeTwo/Helper/Country.php b/app/code/Magento/BraintreeTwo/Helper/Country.php index 8065a3ca4c61d..1780e63358699 100644 --- a/app/code/Magento/BraintreeTwo/Helper/Country.php +++ b/app/code/Magento/BraintreeTwo/Helper/Country.php @@ -9,7 +9,6 @@ /** * Class Country - * @package Magento\BraintreeTwo\Helper */ class Country { diff --git a/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeAdapter.php b/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeAdapter.php new file mode 100644 index 0000000000000..75ddf82a0d931 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeAdapter.php @@ -0,0 +1,173 @@ +config = $config; + $this->initCredentials(); + } + + /** + * Initializes credentials. + * + * @return void + */ + protected function initCredentials() + { + if ($this->config->getValue(Config::KEY_ENVIRONMENT) == Environment::ENVIRONMENT_PRODUCTION) { + $this->environment(Environment::ENVIRONMENT_PRODUCTION); + } else { + $this->environment(Environment::ENVIRONMENT_SANDBOX); + } + $this->merchantId($this->config->getValue(Config::KEY_MERCHANT_ID)); + $this->publicKey($this->config->getValue(Config::KEY_PUBLIC_KEY)); + $this->privateKey($this->config->getValue(Config::KEY_PRIVATE_KEY)); + } + + /** + * @param string|null $value + * @return mixed + */ + public function environment($value = null) + { + return Configuration::environment($value); + } + + /** + * @param string|null $value + * @return mixed + */ + public function merchantId($value = null) + { + return Configuration::merchantId($value); + } + + /** + * @param string|null $value + * @return mixed + */ + public function publicKey($value = null) + { + return Configuration::publicKey($value); + } + + /** + * @param string|null $value + * @return mixed + */ + public function privateKey($value = null) + { + return Configuration::privateKey($value); + } + + /** + * @param array $params + * @return \Braintree\Result\Successful|\Braintree\Result\Error|null + */ + public function generate(array $params = []) + { + try { + return ClientToken::generate($params); + } catch (\Exception $e) { + return null; + } + } + + /** + * @param string $id + * @return \Braintree\CreditCard|null + */ + public function find($token) + { + try { + return CreditCard::find($token); + } catch (\Exception $e) { + return null; + } + } + + /** + * @param string $token + * @return \Braintree\Result\Successful|\Braintree\Result\Error + */ + public function createNonce($token) + { + return PaymentMethodNonce::create($token); + } + + /** + * @param array $attributes + * @return \Braintree\Result\Successful|\Braintree\Result\Error + */ + public function sale(array $attributes) + { + return Transaction::sale($attributes); + } + + /** + * @param string $transactionId + * @param null|float $amount + * @return \Braintree\Result\Successful|\Braintree\Result\Error + */ + public function submitForSettlement($transactionId, $amount = null) + { + return Transaction::submitForSettlement($transactionId, $amount); + } + + /** + * @param string $transactionId + * @return \Braintree\Result\Successful|\Braintree\Result\Error + */ + public function void($transactionId) + { + return Transaction::void($transactionId); + } + + /** + * @param string $transactionId + * @param null|float $amount + * @return \Braintree\Result\Successful|\Braintree\Result\Error + */ + public function refund($transactionId, $amount = null) + { + return Transaction::refund($transactionId, $amount); + } + + /** + * Clone original transaction + * @param string $transactionId + * @param array $attributes + * @return mixed + */ + public function cloneTransaction($transactionId, array $attributes) + { + return Transaction::cloneTransaction($transactionId, $attributes); + } +} diff --git a/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeClientToken.php b/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeClientToken.php deleted file mode 100644 index 02c79825e634d..0000000000000 --- a/app/code/Magento/BraintreeTwo/Model/Adapter/BraintreeClientToken.php +++ /dev/null @@ -1,31 +0,0 @@ -config = $config; + $this->adapter = $adapter; } /** @@ -40,14 +53,36 @@ public function getConfig() return [ 'payment' => [ self::CODE => [ - 'clientToken' => $this->config->getClientToken(), + 'clientToken' => $this->getClientToken(), 'ccTypesMapper' => $this->config->getCctypesMapper(), - 'sdkUrl' => $this->config->getValue(Config::KEY_SDK_URL), + 'sdkUrl' => $this->config->getSdkUrl(), 'countrySpecificCardTypes' => $this->config->getCountrySpecificCardTypeConfig(), 'availableCardTypes' => $this->config->getAvailableCardTypes(), - 'useCvv' => $this->config->isCvvEnabled() + 'useCvv' => $this->config->isCvvEnabled(), + 'environment' => $this->config->getEnvironment(), + 'kountMerchantId' => $this->config->getKountMerchantId(), + 'hasFraudProtection' => $this->config->hasFraudProtection(), + 'merchantId' => $this->config->getMerchantId(), ], + Config::CODE_3DSECURE => [ + 'enabled' => $this->config->isVerify3DSecure(), + 'thresholdAmount' => $this->config->getThresholdAmount(), + 'specificCountries' => $this->config->get3DSecureSpecificCountries() + ] ] ]; } + + /** + * Generate a new client token if necessary + * @return string + */ + public function getClientToken() + { + if (empty($this->clientToken)) { + $this->clientToken = $this->adapter->generate(); + } + + return $this->clientToken; + } } diff --git a/app/code/Magento/BraintreeTwo/Model/Ui/TokenUiComponentProvider.php b/app/code/Magento/BraintreeTwo/Model/Ui/TokenUiComponentProvider.php new file mode 100644 index 0000000000000..26ee2a1087569 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Model/Ui/TokenUiComponentProvider.php @@ -0,0 +1,71 @@ +componentFactory = $componentFactory; + $this->urlBuilder = $urlBuilder; + } + + /** + * Get UI component for token + * @param PaymentTokenInterface $paymentToken + * @return TokenUiComponentInterface + */ + public function getComponentForToken(PaymentTokenInterface $paymentToken) + { + $jsonDetails = json_decode($paymentToken->getTokenDetails() ?: '{}', true); + $component = $this->componentFactory->create( + [ + 'config' => [ + TokenUiComponentProviderInterface::COMPONENT_NONCE_URL => $this->getNonceRetrieveUrl(), + TokenUiComponentProviderInterface::COMPONENT_DETAILS => $jsonDetails, + TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash() + ], + 'name' => 'Magento_BraintreeTwo/js/view/payment/method-renderer/vault' + ] + ); + + return $component; + } + + /** + * Get url to retrieve payment method nonce + * @return string + */ + private function getNonceRetrieveUrl() + { + return $this->urlBuilder->getUrl(ConfigProvider::CODE . '/payment/getnonce', ['_secure' => true]); + } +} diff --git a/app/code/Magento/BraintreeTwo/Observer/DataAssignObserver.php b/app/code/Magento/BraintreeTwo/Observer/DataAssignObserver.php index 8da4f09767cbd..e38425e2fac25 100644 --- a/app/code/Magento/BraintreeTwo/Observer/DataAssignObserver.php +++ b/app/code/Magento/BraintreeTwo/Observer/DataAssignObserver.php @@ -14,6 +14,15 @@ class DataAssignObserver extends AbstractDataAssignObserver { const PAYMENT_METHOD_NONCE = 'payment_method_nonce'; + const DEVICE_DATA = 'device_data'; + + /** + * @var array + */ + protected $additionalInformationList = [ + self::PAYMENT_METHOD_NONCE, + self::DEVICE_DATA + ]; /** * @param Observer $observer @@ -21,17 +30,16 @@ class DataAssignObserver extends AbstractDataAssignObserver */ public function execute(Observer $observer) { - $method = $this->readMethodArgument($observer); $data = $this->readDataArgument($observer); - /** - * @TODO should be refactored after interface changes in payment and quote - */ - $paymentInfo = $method->getInfoInstance(); - if ($data->getDataByKey(self::PAYMENT_METHOD_NONCE) !== null) { - $paymentInfo->setAdditionalInformation( - self::PAYMENT_METHOD_NONCE, - $data->getDataByKey(self::PAYMENT_METHOD_NONCE) - ); + $paymentInfo = $this->readPaymentModelArgument($observer); + + foreach ($this->additionalInformationList as $additionalInformationKey) { + if ($data->getDataByKey($additionalInformationKey) !== null) { + $paymentInfo->setAdditionalInformation( + $additionalInformationKey, + $data->getDataByKey($additionalInformationKey) + ); + } } } } diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Block/FormTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Block/FormTest.php index 100a8d44f2b84..4c95ff76508f7 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Block/FormTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Block/FormTest.php @@ -14,7 +14,6 @@ /** * Class FormTest - * @package Magento\BraintreeTwo\Test\Unit\Block */ class FormTest extends \PHPUnit_Framework_TestCase { diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Controller/Payment/GetNonceTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Controller/Payment/GetNonceTest.php new file mode 100644 index 0000000000000..b7550e7c60a5f --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Controller/Payment/GetNonceTest.php @@ -0,0 +1,203 @@ +initResultFactoryMock(); + + $this->request = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getParam']) + ->getMock(); + + $this->command = $this->getMockBuilder(GetPaymentNonceCommand::class) + ->disableOriginalConstructor() + ->setMethods(['execute', '__wakeup']) + ->getMock(); + + $this->commandResult = $this->getMockBuilder(CommandResultInterface::class) + ->setMethods(['get']) + ->getMock(); + + $this->session = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->setMethods(['getCustomerId']) + ->getMock(); + + $this->logger = $this->getMock(LoggerInterface::class); + + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $context->expects(static::any()) + ->method('getRequest') + ->willReturn($this->request); + $context->expects(static::any()) + ->method('getResultFactory') + ->willReturn($this->resultFactory); + + $managerHelper = new ObjectManager($this); + $this->action = $managerHelper->getObject(GetNonce::class, [ + 'context' => $context, + 'logger' => $this->logger, + 'session' => $this->session, + 'command' => $this->command + ]); + } + + /** + * @covers \Magento\BraintreeTwo\Controller\Payment\GetNonce::execute + */ + public function testExecuteWithException() + { + $this->request->expects(static::once()) + ->method('getParam') + ->with('public_hash') + ->willReturn(null); + + $this->session->expects(static::once()) + ->method('getCustomerId') + ->willReturn(null); + + $exception = new \Exception('The "publicHash" field does not exists'); + $this->command->expects(static::once()) + ->method('execute') + ->willThrowException($exception); + + $this->logger->expects(static::once()) + ->method('critical') + ->with($exception); + + $this->result->expects(static::once()) + ->method('setHttpResponseCode') + ->with(Exception::HTTP_BAD_REQUEST); + $this->result->expects(static::once()) + ->method('setData') + ->with(['message' => 'Sorry, but something went wrong']); + + $this->action->execute(); + } + + /** + * @covers \Magento\BraintreeTwo\Controller\Payment\GetNonce::execute + */ + public function testExecute() + { + $customerId = 1; + $publicHash = '65b7bae0dcb690d93'; + $nonce = 'f1hc45'; + + $this->request->expects(static::once()) + ->method('getParam') + ->with('public_hash') + ->willReturn($publicHash); + + $this->session->expects(static::once()) + ->method('getCustomerId') + ->willReturn($customerId); + + $this->commandResult->expects(static::once()) + ->method('get') + ->willReturn([ + 'paymentMethodNonce' => $nonce + ]); + $this->command->expects(static::once()) + ->method('execute') + ->willReturn($this->commandResult); + + $this->result->expects(static::once()) + ->method('setData') + ->with(['paymentMethodNonce' => $nonce]); + + $this->logger->expects(static::never()) + ->method('critical'); + + $this->result->expects(static::never()) + ->method('setHttpResponseCode'); + + $this->action->execute(); + } + + /** + * Create mock for result factory object + */ + private function initResultFactoryMock() + { + $this->result = $this->getMockBuilder(ResultInterface::class) + ->setMethods(['setHttpResponseCode', 'renderResult', 'setHeader', 'setData']) + ->getMock(); + + $this->resultFactory = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->resultFactory->expects(static::once()) + ->method('create') + ->willReturn($this->result); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php new file mode 100644 index 0000000000000..53bce5cd4f904 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/CaptureStrategyCommandTest.php @@ -0,0 +1,257 @@ +commandPool = $this->getMockBuilder(CommandPoolInterface::class) + ->disableOriginalConstructor() + ->setMethods(['get', '__wakeup']) + ->getMock(); + + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->initCommandMock(); + $this->initTransactionRepositoryMock(); + $this->initFilterBuilderMock(); + $this->initSearchCriteriaBuilderMock(); + + $this->strategyCommand = new CaptureStrategyCommand( + $this->commandPool, + $this->transactionRepository, + $this->filterBuilder, + $this->searchCriteriaBuilder, + $this->subjectReaderMock + ); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\CaptureStrategyCommand::execute + */ + public function testSaleExecute() + { + $paymentData = $this->getPaymentDataObjectMock(); + $subject['payment'] = $paymentData; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + + $this->payment->expects(static::once()) + ->method('getAuthorizationTransaction') + ->willReturn(false); + + $this->payment->expects(static::never()) + ->method('getId'); + + $this->commandPool->expects(static::once()) + ->method('get') + ->with(CaptureStrategyCommand::SALE) + ->willReturn($this->command); + + $this->strategyCommand->execute($subject); + } + + /** + * @param int $size + * @param string $command + * @covers \Magento\BraintreeTwo\Gateway\Command\CaptureStrategyCommand::execute + * @dataProvider captureDataProvider + */ + public function testCaptureExecute($size, $command) + { + $paymentData = $this->getPaymentDataObjectMock(); + $subject['payment'] = $paymentData; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + + $this->payment->expects(static::once()) + ->method('getAuthorizationTransaction') + ->willReturn(true); + + $this->payment->expects(static::once()) + ->method('getId') + ->willReturn(1); + + $this->filterBuilder->expects(static::exactly(2)) + ->method('setField') + ->willReturnSelf(); + $this->filterBuilder->expects(static::exactly(2)) + ->method('setValue') + ->willReturnSelf(); + + $searchCriteria = new SearchCriteria(); + $this->searchCriteriaBuilder->expects(static::once()) + ->method('addFilters') + ->willReturnSelf(); + $this->searchCriteriaBuilder->expects(static::once()) + ->method('create') + ->willReturn($searchCriteria); + + $this->transactionRepository->expects(static::once()) + ->method('getList') + ->with($searchCriteria) + ->willReturnSelf(); + + $this->transactionRepository->expects(static::once()) + ->method('getTotalCount') + ->willReturn($size); + + $this->commandPool->expects(static::once()) + ->method('get') + ->with($command) + ->willReturn($this->command); + + $this->strategyCommand->execute($subject); + } + + /** + * Return variations for command testing + */ + public function captureDataProvider() + { + return [ + ['collectionSize' => 0, 'command' => CaptureStrategyCommand::CAPTURE], + ['collectionSize' => 1, 'command' => CaptureStrategyCommand::CLONE_TRANSACTION] + ]; + } + + /** + * Create mock for payment data object and order payment + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPaymentDataObjectMock() + { + $this->payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods(['getAuthorizationTransaction', 'getId']) + ->getMock(); + + $mock = $this->getMockBuilder(PaymentDataObject::class) + ->setMethods(['getPayment']) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + return $mock; + } + + /** + * Create mock for gateway command object + */ + private function initCommandMock() + { + $this->command = $this->getMockBuilder(GatewayCommand::class) + ->disableOriginalConstructor() + ->setMethods(['execute']) + ->getMock(); + + $this->command->expects(static::once()) + ->method('execute') + ->willReturn([]); + } + + /** + * Create mock for filter object + */ + private function initFilterBuilderMock() + { + $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['setField', 'setValue', 'create', '__wakeup']) + ->getMock(); + } + + /** + * Create mock for search criteria object + */ + private function initSearchCriteriaBuilderMock() + { + $this->searchCriteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['addFilters', 'create', '__wakeup']) + ->getMock(); + } + + /** + * Create mock for transaction repository + */ + private function initTransactionRepositoryMock() + { + $this->transactionRepository = $this->getMockBuilder(TransactionRepositoryInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getList', 'getTotalCount', 'delete', 'get', 'save', 'create', '__wakeup']) + ->getMock(); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php new file mode 100644 index 0000000000000..7e75dee359b78 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Command/GetPaymentNonceCommandTest.php @@ -0,0 +1,300 @@ +paymentToken = $this->getMockBuilder(PaymentToken::class) + ->disableOriginalConstructor() + ->setMethods(['getGatewayToken']) + ->getMock(); + + $this->tokenManagement = $this->getMockBuilder(PaymentTokenManagement::class) + ->disableOriginalConstructor() + ->setMethods(['getByPublicHash']) + ->getMock(); + + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['createNonce']) + ->getMock(); + + $this->resultFactory = $this->getMockBuilder(ArrayResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->setMethods(['readPublicHash', 'readCustomerId']) + ->getMock(); + + $this->validationResult = $this->getMockBuilder(ResultInterface::class) + ->setMethods(['isValid', 'getFailsDescription']) + ->getMock(); + + $this->responseValidator = $this->getMockBuilder(PaymentNonceResponseValidator::class) + ->disableOriginalConstructor() + ->setMethods(['validate', 'isValid', 'getFailsDescription']) + ->getMock(); + + $this->command = new GetPaymentNonceCommand( + $this->tokenManagement, + $this->adapter, + $this->resultFactory, + $this->subjectReader, + $this->responseValidator + ); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\GetPaymentNonceCommand::execute + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "publicHash" field does not exists + */ + public function testExecuteWithExceptionForPublicHash() + { + $exception = new \InvalidArgumentException('The "publicHash" field does not exists'); + + $this->subjectReader->expects(static::once()) + ->method('readPublicHash') + ->willThrowException($exception); + + $this->subjectReader->expects(static::never()) + ->method('readCustomerId'); + + $this->command->execute([]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\GetPaymentNonceCommand::execute + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The "customerId" field does not exists + */ + public function testExecuteWithExceptionForCustomerId() + { + $publicHash = '3wv2m24d2er3'; + + $this->subjectReader->expects(static::once()) + ->method('readPublicHash') + ->willReturn($publicHash); + + $exception = new \InvalidArgumentException('The "customerId" field does not exists'); + $this->subjectReader->expects(static::once()) + ->method('readCustomerId') + ->willThrowException($exception); + + $this->tokenManagement->expects(static::never()) + ->method('getByPublicHash'); + + $this->command->execute(['publicHash' => $publicHash]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\GetPaymentNonceCommand::execute + * @expectedException \Exception + * @expectedExceptionMessage No available payment tokens + */ + public function testExecuteWithExceptionForTokenManagement() + { + $publicHash = '3wv2m24d2er3'; + $customerId = 1; + + $this->subjectReader->expects(static::once()) + ->method('readPublicHash') + ->willReturn($publicHash); + + $this->subjectReader->expects(static::once()) + ->method('readCustomerId') + ->willReturn($customerId); + + $exception = new \Exception('No available payment tokens'); + $this->tokenManagement->expects(static::once()) + ->method('getByPublicHash') + ->willThrowException($exception); + + $this->paymentToken->expects(static::never()) + ->method('getGatewayToken'); + + $this->command->execute(['publicHash' => $publicHash, 'customerId' => $customerId]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\GetPaymentNonceCommand::execute + * @expectedException \Exception + * @expectedExceptionMessage Payment method nonce can't be retrieved. + */ + public function testExecuteWithFailedValidation() + { + $publicHash = '3wv2m24d2er3'; + $customerId = 1; + $token = 'jd2vnq'; + + $this->subjectReader->expects(static::once()) + ->method('readPublicHash') + ->willReturn($publicHash); + + $this->subjectReader->expects(static::once()) + ->method('readCustomerId') + ->willReturn($customerId); + + $this->tokenManagement->expects(static::once()) + ->method('getByPublicHash') + ->with($publicHash, $customerId) + ->willReturn($this->paymentToken); + + $this->paymentToken->expects(static::once()) + ->method('getGatewayToken') + ->willReturn($token); + + $obj = new \stdClass(); + $obj->success = false; + $this->adapter->expects(static::once()) + ->method('createNonce') + ->with($token) + ->willReturn($obj); + + $this->responseValidator->expects(static::once()) + ->method('validate') + ->with(['response' => ['object' => $obj]]) + ->willReturn($this->validationResult); + + $this->validationResult->expects(static::once()) + ->method('isValid') + ->willReturn(false); + + $this->validationResult->expects(static::once()) + ->method('getFailsDescription') + ->willReturn(['Payment method nonce can\'t be retrieved.']); + + $this->resultFactory->expects(static::never()) + ->method('create'); + + $this->command->execute(['publicHash' => $publicHash, 'customerId' => $customerId]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Command\GetPaymentNonceCommand::execute + */ + public function testExecute() + { + $publicHash = '3wv2m24d2er3'; + $customerId = 1; + $token = 'jd2vnq'; + $nonce = 's1dj23'; + + $this->subjectReader->expects(static::once()) + ->method('readPublicHash') + ->willReturn($publicHash); + + $this->subjectReader->expects(static::once()) + ->method('readCustomerId') + ->willReturn($customerId); + + $this->tokenManagement->expects(static::once()) + ->method('getByPublicHash') + ->with($publicHash, $customerId) + ->willReturn($this->paymentToken); + + $this->paymentToken->expects(static::once()) + ->method('getGatewayToken') + ->willReturn($token); + + $obj = new \stdClass(); + $obj->success = true; + $obj->paymentMethodNonce = new \stdClass(); + $obj->paymentMethodNonce->nonce = $nonce; + $this->adapter->expects(static::once()) + ->method('createNonce') + ->with($token) + ->willReturn($obj); + + $this->responseValidator->expects(static::once()) + ->method('validate') + ->with(['response' => ['object' => $obj]]) + ->willReturn($this->validationResult); + + $this->validationResult->expects(static::once()) + ->method('isValid') + ->willReturn(true); + + $this->validationResult->expects(static::never()) + ->method('getFailsDescription'); + + $expected = $this->getMockBuilder(ArrayResult::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + $expected->expects(static::once()) + ->method('get') + ->willReturn(['paymentMethodNonce' => $nonce]); + $this->resultFactory->expects(static::once()) + ->method('create') + ->willReturn($expected); + + $actual = $this->command->execute(['publicHash' => $publicHash, 'customerId' => $customerId]); + static::assertEquals($expected, $actual); + static::assertEquals($nonce, $actual->get()['paymentMethodNonce']); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Config/ConfigTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Config/ConfigTest.php index c275b3a8aa2de..ec998eb136e03 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Config/ConfigTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Config/ConfigTest.php @@ -7,35 +7,21 @@ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Config; use Magento\BraintreeTwo\Gateway\Config\Config; -use Magento\BraintreeTwo\Model\Adapter\BraintreeClientToken; -use Magento\BraintreeTwo\Model\Adapter\BraintreeConfiguration; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface; /** * Class ConfigTest - * */ class ConfigTest extends \PHPUnit_Framework_TestCase { const METHOD_CODE = 'braintree'; - const CLIENT_TOKEN = 'token'; /** * @var Config */ private $model; - /** - * @var BraintreeConfiguration|\PHPUnit_Framework_MockObject_MockObject - */ - private $braintreeConfigurationMock; - - /** - * @var BraintreeClientToken|\PHPUnit_Framework_MockObject_MockObject - */ - private $braintreeClientTokenMock; - /** * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -44,96 +30,9 @@ class ConfigTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->scopeConfigMock = $this->getMock(ScopeConfigInterface::class); - $this->braintreeConfigurationMock = $this->getMock(BraintreeConfiguration::class); - $this->braintreeClientTokenMock = $this->getMock(BraintreeClientToken::class); - - $this->model = new Config( - $this->scopeConfigMock, - $this->braintreeConfigurationMock, - $this->braintreeClientTokenMock, - self::METHOD_CODE - ); - } - - /** - * Test initCredentials call in constructor when payment is active - * - * @covers \Magento\BraintreeTwo\Gateway\Config\Config::initCredentials - */ - public function testConstructorActive() - { - $environment = \Magento\BraintreeTwo\Model\Adminhtml\Source\Environment::ENVIRONMENT_PRODUCTION; - $merchantId = 'merchantId'; - $publicKey = 'public_key'; - $privateKey = 'private_key'; - - $this->scopeConfigMock->expects(static::exactly(5)) - ->method('getValue') - ->willReturnMap( - [ - [$this->getPath(Config::KEY_ACTIVE), ScopeInterface::SCOPE_STORE, null, 1], - [$this->getPath(Config::KEY_ENVIRONMENT), ScopeInterface::SCOPE_STORE, null, $environment], - [$this->getPath(Config::KEY_MERCHANT_ID), ScopeInterface::SCOPE_STORE, null, $merchantId], - [$this->getPath(Config::KEY_PUBLIC_KEY), ScopeInterface::SCOPE_STORE, null, $publicKey], - [$this->getPath(Config::KEY_PRIVATE_KEY), ScopeInterface::SCOPE_STORE, null, $privateKey] - ] - ); - - $this->braintreeConfigurationMock->expects(static::once()) - ->method('environment') - ->with($environment); - $this->braintreeConfigurationMock->expects(static::once()) - ->method('merchantId') - ->with($merchantId); - $this->braintreeConfigurationMock->expects(static::once()) - ->method('publicKey') - ->with($publicKey); - $this->braintreeConfigurationMock->expects(static::once()) - ->method('privateKey') - ->with($privateKey); - - $this->model = new Config( - $this->scopeConfigMock, - $this->braintreeConfigurationMock, - $this->braintreeClientTokenMock, - self::METHOD_CODE - ); - } - - /** - * Test constructor when payment is inactive - */ - public function testConstructorInActive() - { - $this->scopeConfigMock->expects(static::once()) - ->method('getValue') - ->with($this->getPath(Config::KEY_ACTIVE), ScopeInterface::SCOPE_STORE, null) - ->willReturn(0); - $this->braintreeConfigurationMock->expects(static::never()) - ->method('environment'); - $this->braintreeConfigurationMock->expects(static::never()) - ->method('merchantId'); - $this->braintreeConfigurationMock->expects(static::never()) - ->method('publicKey'); - $this->braintreeConfigurationMock->expects(static::never()) - ->method('privateKey'); - $this->model = new Config( - $this->scopeConfigMock, - $this->braintreeConfigurationMock, - $this->braintreeClientTokenMock, - self::METHOD_CODE - ); - } - - public function testGetClientToken() - { - $this->braintreeClientTokenMock->expects(static::once()) - ->method('generate') - ->willReturn(self::CLIENT_TOKEN); - - static::assertEquals(self::CLIENT_TOKEN, $this->model->getClientToken()); + $this->model = new Config($this->scopeConfigMock, self::METHOD_CODE); } /** @@ -246,7 +145,7 @@ public function getCcTypesMapperDataProvider() } /** - * @covers \Magento\BraintreeTwo\Gateway\Config\Config::getCountryAvailableCardTypes + * @covers \Magento\BraintreeTwo\Gateway\Config\Config::getCountryAvailableCardTypes * @dataProvider getCountrySpecificCardTypeConfigDataProvider */ public function testCountryAvailableCardTypes($data, $countryData) @@ -262,6 +161,9 @@ public function testCountryAvailableCardTypes($data, $countryData) } } + /** + * @covers \Magento\BraintreeTwo\Gateway\Config\Config::isCvvEnabled + */ public function testUseCvv() { $this->scopeConfigMock->expects(static::any()) @@ -272,6 +174,104 @@ public function testUseCvv() static::assertEquals(true, $this->model->isCvvEnabled()); } + /** + * @param mixed $data + * @param boolean $expected + * @dataProvider verify3DSecureDataProvider + * @covers \Magento\BraintreeTwo\Gateway\Config\Config::isVerify3DSecure + */ + public function testIsVerify3DSecure($data, $expected) + { + $this->scopeConfigMock->expects(static::any()) + ->method('getValue') + ->with($this->getPath(Config::KEY_VERIFY_3DSECURE), ScopeInterface::SCOPE_STORE, null) + ->willReturn($data); + static::assertEquals($expected, $this->model->isVerify3DSecure()); + } + + /** + * Get items to verify 3d secure testing + * @return array + */ + public function verify3DSecureDataProvider() + { + return [ + ['data' => 1, 'expected' => true], + ['data' => true, 'expected' => true], + ['data' => '1', 'expected' => true], + ['data' => 0, 'expected' => false], + ['data' => '0', 'expected' => false], + ['data' => false, 'expected' => false], + ]; + } + + /** + * @param mixed $data + * @param double $expected + * @covers \Magento\BraintreeTwo\Gateway\Config\Config::getThresholdAmount + * @dataProvider thresholdAmountDataProvider + */ + public function testGetThresholdAmount($data, $expected) + { + $this->scopeConfigMock->expects(static::any()) + ->method('getValue') + ->with($this->getPath(Config::KEY_THRESHOLD_AMOUNT), ScopeInterface::SCOPE_STORE, null) + ->willReturn($data); + static::assertEquals($expected, $this->model->getThresholdAmount()); + } + + /** + * Get items for testing threshold amount + * @return array + */ + public function thresholdAmountDataProvider() + { + return [ + ['data' => '23.01', 'expected' => 23.01], + ['data' => -1.02, 'expected' => -1.02], + ['data' => true, 'expected' => 1], + ['data' => 'true', 'expected' => 0], + ['data' => 'abc', 'expected' => 0], + ['data' => false, 'expected' => 0], + ['data' => 'false', 'expected' => 0], + ['data' => 1, 'expected' => 1], + ]; + } + + /** + * @param int $value + * @param array $expected + * @covers \Magento\BraintreeTwo\Gateway\Config\Config::get3DSecureSpecificCountries + * @dataProvider threeDSecureSpecificCountriesDataProvider + */ + public function testGet3DSecureSpecificCountries($value, array $expected) + { + $this->scopeConfigMock->expects(static::at(0)) + ->method('getValue') + ->with($this->getPath(Config::KEY_VERIFY_ALLOW_SPECIFIC), ScopeInterface::SCOPE_STORE, null) + ->willReturn($value); + + if ($value !== Config::VALUE_3DSECURE_ALL) { + $this->scopeConfigMock->expects(static::at(1)) + ->method('getValue') + ->with($this->getPath(Config::KEY_VERIFY_SPECIFIC), ScopeInterface::SCOPE_STORE, null) + ->willReturn('GB,US'); + } + static::assertEquals($expected, $this->model->get3DSecureSpecificCountries()); + } + + /** + * Get variations to test specific countries for 3d secure + * @return array + */ + public function threeDSecureSpecificCountriesDataProvider() + { + return [ + ['configValue' => 0, 'expected' => []], + ['configValue' => 1, 'expected' => ['GB', 'US']], + ]; + } + /** * Return config path * diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Helper/SubjectReaderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Helper/SubjectReaderTest.php new file mode 100644 index 0000000000000..169a2a8ecaa6f --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Helper/SubjectReaderTest.php @@ -0,0 +1,63 @@ +subjectReader = new SubjectReader(); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Helper\SubjectReader::readCustomerId + * @expectedException InvalidArgumentException + * @expectedExceptionMessage The "customerId" field does not exists + */ + public function testReadCustomerIdWithException() + { + $this->subjectReader->readCustomerId([]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Helper\SubjectReader::readCustomerId + */ + public function testReadCustomerId() + { + $customerId = 1; + static::assertEquals($customerId, $this->subjectReader->readCustomerId(['customerId' => $customerId])); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Helper\SubjectReader::readPublicHash + * @expectedException InvalidArgumentException + * @expectedExceptionMessage The "publicHash" field does not exists + */ + public function testReadPublicHashWithException() + { + $this->subjectReader->readPublicHash([]); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Helper\SubjectReader::readPublicHash + */ + public function testReadPublicHash() + { + $hash = 'fj23djf2o1fd'; + static::assertEquals($hash, $this->subjectReader->readPublicHash(['publicHash' => $hash])); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionCloneTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionCloneTest.php new file mode 100644 index 0000000000000..2dba828a3f586 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionCloneTest.php @@ -0,0 +1,81 @@ +getMockForAbstractClass(LoggerInterface::class); + $this->loggerMock = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->getMock(); + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->client = new TransactionClone($criticalLogger, $this->loggerMock, $this->adapter); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Http\Client\TransactionSubmitForSettlement::placeRequest + */ + public function testPlaceRequest() + { + $data = new Successful(['success'], [true]); + $this->adapter->expects(static::once()) + ->method('cloneTransaction') + ->willReturn($data); + + /** @var TransferInterface|\PHPUnit_Framework_MockObject_MockObject $transferObjectMock */ + $transferObjectMock = $this->getTransferObjectMock(); + $response = $this->client->placeRequest($transferObjectMock); + static::assertTrue(is_object($response['object'])); + static::assertEquals(['object' => $data], $response); + } + + /** + * @return TransferInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private function getTransferObjectMock() + { + $mock = $this->getMock(TransferInterface::class); + $mock->expects(static::once()) + ->method('getBody') + ->willReturn([ + 'transaction_id' => 'vb4b7c6b', + 'amount' => 45.00 + ]); + + return $mock; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSaleTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSaleTest.php index af1ad2a8f74f5..d2c26fc00c9d9 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSaleTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSaleTest.php @@ -5,10 +5,11 @@ */ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Http\Client; -use Magento\BraintreeTwo\Model\Adapter\BraintreeTransaction; -use Magento\Payment\Model\Method\Logger; use Magento\BraintreeTwo\Gateway\Http\Client\TransactionSale; +use Magento\BraintreeTwo\Model\Adapter\BraintreeAdapter; use Magento\Payment\Gateway\Http\TransferInterface; +use Magento\Payment\Model\Method\Logger; +use Psr\Log\LoggerInterface; /** * Class TransactionSaleTest @@ -26,9 +27,9 @@ class TransactionSaleTest extends \PHPUnit_Framework_TestCase private $loggerMock; /** - * @var BraintreeTransaction|\PHPUnit_Framework_MockObject_MockObject + * @var BraintreeAdapter|\PHPUnit_Framework_MockObject_MockObject */ - private $braintreeTransactionMock; + private $adapter; /** * Set up @@ -37,13 +38,15 @@ class TransactionSaleTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { + $criticalLoggerMock = $this->getMockForAbstractClass(LoggerInterface::class); $this->loggerMock = $this->getMockBuilder(Logger::class) ->disableOriginalConstructor() ->getMock(); - $this->braintreeTransactionMock = $this->getMockBuilder(BraintreeTransaction::class) + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() ->getMock(); - $this->model = new TransactionSale($this->loggerMock, $this->braintreeTransactionMock); + $this->model = new TransactionSale($criticalLoggerMock, $this->loggerMock, $this->adapter); } /** @@ -66,7 +69,7 @@ public function testPlaceRequestException() ] ); - $this->braintreeTransactionMock->expects($this->once()) + $this->adapter->expects($this->once()) ->method('sale') ->willThrowException(new \Exception('Test messages')); @@ -84,7 +87,7 @@ public function testPlaceRequestException() public function testPlaceRequestSuccess() { $response = $this->getResponseObject(); - $this->braintreeTransactionMock->expects($this->once()) + $this->adapter->expects($this->once()) ->method('sale') ->with($this->getTransferData()) ->willReturn($response) diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSubmitForSettlementTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSubmitForSettlementTest.php new file mode 100644 index 0000000000000..d5a3f4bc6a24b --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Http/Client/TransactionSubmitForSettlementTest.php @@ -0,0 +1,103 @@ +getMockForAbstractClass(LoggerInterface::class); + $this->logger = $this->getMockBuilder(Logger::class) + ->disableOriginalConstructor() + ->setMethods(['debug']) + ->getMock(); + $this->adapter = $this->getMockBuilder(BraintreeAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['submitForSettlement']) + ->getMock(); + + $this->client = new TransactionSubmitForSettlement( + $criticalLoggerMock, + $this->logger, + $this->adapter + ); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Http\Client\TransactionSubmitForSettlement::placeRequest + * @expectedException \Magento\Payment\Gateway\Http\ClientException + * @expectedExceptionMessage Transaction has been declined + */ + public function testPlaceRequestWithException() + { + $exception = new \Exception('Transaction has been declined'); + $this->adapter->expects(static::once()) + ->method('submitForSettlement') + ->willThrowException($exception); + + /** @var TransferInterface|\PHPUnit_Framework_MockObject_MockObject $transferObjectMock */ + $transferObjectMock = $this->getTransferObjectMock(); + $this->client->placeRequest($transferObjectMock); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Http\Client\TransactionSubmitForSettlement::placeRequest + */ + public function testPlaceRequest() + { + $data = new Successful(['success'], [true]); + $this->adapter->expects(static::once()) + ->method('submitForSettlement') + ->willReturn($data); + + /** @var TransferInterface|\PHPUnit_Framework_MockObject_MockObject $transferObjectMock */ + $transferObjectMock = $this->getTransferObjectMock(); + $response = $this->client->placeRequest($transferObjectMock); + static::assertTrue(is_object($response['object'])); + static::assertEquals(['object' => $data], $response); + } + + /** + * @return TransferInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private function getTransferObjectMock() + { + $mock = $this->getMock(TransferInterface::class); + $mock->expects($this->once()) + ->method('getBody') + ->willReturn([ + 'transaction_id' => 'vb4c6b', + 'amount' => 124.00 + ]); + + return $mock; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/AddressDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/AddressDataBuilderTest.php index e2a432e9fa36c..a37c0a1241639 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/AddressDataBuilderTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/AddressDataBuilderTest.php @@ -9,7 +9,11 @@ use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Payment\Gateway\Data\OrderAdapterInterface; use Magento\Payment\Gateway\Data\AddressAdapterInterface; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +/** + * Class AddressDataBuilderTest + */ class AddressDataBuilderTest extends \PHPUnit_Framework_TestCase { /** @@ -27,17 +31,24 @@ class AddressDataBuilderTest extends \PHPUnit_Framework_TestCase */ private $builder; + /** + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + */ + private $subjectReaderMock; + public function setUp() { $this->paymentDOMock = $this->getMock(PaymentDataObjectInterface::class); $this->orderMock = $this->getMock(OrderAdapterInterface::class); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); - $this->builder = new AddressDataBuilder(); + $this->builder = new AddressDataBuilder($this->subjectReaderMock); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided */ public function testBuildReadPaymentException() { @@ -45,18 +56,27 @@ public function testBuildReadPaymentException() 'payment' => null, ]; + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->builder->build($buildSubject); } public function testBuildNoAddresses() { - $this->paymentDOMock->expects($this->once()) + $this->paymentDOMock->expects(static::once()) ->method('getOrder') ->willReturn($this->orderMock); - $this->orderMock->expects($this->once()) + + $this->orderMock->expects(static::once()) + ->method('getOrderIncrementId') + ->willReturn('000000100'); + $this->orderMock->expects(static::once()) ->method('getShippingAddress') ->willReturn(null); - $this->orderMock->expects($this->once()) + $this->orderMock->expects(static::once()) ->method('getBillingAddress') ->willReturn(null); @@ -64,7 +84,12 @@ public function testBuildNoAddresses() 'payment' => $this->paymentDOMock, ]; - static::assertEmpty($this->builder->build($buildSubject)); + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDOMock); + + static::assertEquals(['orderId' => '000000100'], $this->builder->build($buildSubject)); } /** @@ -77,13 +102,17 @@ public function testBuild($addressData, $expectedResult) { $addressMock = $this->getAddressMock($addressData); - $this->paymentDOMock->expects($this->once()) + $this->paymentDOMock->expects(static::once()) ->method('getOrder') ->willReturn($this->orderMock); - $this->orderMock->expects($this->once()) + + $this->orderMock->expects(static::once()) + ->method('getOrderIncrementId') + ->willReturn('000000101'); + $this->orderMock->expects(static::once()) ->method('getShippingAddress') ->willReturn($addressMock); - $this->orderMock->expects($this->once()) + $this->orderMock->expects(static::once()) ->method('getBillingAddress') ->willReturn($addressMock); @@ -91,7 +120,12 @@ public function testBuild($addressData, $expectedResult) 'payment' => $this->paymentDOMock, ]; - $this->assertEquals($expectedResult, $this->builder->build($buildSubject)); + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDOMock); + + self::assertEquals($expectedResult, $this->builder->build($buildSubject)); } /** @@ -113,6 +147,7 @@ public function dataProviderBuild() 'post_code' => '00000' ], [ + AddressDataBuilder::ORDER_ID => '000000101', AddressDataBuilder::SHIPPING_ADDRESS => [ AddressDataBuilder::FIRST_NAME => 'John', AddressDataBuilder::LAST_NAME => 'Smith', diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php new file mode 100644 index 0000000000000..1a5e6d7cd0863 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CaptureDataBuilderTest.php @@ -0,0 +1,117 @@ +paymentDO = $this->getMock(PaymentDataObjectInterface::class); + $this->payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new CaptureDataBuilder($this->subjectReaderMock); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Request\CaptureDataBuilder::build + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage No authorization transaction to proceed capture. + */ + public function testBuildWithException() + { + $amount = 10.00; + $buildSubject = [ + 'payment' => $this->paymentDO, + 'amount' => $amount + ]; + + $this->payment->expects(static::once()) + ->method('getCcTransId') + ->willReturn(''); + + $this->paymentDO->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + + $this->builder->build($buildSubject); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Request\CaptureDataBuilder::build + */ + public function testBuild() + { + $transactionId = 'b3b99d'; + $amount = 10.00; + + $expected = [ + 'transaction_id' => $transactionId, + 'amount' => $amount + ]; + + $buildSubject = [ + 'payment' => $this->paymentDO, + 'amount' => $amount + ]; + + $this->payment->expects(static::once()) + ->method('getCcTransId') + ->willReturn($transactionId); + + $this->paymentDO->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReaderMock->expects(self::once()) + ->method('readAmount') + ->with($buildSubject) + ->willReturn($amount); + + static::assertEquals($expected, $this->builder->build($buildSubject)); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php index 79148e1f25b01..e2cca39949797 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/CustomerDataBuilderTest.php @@ -9,7 +9,11 @@ use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Payment\Gateway\Data\OrderAdapterInterface; use Magento\Payment\Gateway\Data\AddressAdapterInterface; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +/** + * Class CustomerDataBuilderTest + */ class CustomerDataBuilderTest extends \PHPUnit_Framework_TestCase { /** @@ -27,17 +31,24 @@ class CustomerDataBuilderTest extends \PHPUnit_Framework_TestCase */ private $builder; + /** + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + */ + private $subjectReaderMock; + public function setUp() { $this->paymentDOMock = $this->getMock(PaymentDataObjectInterface::class); $this->orderMock = $this->getMock(OrderAdapterInterface::class); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); - $this->builder = new CustomerDataBuilder(); + $this->builder = new CustomerDataBuilder($this->subjectReaderMock); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided */ public function testBuildReadPaymentException() { @@ -45,6 +56,11 @@ public function testBuildReadPaymentException() 'payment' => null, ]; + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->builder->build($buildSubject); } @@ -58,10 +74,10 @@ public function testBuild($billingData, $expectedResult) { $billingMock = $this->getBillingMock($billingData); - $this->paymentDOMock->expects($this->once()) + $this->paymentDOMock->expects(static::once()) ->method('getOrder') ->willReturn($this->orderMock); - $this->orderMock->expects($this->once()) + $this->orderMock->expects(static::once()) ->method('getBillingAddress') ->willReturn($billingMock); @@ -69,7 +85,12 @@ public function testBuild($billingData, $expectedResult) 'payment' => $this->paymentDOMock, ]; - $this->assertEquals($expectedResult, $this->builder->build($buildSubject)); + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDOMock); + + self::assertEquals($expectedResult, $this->builder->build($buildSubject)); } /** diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php new file mode 100644 index 0000000000000..9c455d2bcb8b9 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/KountPaymentDataBuilderTest.php @@ -0,0 +1,118 @@ +paymentDO = $this->getMock(PaymentDataObjectInterface::class); + $this->configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentMock = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new KountPaymentDataBuilder($this->configMock, $this->subjectReaderMock); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testBuildReadPaymentException() + { + $buildSubject = []; + + $this->configMock->expects(static::once()) + ->method('hasFraudProtection') + ->willReturn(true); + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willThrowException(new \InvalidArgumentException()); + + $this->builder->build($buildSubject); + } + + public function testBuild() + { + $additionalData = [ + DataAssignObserver::DEVICE_DATA => self::DEVICE_DATA + ]; + + $expectedResult = [ + KountPaymentDataBuilder::DEVICE_DATA => self::DEVICE_DATA, + ]; + + $buildSubject = ['payment' => $this->paymentDO]; + + $this->paymentMock->expects(static::exactly(count($additionalData))) + ->method('getAdditionalInformation') + ->willReturn($additionalData); + + $this->configMock->expects(static::once()) + ->method('hasFraudProtection') + ->willReturn(true); + + $this->paymentDO->expects(static::once()) + ->method('getPayment') + ->willReturn($this->paymentMock); + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + + static::assertEquals( + $expectedResult, + $this->builder->build($buildSubject) + ); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php index 903f64da383d6..1a95153293932 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/PaymentDataBuilderTest.php @@ -5,12 +5,16 @@ */ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Request; -use Magento\BraintreeTwo\Gateway\Request\PaymentDataBuilder; -use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\BraintreeTwo\Gateway\Config\Config; +use Magento\BraintreeTwo\Gateway\Request\PaymentDataBuilder; use Magento\BraintreeTwo\Observer\DataAssignObserver; +use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Sales\Model\Order\Payment; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +/** + * Class PaymentDataBuilderTest + */ class PaymentDataBuilderTest extends \PHPUnit_Framework_TestCase { const PAYMENT_METHOD_NONCE = 'nonce'; @@ -36,6 +40,11 @@ class PaymentDataBuilderTest extends \PHPUnit_Framework_TestCase */ private $paymentDO; + /** + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + */ + private $subjectReaderMock; + public function setUp() { $this->paymentDO = $this->getMock(PaymentDataObjectInterface::class); @@ -45,24 +54,30 @@ public function setUp() $this->paymentMock = $this->getMockBuilder(Payment::class) ->disableOriginalConstructor() ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); - $this->builder = new PaymentDataBuilder($this->configMock); + $this->builder = new PaymentDataBuilder($this->configMock, $this->subjectReaderMock); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Payment data object should be provided */ public function testBuildReadPaymentException() { $buildSubject = []; + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->builder->build($buildSubject); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Amount should be provided */ public function testBuildReadAmountException() { @@ -71,15 +86,31 @@ public function testBuildReadAmountException() 'amount' => null ]; + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReaderMock->expects(self::once()) + ->method('readAmount') + ->with($buildSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->builder->build($buildSubject); } public function testBuild() { + $additionalData = [ + [ + DataAssignObserver::PAYMENT_METHOD_NONCE, + self::PAYMENT_METHOD_NONCE + ] + ]; + $expectedResult = [ PaymentDataBuilder::AMOUNT => 10.00, PaymentDataBuilder::PAYMENT_METHOD_NONCE => self::PAYMENT_METHOD_NONCE, - PaymentDataBuilder::MERCHANT_ACCOUNT_ID => self::MERCHANT_ACCOUNT_ID + PaymentDataBuilder::MERCHANT_ACCOUNT_ID => self::MERCHANT_ACCOUNT_ID, ]; $buildSubject = [ @@ -87,10 +118,9 @@ public function testBuild() 'amount' => 10.00 ]; - $this->paymentMock->expects(static::once()) + $this->paymentMock->expects(static::exactly(count($additionalData))) ->method('getAdditionalInformation') - ->with(DataAssignObserver::PAYMENT_METHOD_NONCE) - ->willReturn(self::PAYMENT_METHOD_NONCE); + ->willReturnMap($additionalData); $this->configMock->expects(static::once()) ->method('getValue') @@ -101,6 +131,15 @@ public function testBuild() ->method('getPayment') ->willReturn($this->paymentMock); + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReaderMock->expects(self::once()) + ->method('readAmount') + ->with($buildSubject) + ->willReturn(10.00); + static::assertEquals( $expectedResult, $this->builder->build($buildSubject) diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/ThreeDSecureDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/ThreeDSecureDataBuilderTest.php new file mode 100644 index 0000000000000..de7ee0e9f2872 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/ThreeDSecureDataBuilderTest.php @@ -0,0 +1,165 @@ +initOrderMock(); + + $this->paymentDO = $this->getMockBuilder(PaymentDataObjectInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getOrder', 'getPayment']) + ->getMock(); + $this->paymentDO->expects(static::once()) + ->method('getOrder') + ->willReturn($this->order); + + $this->configMock = $this->getMockBuilder(Config::class) + ->setMethods(['isVerify3DSecure', 'getThresholdAmount', 'get3DSecureSpecificCountries']) + ->disableOriginalConstructor() + ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->builder = new ThreeDSecureDataBuilder($this->configMock, $this->subjectReaderMock); + } + + /** + * @param bool $verify + * @param float $thresholdAmount + * @param string $countryId + * @param array $countries + * @param array $expected + * @covers \Magento\BraintreeTwo\Gateway\Request\ThreeDSecureDataBuilder::build + * @dataProvider buildDataProvider + */ + public function testBuild($verify, $thresholdAmount, $countryId, array $countries, array $expected) + { + $buildSubject = [ + 'payment' => $this->paymentDO, + 'amount' => 25 + ]; + + $this->configMock->expects(static::once()) + ->method('isVerify3DSecure') + ->willReturn($verify); + + $this->configMock->expects(static::any()) + ->method('getThresholdAmount') + ->willReturn($thresholdAmount); + + $this->configMock->expects(static::any()) + ->method('get3DSecureSpecificCountries') + ->willReturn($countries); + + $this->billingAddress->expects(static::any()) + ->method('getCountryId') + ->willReturn($countryId); + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($buildSubject) + ->willReturn($this->paymentDO); + $this->subjectReaderMock->expects(self::once()) + ->method('readAmount') + ->with($buildSubject) + ->willReturn(25); + + $result = $this->builder->build($buildSubject); + static::assertEquals($expected, $result); + } + + /** + * Get list of variations for build test + * @return array + */ + public function buildDataProvider() + { + return [ + ['verify' => true, 'amount' => 20, 'countryId' => 'US', 'countries' => [], 'result' => [ + 'options' => [ + 'three_d_secure' => [ + 'required' => true + ] + ] + ]], + ['verify' => true, 'amount' => 0, 'countryId' => 'US', 'countries' => ['US', 'GB'], 'result' => [ + 'options' => [ + 'three_d_secure' => [ + 'required' => true + ] + ] + ]], + ['verify' => true, 'amount' => 40, 'countryId' => 'US', 'countries' => [], 'result' => []], + ['verify' => false, 'amount' => 40, 'countryId' => 'US', 'countries' => [], 'result' => []], + ['verify' => false, 'amount' => 20, 'countryId' => 'US', 'countries' => [], 'result' => []], + ['verify' => true, 'amount' => 20, 'countryId' => 'CA', 'countries' => ['US', 'GB'], 'result' => []], + ]; + } + + /** + * Create mock object for order adapter + */ + private function initOrderMock() + { + $this->billingAddress = $this->getMockBuilder(AddressAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['getCountryId']) + ->getMock(); + + $this->order = $this->getMockBuilder(OrderAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['getBillingAddress']) + ->getMock(); + + $this->order->expects(static::any()) + ->method('getBillingAddress') + ->willReturn($this->billingAddress); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/VaultDataBuilderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/VaultDataBuilderTest.php new file mode 100644 index 0000000000000..ab11cb2bf82c6 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Request/VaultDataBuilderTest.php @@ -0,0 +1,64 @@ +vaultPaymentMock = $this->getMock(VaultPaymentInterface::class); + + $this->builder = new VaultDataBuilder($this->vaultPaymentMock); + } + + public function testBuild() + { + $expectedResult = [ + VaultDataBuilder::OPTIONS => [VaultDataBuilder::STORE_IN_VAULT_ON_SUCCESS => true] + ]; + + $buildSubject = []; + + $this->vaultPaymentMock->expects(self::once()) + ->method('isActiveForPayment') + ->willReturn(true); + + static::assertEquals( + $expectedResult, + $this->builder->build($buildSubject) + ); + } + + public function testBuildWithSwitchedOffVault() + { + $expectedResult = []; + + $buildSubject = []; + + $this->vaultPaymentMock->expects(self::once()) + ->method('isActiveForPayment') + ->willReturn(false); + + static::assertEquals( + $expectedResult, + $this->builder->build($buildSubject) + ); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CaptureDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CaptureDetailsHandlerTest.php new file mode 100644 index 0000000000000..8cbab55ad4652 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CaptureDetailsHandlerTest.php @@ -0,0 +1,91 @@ +payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods(['setIsTransactionClosed']) + ->getMock(); + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->captureHandler = new CaptureDetailsHandler($this->subjectReader); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Response\CaptureDetailsHandler::handle + */ + public function testHandle() + { + $paymentData = $this->getPaymentDataObjectMock(); + $subject['payment'] = $paymentData; + + $this->payment->expects(static::once()) + ->method('setIsTransactionClosed') + ->with(false); + + $response = [ + 'object' => [ + 'success' => true + ] + ]; + + $this->subjectReader->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + + $this->captureHandler->handle($subject, $response); + } + + /** + * Create mock for payment data object and order payment + * @return MockObject + */ + private function getPaymentDataObjectMock() + { + $mock = $this->getMockBuilder(PaymentDataObject::class) + ->setMethods(['getPayment']) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + return $mock; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CardDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CardDetailsHandlerTest.php index 1bdce17368595..5b65be7de10ae 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CardDetailsHandlerTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CardDetailsHandlerTest.php @@ -5,21 +5,19 @@ */ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Response; -use Braintree_Result_Successful; -use Braintree_Transaction; +use Braintree\Result\Successful; +use Braintree\Transaction; use Magento\BraintreeTwo\Gateway\Response\CardDetailsHandler; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Sales\Model\Order\Payment; use Magento\BraintreeTwo\Gateway\Config\Config; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; /** * Class CardDetailsHandlerTest - * @package Magento\BraintreeTwo\Test\Unit\Gateway\Response */ class CardDetailsHandlerTest extends \PHPUnit_Framework_TestCase { - /** * @var \Magento\BraintreeTwo\Gateway\Response\CardDetailsHandler */ @@ -35,12 +33,19 @@ class CardDetailsHandlerTest extends \PHPUnit_Framework_TestCase */ private $config; + /** + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + */ + private $subjectReaderMock; + protected function setUp() { $this->initConfigMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); - $helper = new ObjectManager($this); - $this->cardHandler = $helper->getObject(CardDetailsHandler::class, ['config' => $this->config]); + $this->cardHandler = new CardDetailsHandler($this->config, $this->subjectReaderMock); } /** @@ -49,11 +54,19 @@ protected function setUp() public function testHandle() { $paymentData = $this->getPaymentDataObjectMock(); - $subject['payment'] = $paymentData; - - $response = [ - 'object' => $this->getBraintreeTransaction() - ]; + $transaction = $this->getBraintreeTransaction(); + + $subject = ['payment' => $paymentData]; + $response = ['object' => $transaction]; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + $this->subjectReaderMock->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); $this->payment->expects(static::once()) ->method('setCcLast4'); @@ -122,7 +135,7 @@ private function getPaymentDataObjectMock() /** * Create Braintree transaction - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \Braintree\Transaction */ private function getBraintreeTransaction() { @@ -135,18 +148,8 @@ private function getBraintreeTransaction() 'last4' => 1231 ] ]; - $transaction = Braintree_Transaction::factory($attributes); - - $mock = $this->getMockBuilder(Braintree_Result_Successful::class) - ->disableOriginalConstructor() - ->setMethods(['__get']) - ->getMock(); + $transaction = Transaction::factory($attributes); - $mock->expects(static::once()) - ->method('__get') - ->with('transaction') - ->willReturn($transaction); - - return $mock; + return $transaction; } } diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CloneDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CloneDetailsHandlerTest.php new file mode 100644 index 0000000000000..4c613476ff016 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/CloneDetailsHandlerTest.php @@ -0,0 +1,111 @@ +payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods([ + 'setIsTransactionClosed', 'setTransactionId' + ]) + ->getMock(); + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cloneHandler = new CloneDetailsHandler($this->subjectReader); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Response\CloneDetailsHandler::handle + */ + public function testHandle() + { + $paymentData = $this->getPaymentDataObjectMock(); + $transaction = $this->getBraintreeTransaction(); + $subject['payment'] = $paymentData; + + $this->payment->expects(static::once()) + ->method('setTransactionId') + ->with(self::TRANSACTION_ID); + + $response = ['object' => $transaction]; + + $this->subjectReader->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + $this->subjectReader->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->cloneHandler->handle($subject, $response); + } + + /** + * Create mock for payment data object and order payment + * @return MockObject + */ + private function getPaymentDataObjectMock() + { + $mock = $this->getMockBuilder(PaymentDataObject::class) + ->setMethods(['getPayment']) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + return $mock; + } + + /** + * Create Braintree transaction + * @return MockObject + */ + private function getBraintreeTransaction() + { + $attributes = [ + 'id' => self::TRANSACTION_ID, + ]; + + $transaction = Transaction::factory($attributes); + + return $transaction; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/PaymentDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/PaymentDetailsHandlerTest.php index 7dfd216703d60..138d772141420 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/PaymentDetailsHandlerTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/PaymentDetailsHandlerTest.php @@ -5,16 +5,16 @@ */ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Response; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Braintree\Transaction; use Magento\BraintreeTwo\Gateway\Response\PaymentDetailsHandler; -use Magento\Sales\Model\Order\Payment; use Magento\Payment\Gateway\Data\PaymentDataObject; -use Braintree_Transaction; -use Braintree_Result_Successful; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Payment; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class PaymentDetailsHandlerTest - * @package Magento\BraintreeTwo\Test\Unit\Gateway\Response */ class PaymentDetailsHandlerTest extends \PHPUnit_Framework_TestCase { @@ -26,23 +26,30 @@ class PaymentDetailsHandlerTest extends \PHPUnit_Framework_TestCase private $paymentHandler; /** - * @var \Magento\Sales\Model\Order\Payment|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Sales\Model\Order\Payment|MockObject */ private $payment; - protected function setUp() - { - $helper = new ObjectManager($this); - $this->paymentHandler = $helper->getObject(PaymentDetailsHandler::class); - } - /** - * @covers \Magento\BraintreeTwo\Gateway\Response\PaymentDetailsHandler::handle + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject */ - public function testHandle() + private $subjectReaderMock; + + protected function setUp() { - $paymentData = $this->getPaymentDataObjectMock(); - $subject['payment'] = $paymentData; + $this->payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods([ + 'setTransactionId', + 'setCcTransId', + 'setLastTransId', + 'setAdditionalInformation', + 'setIsTransactionClosed' + ]) + ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); $this->payment->expects(static::once()) ->method('setTransactionId'); @@ -52,33 +59,41 @@ public function testHandle() ->method('setLastTransId'); $this->payment->expects(static::once()) ->method('setIsTransactionClosed'); - $this->payment->expects(static::exactly(6)) + $this->payment->expects(static::any()) ->method('setAdditionalInformation'); - $response = [ - 'object' => $this->getBraintreeTransaction() - ]; + $this->paymentHandler = new PaymentDetailsHandler($this->subjectReaderMock); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Response\PaymentDetailsHandler::handle + */ + public function testHandle() + { + $paymentData = $this->getPaymentDataObjectMock(); + $transaction = $this->getBraintreeTransaction(); + + $subject = ['payment' => $paymentData]; + $response = ['object' => $transaction]; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + $this->subjectReaderMock->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); $this->paymentHandler->handle($subject, $response); } /** * Create mock for payment data object and order payment - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ private function getPaymentDataObjectMock() { - $this->payment = $this->getMockBuilder(Payment::class) - ->disableOriginalConstructor() - ->setMethods([ - 'setTransactionId', - 'setCcTransId', - 'setLastTransId', - 'setAdditionalInformation', - 'setIsTransactionClosed', - ]) - ->getMock(); - $mock = $this->getMockBuilder(PaymentDataObject::class) ->setMethods(['getPayment']) ->disableOriginalConstructor() @@ -93,7 +108,7 @@ private function getPaymentDataObjectMock() /** * Create Braintree transaction - * @return \PHPUnit_Framework_MockObject_MockObject + * @return MockObject */ private function getBraintreeTransaction() { @@ -104,21 +119,11 @@ private function getBraintreeTransaction() 'cvvResponseCode' => 'M', 'processorAuthorizationCode' => 'W1V8XK', 'processorResponseCode' => '1000', - 'processorResponseText' => 'Approved', + 'processorResponseText' => 'Approved' ]; - $transaction = Braintree_Transaction::factory($attributes); - - $mock = $this->getMockBuilder(Braintree_Result_Successful::class) - ->disableOriginalConstructor() - ->setMethods(['__get']) - ->getMock(); - - $mock->expects(static::once()) - ->method('__get') - ->with('transaction') - ->willReturn($transaction); + $transaction = \Braintree\Transaction::factory($attributes); - return $mock; + return $transaction; } } diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/RiskDataHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/RiskDataHandlerTest.php new file mode 100644 index 0000000000000..c4aa2e4e251bb --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/RiskDataHandlerTest.php @@ -0,0 +1,123 @@ +subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->riskDataHandler = new RiskDataHandler($this->subjectReaderMock); + } + + /** + * Run test for handle method + */ + public function testHandle() + { + $paymentData = $this->getPaymentDataObjectMock(); + $transaction = $this->getBraintreeTransactionMock(); + + $response = [ + 'object' => $transaction + ]; + $handlingSubject = [ + 'payment' =>$paymentData, + ]; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($handlingSubject) + ->willReturn($paymentData); + $this->subjectReaderMock->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->riskDataHandler->handle($handlingSubject, $response); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getBraintreeTransactionMock() + { + $transaction = \Braintree\Transaction::factory([]); + $transaction->_set( + 'riskData', + RiskData::factory( + [ + 'id' => 'test-id', + 'decision' => 'test-decision', + ] + ) + ); + + return $transaction; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPaymentDataObjectMock() + { + $mock = $this->getMockBuilder(PaymentDataObjectInterface::class) + ->getMockForAbstractClass(); + + $mock->expects(static::once()) + ->method('getPayment') + ->willReturn($this->getPaymentMock()); + + return $mock; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getPaymentMock() + { + $paymentMock = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + + $paymentMock->expects(self::at(0)) + ->method('setAdditionalInformation') + ->with(RiskDataHandler::RISK_DATA_ID, 'test-id'); + $paymentMock->expects(self::at(1)) + ->method('setAdditionalInformation') + ->with(RiskDataHandler::RISK_DATA_DECISION, 'test-decision'); + + return $paymentMock; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/ThreeDSecureDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/ThreeDSecureDetailsHandlerTest.php new file mode 100644 index 0000000000000..38b1849761ac6 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/ThreeDSecureDetailsHandlerTest.php @@ -0,0 +1,134 @@ +payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods([ + 'unsAdditionalInformation', + 'hasAdditionalInformation', + 'setAdditionalInformation', + ]) + ->getMock(); + + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->handler = new ThreeDSecureDetailsHandler($this->subjectReaderMock); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Response\ThreeDSecureDetailsHandler::handle + */ + public function testHandle() + { + $paymentData = $this->getPaymentDataObjectMock(); + $transaction = $this->getBraintreeTransaction(); + + $subject = ['payment' => $paymentData]; + $response = ['object' => $transaction]; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + $this->subjectReaderMock->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->payment->expects(static::at(1)) + ->method('setAdditionalInformation') + ->with('liabilityShifted', 'Yes'); + $this->payment->expects(static::at(2)) + ->method('setAdditionalInformation') + ->with('liabilityShiftPossible', 'Yes'); + + $this->handler->handle($subject, $response); + } + + /** + * Create mock for payment data object and order payment + * @return MockObject + */ + private function getPaymentDataObjectMock() + { + $mock = $this->getMockBuilder(PaymentDataObject::class) + ->setMethods(['getPayment']) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects(static::once()) + ->method('getPayment') + ->willReturn($this->payment); + + return $mock; + } + + /** + * Create Braintree transaction + * @return MockObject + */ + private function getBraintreeTransaction() + { + $attributes = [ + 'id' => self::TRANSACTION_ID, + 'threeDSecureInfo' => $this->getThreeDSecureInfo() + ]; + + $transaction = Transaction::factory($attributes); + + return $transaction; + } + + /** + * Get 3d secure details + * @return array + */ + private function getThreeDSecureInfo() + { + $attributes = [ + 'liabilityShifted' => 'Yes', + 'liabilityShiftPossible' => 'Yes' + ]; + + return $attributes; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php new file mode 100644 index 0000000000000..423900bdbcc96 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Response/VaultDetailsHandlerTest.php @@ -0,0 +1,255 @@ +paymentTokenMock = $this->getMockBuilder(PaymentToken::class) + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentTokenFactoryMock = $this->getMockBuilder(PaymentTokenFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentTokenFactoryMock->expects(self::once()) + ->method('create') + ->willReturn($this->paymentTokenMock); + + $this->paymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->setMethods(['setVaultPaymentToken', 'getVaultPaymentToken', '__wakeup']) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentExtensionFactoryMock = $this->getMockBuilder(OrderPaymentExtensionFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentExtensionFactoryMock->expects(self::once()) + ->method('create') + ->willReturn($this->paymentExtension); + + // Sales Order Model + $this->salesOrderMock = $this->getMockBuilder(Order::class) + ->setMethods(null) + ->disableOriginalConstructor() + ->getMock(); + + $this->payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getMethod', + 'getOrder' + ]) + ->getMock(); + + $this->payment->expects(self::once()) + ->method('getOrder') + ->willReturn($this->salesOrderMock); + + $this->vaultPaymentMock = $this->getMock(VaultPaymentInterface::class); + + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); + + $mapperArray = [ + "american-express" => "AE", + "discover" => "DI", + "jcb" => "JCB", + "mastercard" => "MC", + "master-card" => "MC", + "visa" => "VI", + "maestro" => "MI", + "diners-club" => "DN", + "unionpay" => "CUP" + ]; + + $this->configMock = $this->getMockBuilder(Config::class) + ->setMethods(['getCctypesMapper']) + ->disableOriginalConstructor() + ->getMock(); + + $this->configMock->expects(self::once()) + ->method('getCctypesMapper') + ->willReturn($mapperArray); + + $this->paymentHandler = new VaultDetailsHandler( + $this->vaultPaymentMock, + $this->paymentTokenFactoryMock, + $this->paymentExtensionFactoryMock, + $this->configMock, + $this->subjectReaderMock + ); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Response\VaultDetailsHandler::handle + */ + public function testHandle() + { + $this->vaultPaymentMock->expects(self::once()) + ->method('isActiveForPayment') + ->willReturn(true); + + $this->paymentExtension->expects(self::once()) + ->method('setVaultPaymentToken') + ->with($this->paymentTokenMock); + $this->paymentExtension->expects(self::once()) + ->method('getVaultPaymentToken') + ->willReturn($this->paymentTokenMock); + + $paymentData = $this->getPaymentDataObjectMock(); + $transaction = $this->getBraintreeTransaction(); + + $subject = ['payment' => $paymentData]; + $response = ['object' => $transaction]; + + $this->subjectReaderMock->expects(self::once()) + ->method('readPayment') + ->with($subject) + ->willReturn($paymentData); + $this->subjectReaderMock->expects(self::once()) + ->method('readTransaction') + ->with($response) + ->willReturn($transaction); + + $this->salesOrderMock->setCustomerId(10); + + $this->paymentHandler->handle($subject, $response); + + $this->assertEquals('rh3gd4', $this->paymentTokenMock->getGatewayToken()); + $this->assertEquals('10', $this->paymentTokenMock->getCustomerId()); + $this->assertSame($this->paymentTokenMock, $this->payment->getExtensionAttributes()->getVaultPaymentToken()); + } + + /** + * Create mock for payment data object and order payment + * @return MockObject + */ + private function getPaymentDataObjectMock() + { + $mock = $this->getMockBuilder(PaymentDataObject::class) + ->setMethods(['getPayment']) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->once()) + ->method('getPayment') + ->willReturn($this->payment); + + return $mock; + } + + /** + * Create Braintree transaction + * @return MockObject + */ + private function getBraintreeTransaction() + { + $attributes = [ + 'id' => self::TRANSACTION_ID, + 'creditCardDetails' => $this->getCreditCardDetails() + ]; + + $transaction = Transaction::factory($attributes); + + return $transaction; + } + + /** + * Create Braintree transaction + * @return \Braintree\Transaction\CreditCardDetails + */ + private function getCreditCardDetails() + { + $attributes = [ + 'token' => 'rh3gd4', + 'bin' => '5421', + 'cardType' => 'American Express', + 'expirationMonth' => 12, + 'expirationYear' => 21, + 'last4' => 1231 + ]; + + $creditCardDetails = new CreditCardDetails($attributes); + + return $creditCardDetails; + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php new file mode 100644 index 0000000000000..b230908cf03b7 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/PaymentNonceResponseValidatorTest.php @@ -0,0 +1,114 @@ +resultInterfaceFactory = $this->getMockBuilder(ResultInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->subjectReader = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->setMethods(['readResponseObject']) + ->getMock(); + + $this->validator = new PaymentNonceResponseValidator( + $this->resultInterfaceFactory, + $this->subjectReader + ); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Validator\PaymentNonceResponseValidator::validate + */ + public function testFailedValidate() + { + $obj = new \stdClass(); + $obj->success = true; + $subject = [ + 'response' => [ + 'object' => $obj + ] + ]; + + $this->subjectReader->expects(static::once()) + ->method('readResponseObject') + ->willReturn($obj); + + $result = $this->getMock(ResultInterface::class); + $this->resultInterfaceFactory->expects(self::once()) + ->method('create') + ->with([ + 'isValid' => false, + 'failsDescription' => ['Payment method nonce can\'t be retrieved.'] + ]) + ->willReturn($result); + + $actual = $this->validator->validate($subject); + static::assertEquals($result, $actual); + } + + /** + * @covers \Magento\BraintreeTwo\Gateway\Validator\PaymentNonceResponseValidator::validatePaymentMethodNonce + */ + public function testValidatePaymentMethodNonce() + { + $obj = new \stdClass(); + $obj->success = true; + $obj->paymentMethodNonce = new \stdClass(); + $obj->paymentMethodNonce->nonce = 'fj2hd9239kd1kq9'; + + $subject = [ + 'response' => [ + 'object' => $obj + ] + ]; + + $this->subjectReader->expects(static::once()) + ->method('readResponseObject') + ->willReturn($obj); + + $result = $this->getMock(ResultInterface::class); + $this->resultInterfaceFactory->expects(self::once()) + ->method('create') + ->with([ + 'isValid' => true, + 'failsDescription' => ['Payment method nonce can\'t be retrieved.'] + ]) + ->willReturn($result); + + $actual = $this->validator->validate($subject); + static::assertEquals($result, $actual); + } +} diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/ResponseValidatorTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/ResponseValidatorTest.php index cfcb03c3d1b35..01e17651063f6 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/ResponseValidatorTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Gateway/Validator/ResponseValidatorTest.php @@ -5,9 +5,11 @@ */ namespace Magento\BraintreeTwo\Test\Unit\Gateway\Validator; +use Braintree\Transaction; use Magento\Payment\Gateway\Validator\ResultInterface; use Magento\Payment\Gateway\Validator\ResultInterfaceFactory; use Magento\BraintreeTwo\Gateway\Validator\ResponseValidator; +use Magento\BraintreeTwo\Gateway\Helper\SubjectReader; /** * Class ResponseValidatorTest @@ -24,6 +26,11 @@ class ResponseValidatorTest extends \PHPUnit_Framework_TestCase */ private $resultInterfaceFactoryMock; + /** + * @var SubjectReader|\PHPUnit_Framework_MockObject_MockObject + */ + private $subjectReaderMock; + /** * Set up * @@ -36,13 +43,18 @@ protected function setUp() )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); + $this->subjectReaderMock = $this->getMockBuilder(SubjectReader::class) + ->disableOriginalConstructor() + ->getMock(); - $this->responseValidator = new ResponseValidator($this->resultInterfaceFactoryMock); + $this->responseValidator = new ResponseValidator( + $this->resultInterfaceFactoryMock, + $this->subjectReaderMock + ); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Response does not exist */ public function testValidateReadResponseException() { @@ -50,12 +62,16 @@ public function testValidateReadResponseException() 'response' => null ]; + $this->subjectReaderMock->expects(self::once()) + ->method('readResponseObject') + ->with($validationSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->responseValidator->validate($validationSubject); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Response object does not exist */ public function testValidateReadResponseObjectException() { @@ -63,10 +79,14 @@ public function testValidateReadResponseObjectException() 'response' => ['object' => null] ]; + $this->subjectReaderMock->expects(self::once()) + ->method('readResponseObject') + ->with($validationSubject) + ->willThrowException(new \InvalidArgumentException()); + $this->responseValidator->validate($validationSubject); } - /** * Run test for validate method * @@ -81,7 +101,12 @@ public function testValidate(array $validationSubject, $isValid) /** @var ResultInterface|\PHPUnit_Framework_MockObject_MockObject $resultMock */ $resultMock = $this->getMock(ResultInterface::class); - $this->resultInterfaceFactoryMock->expects($this->once()) + $this->subjectReaderMock->expects(self::once()) + ->method('readResponseObject') + ->with($validationSubject) + ->willReturn($validationSubject['response']['object']); + + $this->resultInterfaceFactoryMock->expects(self::once()) ->method('create') ->with([ 'isValid' => $isValid, @@ -91,7 +116,7 @@ public function testValidate(array $validationSubject, $isValid) $actualMock = $this->responseValidator->validate($validationSubject); - $this->assertEquals($resultMock, $actualMock); + self::assertEquals($resultMock, $actualMock); } /** @@ -102,7 +127,7 @@ public function dataProviderTestValidate() $successTrue = new \stdClass(); $successTrue->success = true; $successTrue->transaction = new \stdClass(); - $successTrue->transaction->status = \Braintree_Transaction::AUTHORIZED; + $successTrue->transaction->status = Transaction::AUTHORIZED; $successFalse = new \stdClass(); $successFalse->success = false; @@ -110,7 +135,7 @@ public function dataProviderTestValidate() $transactionDeclined = new \stdClass(); $transactionDeclined->success = true; $transactionDeclined->transaction = new \stdClass(); - $transactionDeclined->transaction->status = \Braintree_Transaction::SETTLEMENT_DECLINED; + $transactionDeclined->transaction->status = Transaction::SETTLEMENT_DECLINED; return [ [ diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CcTypeTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CcTypeTest.php index 39b65d1d74c52..3e4335499bee8 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CcTypeTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CcTypeTest.php @@ -11,7 +11,6 @@ /** * Class CcTypeTest - * @package Magento\BraintreeTwo\Test\Unit\Helper */ class CcTypeTest extends \PHPUnit_Framework_TestCase { diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CountryTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CountryTest.php index 01390f9d238d3..a2dcaba980762 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CountryTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Helper/CountryTest.php @@ -12,7 +12,6 @@ /** * Class CountryTest - * @package Magento\BraintreeTwo\Test\Unit\Helper */ class CountryTest extends \PHPUnit_Framework_TestCase { diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Model/Ui/ConfigProviderTest.php index 3fbbc73939233..69c3650430dc8 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -6,6 +6,7 @@ namespace Magento\BraintreeTwo\Test\Unit\Model\Ui; use Magento\BraintreeTwo\Gateway\Config\Config; +use Magento\BraintreeTwo\Model\Adapter\BraintreeAdapter; use Magento\BraintreeTwo\Model\Ui\ConfigProvider; /** @@ -17,16 +18,33 @@ class ConfigProviderTest extends \PHPUnit_Framework_TestCase { const SDK_URL = 'https://js.braintreegateway.com/v2/braintree.js'; + const CLIENT_TOKEN = 'token'; + /** * @var Config|\PHPUnit_Framework_MockObject_MockObject */ - private $configMock; + private $config; + + /** + * @var BraintreeAdapter|\PHPUnit_Framework_MockObject_MockObject + */ + private $braintreeAdapter; + + /** + * @var ConfigProvider + */ + private $configProvider; protected function setUp() { - $this->configMock = $this->getMockBuilder(Config::class) + $this->config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->braintreeAdapter = $this->getMockBuilder(BraintreeAdapter::class) ->disableOriginalConstructor() ->getMock(); + + $this->configProvider = new ConfigProvider($this->config, $this->braintreeAdapter); } /** @@ -38,17 +56,29 @@ protected function setUp() */ public function testGetConfig($config, $expected) { - $configProvider = new ConfigProvider($this->configMock); + $this->braintreeAdapter->expects(static::once()) + ->method('generate') + ->willReturn(self::CLIENT_TOKEN); + foreach ($config as $method => $value) { - $this->configMock->expects(static::once()) + $this->config->expects(static::once()) ->method($method) ->willReturn($value); } - $this->configMock->expects(static::once()) - ->method('getValue') - ->with(Config::KEY_SDK_URL) - ->willReturn(self::SDK_URL); - static::assertEquals($expected, $configProvider->getConfig()); + + static::assertEquals($expected, $this->configProvider->getConfig()); + } + + /** + * @covers \Magento\BraintreeTwo\Model\Ui\ConfigProvider::getClientToken + */ + public function testGetClientToken() + { + $this->braintreeAdapter->expects(static::once()) + ->method('generate') + ->willReturn(self::CLIENT_TOKEN); + + static::assertEquals(self::CLIENT_TOKEN, $this->configProvider->getClientToken()); } /** @@ -59,19 +89,26 @@ public function getConfigDataProvider() return [ [ 'config' => [ - 'getClientToken' => 'token', 'getCcTypesMapper' => ['visa' => 'VI', 'american-express'=> 'AE'], + 'getSdkUrl' => self::SDK_URL, 'getCountrySpecificCardTypeConfig' => [ 'GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB'] ], 'getAvailableCardTypes' => ['AE', 'VI', 'MC', 'DI', 'JCB'], - 'isCvvEnabled' => true + 'isCvvEnabled' => true, + 'isVerify3DSecure' => true, + 'getThresholdAmount' => 20, + 'get3DSecureSpecificCountries' => ['GB', 'US', 'CA'], + 'getEnvironment' => 'test-environment', + 'getKountMerchantId' => 'test-kount-merchant-id', + 'getMerchantId' => 'test-merchant-id', + 'hasFraudProtection' => true ], 'expected' => [ 'payment' => [ ConfigProvider::CODE => [ - 'clientToken' => 'token', + 'clientToken' => self::CLIENT_TOKEN, 'ccTypesMapper' => ['visa' => 'VI', 'american-express' => 'AE'], 'sdkUrl' => self::SDK_URL, 'countrySpecificCardTypes' =>[ @@ -79,7 +116,16 @@ public function getConfigDataProvider() 'US' => ['DI', 'JCB'] ], 'availableCardTypes' => ['AE', 'VI', 'MC', 'DI', 'JCB'], - 'useCvv' => true + 'useCvv' => true, + 'environment' => 'test-environment', + 'kountMerchantId' => 'test-kount-merchant-id', + 'merchantId' => 'test-merchant-id', + 'hasFraudProtection' => true + ], + Config::CODE_3DSECURE => [ + 'enabled' => true, + 'thresholdAmount' => 20, + 'specificCountries' => ['GB', 'US', 'CA'] ] ] ] diff --git a/app/code/Magento/BraintreeTwo/Test/Unit/Observer/DataAssignObserverTest.php b/app/code/Magento/BraintreeTwo/Test/Unit/Observer/DataAssignObserverTest.php index 870b8f1755a49..e11aa39f26abb 100644 --- a/app/code/Magento/BraintreeTwo/Test/Unit/Observer/DataAssignObserverTest.php +++ b/app/code/Magento/BraintreeTwo/Test/Unit/Observer/DataAssignObserverTest.php @@ -8,7 +8,6 @@ use Magento\Framework\DataObject; use Magento\Framework\Event; use Magento\Payment\Model\InfoInterface; -use Magento\Payment\Model\MethodInterface; use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\BraintreeTwo\Observer\DataAssignObserver; @@ -18,6 +17,7 @@ class DataAssignObserverTest extends \PHPUnit_Framework_TestCase { const PAYMENT_METHOD_NONCE = 'nonce'; + const DEVICE_DATA = '{"test": "test"}'; public function testExecute() { @@ -27,11 +27,11 @@ public function testExecute() $event = $this->getMockBuilder(Event::class) ->disableOriginalConstructor() ->getMock(); - $paymentMethodFacade = $this->getMock(MethodInterface::class); $paymentInfoModel = $this->getMock(InfoInterface::class); $dataObject = new DataObject( [ - 'payment_method_nonce' => self::PAYMENT_METHOD_NONCE + 'payment_method_nonce' => self::PAYMENT_METHOD_NONCE, + 'device_data' => self::DEVICE_DATA, ] ); $observerContainer->expects(static::atLeastOnce()) @@ -41,19 +41,17 @@ public function testExecute() ->method('getDataByKey') ->willReturnMap( [ - [AbstractDataAssignObserver::METHOD_CODE, $paymentMethodFacade], + [AbstractDataAssignObserver::MODEL_CODE, $paymentInfoModel], [AbstractDataAssignObserver::DATA_CODE, $dataObject] ] ); - $paymentMethodFacade->expects(static::once()) - ->method('getInfoInstance') - ->willReturn($paymentInfoModel); - $paymentInfoModel->expects(static::once()) + $paymentInfoModel->expects(static::at(0)) ->method('setAdditionalInformation') - ->with( - 'payment_method_nonce', - self::PAYMENT_METHOD_NONCE - ); + ->with('payment_method_nonce', self::PAYMENT_METHOD_NONCE); + $paymentInfoModel->expects(static::at(1)) + ->method('setAdditionalInformation') + ->with('device_data', self::DEVICE_DATA); + $observer = new DataAssignObserver(); $observer->execute($observerContainer); } diff --git a/app/code/Magento/BraintreeTwo/composer.json b/app/code/Magento/BraintreeTwo/composer.json index 9e0c1ab77c89a..4ecded4b14c1a 100644 --- a/app/code/Magento/BraintreeTwo/composer.json +++ b/app/code/Magento/BraintreeTwo/composer.json @@ -5,12 +5,14 @@ "php": "~5.5.0|~5.6.0|~7.0.0", "magento/framework": "*", "magento/magento-composer-installer": "*", + "magento/module-customer": "*", "magento/module-config": "*", "magento/module-directory": "*", "magento/module-payment": "*", "magento/module-checkout": "*", "magento/module-sales": "*", "magento/module-backend": "*", + "magento/module-vault": "*", "braintree/braintree_php": "3.7.0" }, "type": "magento2-module", @@ -26,4 +28,4 @@ "Magento\\BraintreeTwo\\": "" } } -} \ No newline at end of file +} diff --git a/app/code/Magento/BraintreeTwo/etc/adminhtml/di.xml b/app/code/Magento/BraintreeTwo/etc/adminhtml/di.xml index 6b455b63ff8b4..7c39bbbb0af31 100644 --- a/app/code/Magento/BraintreeTwo/etc/adminhtml/di.xml +++ b/app/code/Magento/BraintreeTwo/etc/adminhtml/di.xml @@ -18,4 +18,21 @@ - \ No newline at end of file + + + + + BraintreeTwoFacade + + + + + + + Magento\BraintreeTwo\Gateway\Request\CustomerDataBuilder + Magento\BraintreeTwo\Gateway\Request\PaymentDataBuilder + Magento\BraintreeTwo\Gateway\Request\AddressDataBuilder + + + + diff --git a/app/code/Magento/BraintreeTwo/etc/adminhtml/system.xml b/app/code/Magento/BraintreeTwo/etc/adminhtml/system.xml index 60cecb2c61422..ab1f8d64d75e1 100644 --- a/app/code/Magento/BraintreeTwo/etc/adminhtml/system.xml +++ b/app/code/Magento/BraintreeTwo/etc/adminhtml/system.xml @@ -68,6 +68,20 @@ If you don't specify the merchant account to use to process a transaction, Braintree will process it using your default merchant account. payment/braintreetwo/merchant_account_id + + + Magento\Config\Model\Config\Source\Yesno + Be sure to Enable Advanced Fraud Protection in Your Braintree Account in Settings/Processing Section + payment/braintreetwo/fraud_protection + + + + accounts@braintreepayments.com to setup your Kount account.]]> + + 1 + + payment/braintreetwo/kount_merchant_id + Magento\Config\Model\Config\Source\Yesno @@ -111,6 +125,30 @@ payment/braintreetwo/countrycreditcard + + + Magento\Config\Block\System\Config\Form\Fieldset + + + Magento\Config\Model\Config\Source\Yesno + payment/braintreetwo/verify_3dsecure + + + + payment/braintreetwo/threshold_amount + + + + Magento\Payment\Model\Config\Source\Allspecificcountries + payment/braintreetwo/verify_all_countries + + + + Magento\BraintreeTwo\Model\Adminhtml\System\Config\Country + 1 + payment/braintreetwo/verify_specific_countries + + diff --git a/app/code/Magento/BraintreeTwo/etc/config.xml b/app/code/Magento/BraintreeTwo/etc/config.xml index a0556c40df792..0162d0f79c655 100644 --- a/app/code/Magento/BraintreeTwo/etc/config.xml +++ b/app/code/Magento/BraintreeTwo/etc/config.xml @@ -17,6 +17,7 @@ 1 1 1 + 1 AE,VI,MC,DI,JCB,CUP,DN,MI 1 @@ -24,14 +25,14 @@ processing sandbox 0 - + cvv,number - avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText - cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText + avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision + cc_type,cc_number,avsPostalCodeResponseCode,avsStreetAddressResponseCode,cvvResponseCode,processorAuthorizationCode,processorResponseCode,processorResponseText,liabilityShifted,liabilityShiftPossible,riskDataId,riskDataDecision 1 - \ No newline at end of file + diff --git a/app/code/Magento/BraintreeTwo/etc/di.xml b/app/code/Magento/BraintreeTwo/etc/di.xml index 8b886b6cc5714..3e94c7ccb93b3 100644 --- a/app/code/Magento/BraintreeTwo/etc/di.xml +++ b/app/code/Magento/BraintreeTwo/etc/di.xml @@ -5,7 +5,6 @@ * See COPYING.txt for license details. */ --> - @@ -38,11 +37,20 @@ BraintreeTwoAuthorizeGatewayCommand - BraintreeTwoSaleGatewayCommand + BraintreeTwoSaleGatewayCommand + BraintreeTwoCaptureStrategyGatewayCommand + BraintreeTwoCaptureGatewayCommand + BraintreeTwoCloneGatewayCommand + + + BraintreeTwoCommandPool + + + @@ -59,12 +67,25 @@ Magento\BraintreeTwo\Gateway\Request\CustomerDataBuilder Magento\BraintreeTwo\Gateway\Request\PaymentDataBuilder Magento\BraintreeTwo\Gateway\Request\AddressDataBuilder + Magento\BraintreeTwo\Gateway\Request\VaultDataBuilder + Magento\BraintreeTwo\Gateway\Request\ThreeDSecureDataBuilder + Magento\BraintreeTwo\Gateway\Request\KountPaymentDataBuilder - BraintreeTwoLogger + BraintreeTwoLogger + + + + + BraintreeTwoLogger + + + + + BraintreeTwoLogger @@ -83,6 +104,44 @@ + + + + BraintreeTwoCaptureDataBuilder + Magento\BraintreeTwo\Gateway\Http\TransferFactory + Magento\BraintreeTwo\Gateway\Http\Client\TransactionSubmitForSettlement + Magento\BraintreeTwo\Gateway\Response\CaptureDetailsHandler + Magento\BraintreeTwo\Gateway\Validator\ResponseValidator + + + + + + + Magento\BraintreeTwo\Gateway\Request\CaptureDataBuilder + + + + + + + + BraintreeTwoCloneDataBuilder + Magento\BraintreeTwo\Gateway\Http\TransferFactory + Magento\BraintreeTwo\Gateway\Http\Client\TransactionClone + BraintreeTwoCloneResponseHandler + Magento\BraintreeTwo\Gateway\Validator\ResponseValidator + + + + + + Magento\BraintreeTwo\Gateway\Request\CaptureDataBuilder + Magento\BraintreeTwo\Gateway\Request\SettlementDataBuilder + + + + @@ -101,6 +160,17 @@ Magento\BraintreeTwo\Gateway\Response\PaymentDetailsHandler Magento\BraintreeTwo\Gateway\Response\CardDetailsHandler + Magento\BraintreeTwo\Gateway\Response\RiskDataHandler + Magento\BraintreeTwo\Gateway\Response\VaultDetailsHandler + Magento\BraintreeTwo\Gateway\Response\ThreeDSecureDetailsHandler + + + + + + + Magento\BraintreeTwo\Gateway\Response\CaptureDetailsHandler + Magento\BraintreeTwo\Gateway\Response\CloneDetailsHandler @@ -124,5 +194,11 @@ Magento\BraintreeTwo\Gateway\Config\Config - - \ No newline at end of file + + + + Magento\Vault\Model\PaymentTokenRepository + + + + diff --git a/app/code/Magento/BraintreeTwo/etc/events.xml b/app/code/Magento/BraintreeTwo/etc/events.xml index def66f4b308ed..2123566b73ab6 100644 --- a/app/code/Magento/BraintreeTwo/etc/events.xml +++ b/app/code/Magento/BraintreeTwo/etc/events.xml @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/app/code/Magento/BraintreeTwo/etc/frontend/di.xml b/app/code/Magento/BraintreeTwo/etc/frontend/di.xml index b0238c838bbc0..2ab0103b00e55 100644 --- a/app/code/Magento/BraintreeTwo/etc/frontend/di.xml +++ b/app/code/Magento/BraintreeTwo/etc/frontend/di.xml @@ -27,4 +27,12 @@ - \ No newline at end of file + + + + + Magento\BraintreeTwo\Model\Ui\TokenUiComponentProvider + + + + diff --git a/app/code/Magento/BraintreeTwo/etc/module.xml b/app/code/Magento/BraintreeTwo/etc/module.xml index 26ac385107f85..eecb50c95c45c 100644 --- a/app/code/Magento/BraintreeTwo/etc/module.xml +++ b/app/code/Magento/BraintreeTwo/etc/module.xml @@ -8,8 +8,10 @@ + + - \ No newline at end of file + diff --git a/app/code/Magento/BraintreeTwo/i18n/en_US.csv b/app/code/Magento/BraintreeTwo/i18n/en_US.csv index 098107d193268..0ab3924ae13c9 100644 --- a/app/code/Magento/BraintreeTwo/i18n/en_US.csv +++ b/app/code/Magento/BraintreeTwo/i18n/en_US.csv @@ -5,4 +5,9 @@ "cvvResponseCode","CVV Response Code" "processorAuthorizationCode","Processor Authorization Code" "processorResponseCode","Processor Response Code" -"processorResponseText","Processor Response Text" \ No newline at end of file +"processorResponseText","Processor Response Text" +"braintreetwo", "Braintree" +"liabilityShifted", "Liability Shifted" +"liabilityShiftPossible", "Liability Shift Possible" +"riskDataId", "Risk ID" +"riskDataDecision", "Risk Decision" diff --git a/app/code/Magento/BraintreeTwo/view/adminhtml/templates/form/cc.phtml b/app/code/Magento/BraintreeTwo/view/adminhtml/templates/form/cc.phtml index d62624fd02b0d..8eb77bc890bbe 100644 --- a/app/code/Magento/BraintreeTwo/view/adminhtml/templates/form/cc.phtml +++ b/app/code/Magento/BraintreeTwo/view/adminhtml/templates/form/cc.phtml @@ -39,8 +39,8 @@ $ccType = $block->getInfoData('cc_type');
-
- escapeHtml(__('Please enter valid Credit Card Number')); ?> +
+ escapeHtml(__('Please, enter valid Credit Card Number')); ?>
@@ -57,7 +57,7 @@ $ccType = $block->getInfoData('cc_type');
-
escapeHtml(__('Please enter valid Expiration Date')); ?>
+
escapeHtml(__('Please, enter valid Expiration Date')); ?>
@@ -70,8 +70,8 @@ $ccType = $block->getInfoData('cc_type');
-
- escapeHtml(__('Please enter valid Card Verification Number')); ?> +
+ escapeHtml(__('Please, enter valid Card Verification Number')); ?>
diff --git a/app/code/Magento/BraintreeTwo/view/adminhtml/web/js/braintree.js b/app/code/Magento/BraintreeTwo/view/adminhtml/web/js/braintree.js index 57586aaf41693..4127b417427a7 100644 --- a/app/code/Magento/BraintreeTwo/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/BraintreeTwo/view/adminhtml/web/js/braintree.js @@ -148,9 +148,13 @@ define([ return false; } + // Handle a change in validation or card type + if (event.target.fieldKey === 'number') { + self.selectedCardType(null); + } + // remove previously set classes $cardType.attr('class', 'icon-type'); - self.selectedCardType(null); if (event.card) { $cardType.addClass('icon-type-' + event.card.type); diff --git a/app/code/Magento/BraintreeTwo/view/base/web/js/validator.js b/app/code/Magento/BraintreeTwo/view/base/web/js/validator.js index 084c61795d75a..fb17fba51a1dc 100644 --- a/app/code/Magento/BraintreeTwo/view/base/web/js/validator.js +++ b/app/code/Magento/BraintreeTwo/view/base/web/js/validator.js @@ -30,7 +30,7 @@ define([ /** * Get list of card types - * @returns {exports.defaults.ccTypesMapper|{}|*} + * @returns {Object} */ getCcTypesMapper: function () { return this.config.ccTypesMapper; diff --git a/app/code/Magento/BraintreeTwo/view/frontend/requirejs-config.js b/app/code/Magento/BraintreeTwo/view/frontend/requirejs-config.js new file mode 100644 index 0000000000000..97890fb321fdb --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/requirejs-config.js @@ -0,0 +1,12 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + map: { + '*': { + braintree: 'https://js.braintreegateway.com/js/braintree-2.17.4.min.js' + } + } +}; diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/3d-secure.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/3d-secure.js new file mode 100644 index 0000000000000..917267bf2339c --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/3d-secure.js @@ -0,0 +1,115 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'Magento_BraintreeTwo/js/view/payment/adapter', + 'Magento_Checkout/js/model/quote', + 'mage/translate' +], function ($, braintree, quote, $t) { + 'use strict'; + + return { + config: null, + + /** + * Set 3d secure config + * @param {Object} config + */ + setConfig: function (config) { + this.config = config; + this.config.thresholdAmount = parseFloat(config.thresholdAmount); + }, + + /** + * Get code + * @returns {String} + */ + getCode: function () { + return 'three_d_secure'; + }, + + /** + * Validate Braintree payment nonce + * @param {Object} context + * @returns {Object} + */ + validate: function (context) { + var client = braintree.getApiClient(), + state = $.Deferred(), + totalAmount = quote.totals()['base_grand_total'], + billingAddress = quote.billingAddress(); + + if (!this.isAmountAvailable(totalAmount) || !this.isCountryAvailable(billingAddress.countryId)) { + state.resolve(); + + return state.promise(); + } + + client.verify3DS({ + amount: totalAmount, + creditCard: context.paymentMethodNonce + }, function (error, response) { + var liability; + + if (error) { + state.reject(error.message); + + return; + } + + liability = { + shifted: response.verificationDetails.liabilityShifted, + shiftPossible: response.verificationDetails.liabilityShiftPossible + }; + + if (liability.shifted || !liability.shifted && !liability.shiftPossible) { + context.paymentMethodNonce = response.nonce; + state.resolve(); + } else { + state.reject($t('Please try again with another form of payment.')); + } + }); + + return state.promise(); + }, + + /** + * Check minimal amount for 3d secure activation + * @param {Number} amount + * @returns {Boolean} + */ + isAmountAvailable: function (amount) { + amount = parseFloat(amount); + + return amount >= this.config.thresholdAmount; + }, + + /** + * Check if current country is available for 3d secure + * @param {String} countryId + * @returns {Boolean} + */ + isCountryAvailable: function (countryId) { + var key, + specificCountries = this.config.specificCountries; + + // all countries are available + if (!specificCountries.length) { + return true; + } + + for (key in specificCountries) { + if (countryId === specificCountries[key]) { + return true; + } + } + + return false; + } + }; +}); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/adapter.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/adapter.js new file mode 100644 index 0000000000000..27dd325d96d72 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/adapter.js @@ -0,0 +1,55 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define([ + 'jquery', + 'braintree' +], function ($, braintree) { + 'use strict'; + + return { + apiClient: null, + + /** + * Get Braintree api client + * @returns {Object} + */ + getApiClient: function () { + if (!this.apiClient) { + this.apiClient = new braintree.api.Client({ + clientToken: this.getClientToken() + }); + } + + return this.apiClient; + }, + + /** + * Get Braintree SDK client + * @returns {Object} + */ + getSdkClient: function () { + return braintree; + }, + + /** + * Get payment name + * @returns {String} + */ + getCode: function () { + return 'braintreetwo'; + }, + + /** + * Get client token + * @returns {String|*} + */ + getClientToken: function () { + + return window.checkoutConfig.payment[this.getCode()].clientToken; + } + }; +}); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/braintree.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/braintree.js index 9331298ac2849..b5bed2304ccee 100644 --- a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/braintree.js +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/braintree.js @@ -18,7 +18,7 @@ define( rendererList.push( { type: 'braintreetwo', - component: 'Magento_BraintreeTwo/js/view/payment/method-renderer/braintree' + component: 'Magento_BraintreeTwo/js/view/payment/method-renderer/hosted-fields' } ); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/braintree.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/braintree.js deleted file mode 100644 index ec458a62c616a..0000000000000 --- a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/braintree.js +++ /dev/null @@ -1,284 +0,0 @@ -/** - * Copyright © 2015 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -/*browser:true*/ -/*global define*/ -define( - [ - 'jquery', - 'Magento_Payment/js/view/payment/cc-form', - 'Magento_Ui/js/model/messageList', - 'Magento_Checkout/js/model/quote', - 'mage/translate', - 'Magento_BraintreeTwo/js/validator' - ], - function ( - $, - Component, - globalMessageList, - quote, - $t, - validator - ) { - 'use strict'; - - return Component.extend({ - defaults: { - template: 'Magento_BraintreeTwo/payment/form', - active: false, - scriptLoaded: false, - braintreeClient: null, - paymentMethodNonce: null, - lastBillingAddress: null, - imports: { - onActiveChange: 'active' - } - }, - - /** - * Set list of observable attributes - * @returns {exports.initObservable} - */ - initObservable: function () { - validator.setConfig(window.checkoutConfig.payment[this.getCode()]); - - this._super() - .observe('active scriptLoaded'); - - return this; - }, - - /** - * Get payment name - * @returns {String} - */ - getCode: function () { - return 'braintreetwo'; - }, - - /** - * Get full selector name - * @param {String} field - * @returns {String} - */ - getSelector: function (field) { - return '#' + this.getCode() + '_' + field; - }, - - /** - * Check if payment is active - * @returns {Boolean} - */ - isActive: function () { - var active = this.getCode() === this.isChecked(); - - this.active(active); - - return active; - }, - - /** - * Triggers on payment change - * @param {Boolean} isActive - */ - onActiveChange: function (isActive) { - if (!isActive) { - return; - } - - if (this.getClientToken()) { - if (!this.scriptLoaded()) { - this.loadScript(); - } - } else { - globalMessageList.addErrorMessage({ - 'message': $t('Sorry, but something went wrong') - }); - } - }, - - /** - * Load Braintree SDK - */ - loadScript: function () { - var state = this.scriptLoaded, - self = this; - - $('body').trigger('processStart'); - require([this.getSdkUrl()], function (braintree) { - state(true); - self.braintreeClient = braintree; - self.initBraintree(); - $('body').trigger('processStop'); - }); - }, - - /** - * Init Braintree client - */ - initBraintree: function () { - var self = this, - fields = { - number: { - selector: self.getSelector('cc_number') - }, - expirationMonth: { - selector: self.getSelector('expirationMonth'), - placeholder: $t('MM') - }, - expirationYear: { - selector: self.getSelector('expirationYear'), - placeholder: $t('YY') - }, - - /** - * Triggers on Hosted Field changes - * @param {Object} event - * @returns {Boolean} - */ - onFieldEvent: function (event) { - if (event.isEmpty === false) { - self.validateCardType(); - } - - if (event.type === 'fieldStateChange') { - // Handle a change in validation or card type - self.selectedCardType(null); - - if (!event.isPotentiallyValid && !event.isValid) { - return false; - } - - if (event.card) { - self.selectedCardType( - validator.getMageCardType(event.card.type, self.getCcAvailableTypes()) - ); - } - } - } - }; - - if (self.hasVerification()) { - fields.cvv = { - selector: self.getSelector('cc_cid') - }; - } - - this.braintreeClient.setup(this.getClientToken(), 'custom', { - id: 'co-transparent-form-braintree', - hostedFields: fields, - - /** - * Triggers on payment nonce receive - * @param {Object} response - */ - onPaymentMethodReceived: function (response) { - self.paymentMethodNonce = response.nonce; - self.placeOrder(); - }, - - /** - * Triggers on any Braintree error - * @param {Object} response - */ - onError: function (response) { - self.messageContainer.addErrorMessage({ - 'message': response.message - }); - } - }); - }, - - /** - * Validate current credit card type - * @returns {Boolean} - */ - validateCardType: function () { - var $selector = $(this.getSelector('cc_number')), - invalidClass = 'braintree-hosted-fields-invalid'; - - $selector.removeClass(invalidClass); - - if (this.selectedCardType() === null) { - $(this.getSelector('cc_number')).addClass('class', invalidClass); - - return false; - } - - return true; - }, - - /** - * Get url of Braintree SDK - * @returns {String} - */ - getSdkUrl: function () { - - return window.checkoutConfig.payment[this.getCode()].sdkUrl; - }, - - /** - * Get client token - * @returns {String|*} - */ - getClientToken: function () { - - return window.checkoutConfig.payment[this.getCode()].clientToken; - }, - - /** - * Get list of available CC types - */ - getCcAvailableTypes: function () { - var availableTypes = validator.getAvailableCardTypes(), - billingAddress = quote.billingAddress(), - billingCountryId; - - this.lastBillingAddress = quote.shippingAddress(); - - if (!billingAddress) { - billingAddress = this.lastBillingAddress; - } - - billingCountryId = billingAddress.countryId; - - if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { - - return validator.collectTypes( - availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) - ); - } - - return availableTypes; - }, - - /** - * Get data - * @returns {Object} - */ - getData: function () { - return { - 'method': this.item.method, - 'additional_data': { - 'payment_method_nonce': this.paymentMethodNonce - } - }; - }, - - /** - * Trigger order placing - */ - placeOrderClick: function () { - if (this.validateCardType()) { - $(this.getSelector('submit')).trigger('click'); - } else { - this.messageContainer.addErrorMessage({ - 'message': $t('Please enter a valid credit card type number') - }); - } - } - - }); - } -); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/cc-form.js new file mode 100644 index 0000000000000..a55247593b940 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -0,0 +1,304 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define( + [ + 'underscore', + 'jquery', + 'Magento_Payment/js/view/payment/cc-form', + 'Magento_Checkout/js/model/quote', + 'Magento_BraintreeTwo/js/view/payment/adapter', + 'Magento_Ui/js/model/messageList', + 'mage/translate', + 'Magento_BraintreeTwo/js/validator', + 'Magento_BraintreeTwo/js/view/payment/validator-handler' + ], + function ( + _, + $, + Component, + quote, + braintree, + globalMessageList, + $t, + validator, + validatorManager + ) { + 'use strict'; + + return Component.extend({ + defaults: { + active: false, + isInitialized: false, + braintreeClient: null, + braintreeDeviceData: null, + paymentMethodNonce: null, + lastBillingAddress: null, + validatorManager: validatorManager, + + /** + * Additional payment data + * + * {Object} + */ + additionalData: {}, + + /** + * {String} + */ + integration: 'custom', + + /** + * Braintree client configuration + * + * {Object} + */ + clientConfig: { + + /** + * Triggers on payment nonce receive + * + * @param {Object} response + */ + onPaymentMethodReceived: function (response) { + this.paymentMethodNonce = response.nonce; + this.placeOrder(); + }, + + /** + * Triggers on any Braintree error + */ + onError: function () { + this.paymentMethodNonce = ''; + } + } + }, + + /** + * Init config + */ + initClientConfig: function () { + // Advanced fraud tools settings + if (this.hasFraudProtection()) { + this.clientConfig = _.extend(this.clientConfig, this.kountConfig()); + } + + _.each(this.clientConfig, function (fn, name) { + if (typeof fn === 'function') { + this.clientConfig[name] = fn.bind(this); + } + }, this); + }, + + /** + * @returns {Object} + */ + kountConfig: function () { + var config = { + dataCollector: { + kount: { + environment: this.getEnvironment() + } + }, + + /** + * Device data initialization + * + * @param {Object} braintreeInstance + */ + onReady: function (braintreeInstance) { + this.additionalData['device_data'] = braintreeInstance.deviceData; + } + }; + + if (this.getKountMerchantId()) { + config.dataCollector.kount.merchantId = this.getKountMerchantId(); + } + + return config; + }, + + /** + * Set list of observable attributes + * + * @returns {exports.initObservable} + */ + initObservable: function () { + validator.setConfig(window.checkoutConfig.payment[this.getCode()]); + this._super() + .observe(['active', 'isInitialized']); + this.validatorManager.initialize(); + this.braintreeClient = braintree; + this.initBraintree(); + + return this; + }, + + /** + * Get payment name + * + * @returns {String} + */ + getCode: function () { + return 'braintreetwo'; + }, + + /** + * Check if payment is active + * + * @returns {Boolean} + */ + isActive: function () { + var active = this.getCode() === this.isChecked(); + + this.active(active); + + return active; + }, + + /** + * Init Braintree handlers + */ + initBraintree: function () { + if (!this.braintreeClient.getClientToken()) { + this.showError($t('Sorry, but something went wrong.')); + } + + if (!this.isInitialized()) { + this.isInitialized(true); + this.initClient(); + } + }, + + /** + * Init Braintree client + */ + initClient: function () { + this.initClientConfig(); + this.braintreeClient.getSdkClient().setup( + this.braintreeClient.getClientToken(), + this.integration, + this.clientConfig + ); + }, + + /** + * Show error message + * + * @param {String} errorMessage + */ + showError: function (errorMessage) { + globalMessageList.addErrorMessage({ + message: errorMessage + }); + }, + + /** + * Get full selector name + * + * @param {String} field + * @returns {String} + */ + getSelector: function (field) { + return '#' + this.getCode() + '_' + field; + }, + + /** + * Get list of available CC types + * + * @returns {Object} + */ + getCcAvailableTypes: function () { + var availableTypes = validator.getAvailableCardTypes(), + billingAddress = quote.billingAddress(), + billingCountryId; + + this.lastBillingAddress = quote.shippingAddress(); + + if (!billingAddress) { + billingAddress = this.lastBillingAddress; + } + + billingCountryId = billingAddress.countryId; + + if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { + + return validator.collectTypes( + availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) + ); + } + + return availableTypes; + }, + + /** + * @returns {Boolean} + */ + hasFraudProtection: function () { + return window.checkoutConfig.payment[this.getCode()].hasFraudProtection; + }, + + /** + * @returns {String} + */ + getEnvironment: function () { + return window.checkoutConfig.payment[this.getCode()].environment; + }, + + /** + * @returns {String} + */ + getKountMerchantId: function () { + return window.checkoutConfig.payment[this.getCode()].kountMerchantId; + }, + + /** + * Get data + * + * @returns {Object} + */ + getData: function () { + var data = { + 'method': this.getCode(), + 'additional_data': { + 'payment_method_nonce': this.paymentMethodNonce + } + }; + + data['additional_data'] = _.extend(data['additional_data'], this.additionalData); + + return data; + }, + + /** + * Set payment nonce + * @param {String} paymentMethodNonce + */ + setPaymentMethodNonce: function (paymentMethodNonce) { + this.paymentMethodNonce = paymentMethodNonce; + }, + + /** + * Action to place order + * + * @param {String} key + */ + placeOrder: function (key) { + var self = this; + + if (key) { + return self._super(); + } + // place order on success validation + self.validatorManager.validate(self, function () { + return self.placeOrder('parent'); + }); + + return false; + } + }); + } +); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js new file mode 100644 index 0000000000000..2ac97155a279f --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js @@ -0,0 +1,157 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'Magento_BraintreeTwo/js/view/payment/method-renderer/cc-form', + 'Magento_BraintreeTwo/js/validator', + 'Magento_Vault/js/view/payment/vault-enabler', + 'mage/translate' +], function ($, Component, validator, vaultEnabler, $t) { + 'use strict'; + + return Component.extend({ + + defaults: { + template: 'Magento_BraintreeTwo/payment/form', + clientConfig: { + + /** + * {String} + */ + id: 'co-transparent-form-braintree' + } + }, + + /** + * @returns {exports.initialize} + */ + initialize: function () { + this._super(); + this.vaultEnabler = vaultEnabler(); + this.vaultEnabler.setPaymentCode(this.getCode()); + + return this; + }, + + /** + * Init config + */ + initClientConfig: function () { + this._super(); + + // Hosted fields settings + this.clientConfig.hostedFields = this.getHostedFields(); + }, + + /** + * @returns {Object} + */ + getData: function () { + var data = this._super(); + + this.vaultEnabler.visitAdditionalData(data); + + return data; + }, + + /** + * @returns {Bool} + */ + isVaultEnabled: function () { + return this.vaultEnabler.isVaultEnabled(); + }, + + /** + * Get Braintree Hosted Fields + * @returns {Object} + */ + getHostedFields: function () { + var self = this, + fields = { + number: { + selector: self.getSelector('cc_number') + }, + expirationMonth: { + selector: self.getSelector('expirationMonth'), + placeholder: $t('MM') + }, + expirationYear: { + selector: self.getSelector('expirationYear'), + placeholder: $t('YY') + } + }; + + if (self.hasVerification()) { + fields.cvv = { + selector: self.getSelector('cc_cid') + }; + } + + /** + * Triggers on Hosted Field changes + * @param {Object} event + * @returns {Boolean} + */ + fields.onFieldEvent = function (event) { + if (event.isEmpty === false) { + self.validateCardType(); + } + + if (event.type !== 'fieldStateChange') { + + return false; + } + + // Handle a change in validation or card type + if (event.target.fieldKey === 'number') { + self.selectedCardType(null); + } + + if (!event.isValid) { + return false; + } + + if (event.card) { + self.selectedCardType( + validator.getMageCardType(event.card.type, self.getCcAvailableTypes()) + ); + } + }; + + return fields; + }, + + /** + * Validate current credit card type + * @returns {Boolean} + */ + validateCardType: function () { + var $selector = $(this.getSelector('cc_number')), + invalidClass = 'braintree-hosted-fields-invalid'; + + $selector.removeClass(invalidClass); + + if (this.selectedCardType() === null) { + $(this.getSelector('cc_number')).addClass(invalidClass); + + return false; + } + + return true; + }, + + /** + * Trigger order placing + */ + placeOrderClick: function () { + if (this.validateCardType()) { + $(this.getSelector('submit')).trigger('click'); + } + } + }); +}); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/vault.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/vault.js new file mode 100644 index 0000000000000..f9efa111ed500 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/method-renderer/vault.js @@ -0,0 +1,98 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define([ + 'jquery', + 'Magento_BraintreeTwo/js/view/payment/method-renderer/cc-form', + 'Magento_Vault/js/view/payment/method-renderer/vault', + 'Magento_Ui/js/model/messageList', + 'Magento_Checkout/js/model/full-screen-loader' +], function ($, Component, VaultComponent, globalMessageList, fullScreenLoader) { + 'use strict'; + + return VaultComponent.extend({ + defaults: { + template: 'Magento_BraintreeTwo/payment/vault', + modules: { + hostedFields: '${ $.parentName }.braintreetwo' + } + }, + + /** + * Get current Braintree vault id + * @returns {String} + */ + getId: function () { + return 'braintreetwo_' + this.index; + }, + + /** + * Get name of scope + * @returns {String} + */ + getScopeName: function () { + return this.parentName + '.braintreetwo'; + }, + + /** + * Get last 4 digits of card + * @returns {String} + */ + getMaskedCard: function () { + return this.details.maskedCC; + }, + + /** + * Get expiration date + * @returns {String} + */ + getExpirationDate: function () { + return this.details.expirationDate; + }, + + /** + * Get card type + * @returns {String} + */ + getCardType: function () { + return this.details.type; + }, + + /** + * Place order + */ + placeOrder: function () { + this.getPaymentMethodNonce(); + }, + + /** + * Send request to get payment method nonce + */ + getPaymentMethodNonce: function () { + var self = this; + + fullScreenLoader.startLoader(); + $.get(self.nonceUrl, { + 'public_hash': self.publicHash + }) + .done(function (response) { + fullScreenLoader.stopLoader(); + self.hostedFields(function (formComponent) { + formComponent.setPaymentMethodNonce(response.paymentMethodNonce); + formComponent.placeOrder(); + }); + }) + .fail(function (response) { + var error = JSON.parse(response.responseText); + + fullScreenLoader.stopLoader(); + globalMessageList.addErrorMessage({ + message: error.message + }); + }); + } + }); +}); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/validator-handler.js b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/validator-handler.js new file mode 100644 index 0000000000000..f3c9c89ed1ed2 --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/js/view/payment/validator-handler.js @@ -0,0 +1,85 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'Magento_Ui/js/model/messageList', + 'Magento_BraintreeTwo/js/view/payment/3d-secure' +], function ($, globalMessageList, verify3DSecure) { + 'use strict'; + + return { + validators: [], + + /** + * Get payment config + * @returns {Object} + */ + getConfig: function () { + return window.checkoutConfig.payment; + }, + + /** + * Init list of validators + */ + initialize: function () { + var config = this.getConfig(); + + if (config[verify3DSecure.getCode()].enabled) { + verify3DSecure.setConfig(config[verify3DSecure.getCode()]); + this.add(verify3DSecure); + } + }, + + /** + * Add new validator + * @param {Object} validator + */ + add: function (validator) { + this.validators.push(validator); + }, + + /** + * Run pull of validators + * @param {Object} context + * @param {Function} callback + */ + validate: function (context, callback) { + var self = this, + deferred; + + // no available validators + if (!self.validators.length) { + callback(); + + return; + } + + // get list of deferred validators + deferred = $.map(self.validators, function (current) { + return current.validate(context); + }); + + $.when.apply($, deferred) + .done(function () { + callback(); + }).fail(function (error) { + self.showError(error); + }); + }, + + /** + * Show error message + * @param {String} errorMessage + */ + showError: function (errorMessage) { + globalMessageList.addErrorMessage({ + message: errorMessage + }); + } + }; +}); diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/form.html b/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/form.html index 1531ceb73f23e..5e5046412c71e 100644 --- a/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/form.html @@ -4,12 +4,12 @@ * See COPYING.txt for license details. */ --> -
+
+ name="payment[method]" + class="radio" + data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/> @@ -34,15 +34,15 @@
  • + _active: $parent.selectedCardType() == item, + _inactive: $parent.selectedCardType() != null && $parent.selectedCardType() != item + } "> + 'src': $parent.getIcons(item).url, + 'width': $parent.getIcons(item).width, + 'height': $parent.getIcons(item).height + }">
  • @@ -52,8 +52,8 @@ class="input-text" value="" data-bind="attr: {id: getCode() + '_cc_type', 'data-container': getCode() + '-cc-type'}, - value: creditCardType - "> + value: creditCardType + ">
@@ -62,7 +62,7 @@
-
+
@@ -77,7 +77,7 @@
-
+
@@ -88,7 +88,7 @@
-
+
+ +
+ + +
+ @@ -114,7 +125,7 @@ data-bind=" click: placeOrderClick, attr: {title: $t('Place Order')} - "> + ">
diff --git a/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/vault.html b/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/vault.html new file mode 100644 index 0000000000000..2dad98628932c --- /dev/null +++ b/app/code/Magento/BraintreeTwo/view/frontend/web/template/payment/vault.html @@ -0,0 +1,50 @@ + +
+
+ + +
+ +
+ + +
+
+ +
+
+
+
\ No newline at end of file diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php index 95bd7ed91cecc..257e937ab6886 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php +++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php @@ -574,8 +574,12 @@ protected function _prepareOptions(\Magento\Framework\DataObject $buyRequest, $p { $transport = new \StdClass(); $transport->options = []; + $options = null; if ($product->getHasOptions()) { - foreach ($product->getOptions() as $option) { + $options = $product->getOptions(); + } + if ($options !== null) { + foreach ($options as $option) { /* @var $option \Magento\Catalog\Model\Product\Option */ $group = $option->groupFactory($option->getType()) ->setOption($option) diff --git a/app/code/Magento/CatalogRule/Api/CatalogRuleRepositoryInterface.php b/app/code/Magento/CatalogRule/Api/CatalogRuleRepositoryInterface.php new file mode 100644 index 0000000000000..88a716ecd25b4 --- /dev/null +++ b/app/code/Magento/CatalogRule/Api/CatalogRuleRepositoryInterface.php @@ -0,0 +1,41 @@ +getRequest()->getParam('id'); if ($id) { try { - /** @var \Magento\CatalogRule\Model\Rule $model */ - $model = $this->_objectManager->create('Magento\CatalogRule\Model\Rule'); - $model->load($id); - $model->delete(); + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $this->_objectManager->create( + 'Magento\CatalogRule\Api\CatalogRuleRepositoryInterface' + ); + $ruleRepository->deleteById($id); + $this->_objectManager->create('Magento\CatalogRule\Model\Flag')->loadSelf()->setState(1)->save(); $this->messageManager->addSuccess(__('You deleted the rule.')); $this->_redirect('catalog_rule/*/'); diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php index 97495f7d0d48f..82b356da5884b 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Edit.php @@ -14,11 +14,19 @@ class Edit extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog public function execute() { $id = $this->getRequest()->getParam('id'); + + /** @var \Magento\CatalogRule\Model\Rule $model */ $model = $this->_objectManager->create('Magento\CatalogRule\Model\Rule'); + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $this->_objectManager->create( + 'Magento\CatalogRule\Api\CatalogRuleRepositoryInterface' + ); + if ($id) { - $model->load($id); - if (!$model->getRuleId()) { + try { + $model = $ruleRepository->get($id); + } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { $this->messageManager->addError(__('This rule no longer exists.')); $this->_redirect('catalog_rule/*'); return; @@ -39,12 +47,9 @@ public function execute() $this->_view->getPage()->getConfig()->getTitle()->prepend( $model->getRuleId() ? $model->getName() : __('New Catalog Price Rule') ); - $this->_view->getLayout()->getBlock( - 'promo_catalog_edit' - )->setData( - 'action', - $this->getUrl('catalog_rule/promo_catalog/save') - ); + $this->_view->getLayout() + ->getBlock('promo_catalog_edit') + ->setData('action', $this->getUrl('catalog_rule/promo_catalog/save')); $breadcrumb = $id ? __('Edit Rule') : __('New Rule'); $this->_addBreadcrumb($breadcrumb, $breadcrumb); diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewActionHtml.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewActionHtml.php index 2a1d8aaf878d0..a1ec19988911e 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewActionHtml.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewActionHtml.php @@ -19,17 +19,12 @@ public function execute() $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type'))); $type = $typeArr[0]; - $model = $this->_objectManager->create( - $type - )->setId( - $id - )->setType( - $type - )->setRule( - $this->_objectManager->create('Magento\CatalogRule\Model\Rule') - )->setPrefix( - 'actions' - ); + $model = $this->_objectManager->create($type) + ->setId($id) + ->setType($type) + ->setRule($this->_objectManager->create('Magento\CatalogRule\Model\Rule')) + ->setPrefix('actions'); + if (!empty($typeArr[1])) { $model->setAttribute($typeArr[1]); } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewConditionHtml.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewConditionHtml.php index 977806d176b76..50b10d6b51fb0 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewConditionHtml.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/NewConditionHtml.php @@ -19,17 +19,12 @@ public function execute() $typeArr = explode('|', str_replace('-', '/', $this->getRequest()->getParam('type'))); $type = $typeArr[0]; - $model = $this->_objectManager->create( - $type - )->setId( - $id - )->setType( - $type - )->setRule( - $this->_objectManager->create('Magento\CatalogRule\Model\Rule') - )->setPrefix( - 'conditions' - ); + $model = $this->_objectManager->create($type) + ->setId($id) + ->setType($type) + ->setRule($this->_objectManager->create('Magento\CatalogRule\Model\Rule')) + ->setPrefix('conditions'); + if (!empty($typeArr[1])) { $model->setAttribute($typeArr[1]); } diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 16415977b5582..6aaf1216d3e18 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -17,9 +17,16 @@ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog public function execute() { if ($this->getRequest()->getPostValue()) { + + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $this->_objectManager->create( + 'Magento\CatalogRule\Api\CatalogRuleRepositoryInterface' + ); + + /** @var \Magento\CatalogRule\Model\Rule $model */ + $model = $this->_objectManager->create('Magento\CatalogRule\Model\Rule'); + try { - /** @var \Magento\CatalogRule\Model\Rule $model */ - $model = $this->_objectManager->create('Magento\CatalogRule\Model\Rule'); $this->_eventManager->dispatch( 'adminhtml_controller_catalogrule_prepare_save', ['request' => $this->getRequest()] @@ -33,10 +40,7 @@ public function execute() $data = $inputFilter->getUnescaped(); $id = $this->getRequest()->getParam('rule_id'); if ($id) { - $model->load($id); - if ($id != $model->getId()) { - throw new LocalizedException(__('Wrong rule specified.')); - } + $model = $ruleRepository->get($id); } $validateResult = $model->validateData(new \Magento\Framework\DataObject($data)); @@ -55,8 +59,7 @@ public function execute() $model->loadPost($data); $this->_objectManager->get('Magento\Backend\Model\Session')->setPageData($model->getData()); - - $model->save(); + $ruleRepository->save($model); $this->messageManager->addSuccess(__('You saved the rule.')); $this->_objectManager->get('Magento\Backend\Model\Session')->setPageData(false); @@ -85,7 +88,7 @@ public function execute() __('Something went wrong while saving the rule data. Please review the error log.') ); $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e); - $this->_objectManager->get('Magento\Backend\Model\Session')->setPageData($data); + $this->_objectManager->get('Magento\Backend\Model\Session')->setPageData($model->getData()); $this->_redirect('catalog_rule/*/edit', ['id' => $this->getRequest()->getParam('rule_id')]); return; } diff --git a/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php b/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php new file mode 100644 index 0000000000000..2c869a0661d8b --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/CatalogRuleRepository.php @@ -0,0 +1,102 @@ +ruleResource = $ruleResource; + $this->ruleFactory = $ruleFactory; + } + + /** + * {@inheritdoc} + */ + public function save(Data\RuleInterface $rule) + { + if ($rule->getRuleId()) { + $rule = $this->get($rule->getRuleId())->addData($rule->getData()); + } + + try { + $this->ruleResource->save($rule); + unset($this->rules[$rule->getId()]); + } catch (\Exception $e) { + throw new CouldNotSaveException(__('Unable to save rule %1', $rule->getRuleId())); + } + return $rule; + } + + /** + * {@inheritdoc} + */ + public function get($ruleId) + { + if (!isset($this->rules[$ruleId])) { + /** @var \Magento\CatalogRule\Model\RuleFactory $rule */ + $rule = $this->ruleFactory->create(); + + /* TODO: change to resource model after entity manager will be fixed */ + $rule->load($ruleId); + if (!$rule->getRuleId()) { + throw new NoSuchEntityException(__('Rule with specified ID "%1" not found.', $ruleId)); + } + $this->rules[$ruleId] = $rule; + } + return $this->rules[$ruleId]; + } + + /** + * {@inheritdoc} + */ + public function delete(Data\RuleInterface $rule) + { + try { + $this->ruleResource->delete($rule); + unset($this->rules[$rule->getId()]); + } catch (\Exception $e) { + throw new CouldNotDeleteException(__('Unable to remove rule %1', $rule->getRuleId())); + } + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteById($ruleId) + { + $model = $this->get($ruleId); + $this->delete($model); + return true; + } +} diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/ReadHandler.php b/app/code/Magento/CatalogRule/Model/ResourceModel/ReadHandler.php new file mode 100644 index 0000000000000..f38a0158a71aa --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/ReadHandler.php @@ -0,0 +1,51 @@ +ruleResource = $ruleResource; + $this->metadataPool = $metadataPool; + } + + /** + * @param string $entityType + * @param array $entityData + * @return array + * @throws \Exception + */ + public function execute($entityType, $entityData) + { + $linkField = $this->metadataPool->getMetadata($entityType)->getLinkField(); + $entityId = $entityData[$linkField]; + + $entityData['customer_group_ids'] = $this->ruleResource->getCustomerGroupIds($entityId); + $entityData['website_ids'] = $this->ruleResource->getWebsiteIds($entityId); + + return $entityData; + } +} diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Rule.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Rule.php index 7a0e2cec1f24c..b4e5a2f544ee6 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Rule.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Rule.php @@ -30,24 +30,6 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource */ protected $_logger; - /** - * Store associated with rule entities information map - * - * @var array - */ - protected $_associatedEntitiesMap = [ - 'website' => [ - 'associations_table' => 'catalogrule_website', - 'rule_id_field' => 'rule_id', - 'entity_id_field' => 'website_id', - ], - 'customer_group' => [ - 'associations_table' => 'catalogrule_customer_group', - 'rule_id_field' => 'rule_id', - 'entity_id_field' => 'customer_group_id', - ], - ]; - /** * Catalog rule data * @@ -93,6 +75,12 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource protected $priceCurrency; /** + * @var \Magento\Framework\Model\EntityManager + */ + protected $entityManager; + + /** + * Rule constructor. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param Product\ConditionFactory $conditionFactory @@ -103,7 +91,9 @@ class Rule extends \Magento\Rule\Model\ResourceModel\AbstractResource * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param PriceCurrencyInterface $priceCurrency - * @param string $connectionName + * @param \Magento\Framework\Model\EntityManager $entityManager + * @param array $associatedEntitiesMap + * @param null $connectionName * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -117,6 +107,8 @@ public function __construct( \Psr\Log\LoggerInterface $logger, \Magento\Framework\Stdlib\DateTime $dateTime, PriceCurrencyInterface $priceCurrency, + \Magento\Framework\Model\EntityManager $entityManager, + array $associatedEntitiesMap = [], $connectionName = null ) { $this->_storeManager = $storeManager; @@ -128,6 +120,8 @@ public function __construct( $this->_logger = $logger; $this->dateTime = $dateTime; $this->priceCurrency = $priceCurrency; + $this->entityManager = $entityManager; + $this->_associatedEntitiesMap = $associatedEntitiesMap; parent::__construct($context, $connectionName); } @@ -142,49 +136,6 @@ protected function _construct() $this->_init('catalogrule', 'rule_id'); } - /** - * Add customer group ids and website ids to rule data after load - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb - */ - protected function _afterLoad(AbstractModel $object) - { - $object->setData('customer_group_ids', (array)$this->getCustomerGroupIds($object->getId())); - $object->setData('website_ids', (array)$this->getWebsiteIds($object->getId())); - - return parent::_afterLoad($object); - } - - /** - * Bind catalog rule to customer group(s) and website(s). - * Update products which are matched for rule. - * - * @param AbstractModel $object - * @return $this - */ - protected function _afterSave(AbstractModel $object) - { - if ($object->hasWebsiteIds()) { - $websiteIds = $object->getWebsiteIds(); - if (!is_array($websiteIds)) { - $websiteIds = explode(',', (string)$websiteIds); - } - $this->bindRuleToEntity($object->getId(), $websiteIds, 'website'); - } - - if ($object->hasCustomerGroupIds()) { - $customerGroupIds = $object->getCustomerGroupIds(); - if (!is_array($customerGroupIds)) { - $customerGroupIds = explode(',', (string)$customerGroupIds); - } - $this->bindRuleToEntity($object->getId(), $customerGroupIds, 'customer_group'); - } - - parent::_afterSave($object); - return $this; - } - /** * @param \Magento\Framework\Model\AbstractModel $rule * @return $this @@ -196,10 +147,6 @@ protected function _afterDelete(\Magento\Framework\Model\AbstractModel $rule) $this->getTable('catalogrule_product'), ['rule_id=?' => $rule->getId()] ); - $connection->delete( - $this->getTable('catalogrule_customer_group'), - ['rule_id=?' => $rule->getId()] - ); $connection->delete( $this->getTable('catalogrule_group_website'), ['rule_id=?' => $rule->getId()] @@ -240,22 +187,13 @@ public function getRulePrice($date, $wId, $gId, $pId) public function getRulePrices(\DateTime $date, $websiteId, $customerGroupId, $productIds) { $connection = $this->getConnection(); - $select = $connection->select()->from( - $this->getTable('catalogrule_product_price'), - ['product_id', 'rule_price'] - )->where( - 'rule_date = ?', - $date->format('Y-m-d') - )->where( - 'website_id = ?', - $websiteId - )->where( - 'customer_group_id = ?', - $customerGroupId - )->where( - 'product_id IN(?)', - $productIds - ); + $select = $connection->select() + ->from($this->getTable('catalogrule_product_price'), ['product_id', 'rule_price']) + ->where('rule_date = ?', $date->format('Y-m-d')) + ->where('website_id = ?', $websiteId) + ->where('customer_group_id = ?', $customerGroupId) + ->where('product_id IN(?)', $productIds); + return $connection->fetchPairs($select); } @@ -274,25 +212,77 @@ public function getRulesFromProduct($date, $websiteId, $customerGroupId, $produc if (is_string($date)) { $date = strtotime($date); } - $select = $connection->select()->from( - $this->getTable('catalogrule_product') - )->where( - 'website_id = ?', - $websiteId - )->where( - 'customer_group_id = ?', - $customerGroupId - )->where( - 'product_id = ?', - $productId - )->where( - 'from_time = 0 or from_time < ?', - $date - )->where( - 'to_time = 0 or to_time > ?', - $date - ); + $select = $connection->select() + ->from($this->getTable('catalogrule_product')) + ->where('website_id = ?', $websiteId) + ->where('customer_group_id = ?', $customerGroupId) + ->where('product_id = ?', $productId) + ->where('from_time = 0 or from_time < ?', $date) + ->where('to_time = 0 or to_time > ?', $date); return $connection->fetchAll($select); } + + /** + * @param \Magento\Framework\Model\AbstractModel $object + * @param mixed $value + * @param string $field + * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) + { + $this->entityManager->load('Magento\CatalogRule\Api\Data\RuleInterface', $object, $value); + + $this->unserializeFields($object); + $this->_afterLoad($object); + + return $this; + } + + /** + * @param AbstractModel $object + * @return $this + * @throws \Exception + */ + public function save(\Magento\Framework\Model\AbstractModel $object) + { + if ($object->isDeleted()) { + return $this->delete($object); + } + + $this->beginTransaction(); + + try { + if (!$this->isModified($object)) { + $this->processNotModifiedSave($object); + $this->commit(); + $object->setHasDataChanges(false); + return $this; + } + $object->validateBeforeSave(); + $object->beforeSave(); + if ($object->isSaveAllowed()) { + $this->_serializeFields($object); + $this->_beforeSave($object); + $this->_checkUnique($object); + $this->objectRelationProcessor->validateDataIntegrity($this->getMainTable(), $object->getData()); + + $this->entityManager->save( + 'Magento\CatalogRule\Api\Data\RuleInterface', + $object + ); + + $this->unserializeFields($object); + $this->processAfterSaves($object); + } + $this->addCommitCallback([$object, 'afterCommitCallback'])->commit(); + $object->setHasDataChanges(false); + } catch (\Exception $e) { + $this->rollBack(); + $object->setHasDataChanges(true); + throw $e; + } + return $this; + } } diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Rule/Collection.php index 33a9c5c81320f..62dd61ea82989 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Rule/Collection.php @@ -12,13 +12,36 @@ class Collection extends \Magento\Rule\Model\ResourceModel\Rule\Collection\Abstr * * @var array */ - protected $_associatedEntitiesMap = [ - 'website' => [ - 'associations_table' => 'catalogrule_website', - 'rule_id_field' => 'rule_id', - 'entity_id_field' => 'website_id', - ], - ]; + protected $_associatedEntitiesMap; + + /** + * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource + * @param array $associatedEntitiesMap + */ + public function __construct( + \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, + \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null, + array $associatedEntitiesMap = [] + ) { + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $connection, + $resource + ); + $this->_associatedEntitiesMap = $associatedEntitiesMap; + } /** * Set resource model @@ -45,4 +68,73 @@ public function addAttributeInConditionFilter($attributeCode) return $this; } + + /** + * @param string $entityType + * @param string $objectField + * @throws \Magento\Framework\Exception\LocalizedException + * @return void + */ + protected function mapAssociatedEntities($entityType, $objectField) + { + if (!$this->_items) { + return; + } + + $entityInfo = $this->_getAssociatedEntityInfo($entityType); + $ruleIdField = $entityInfo['rule_id_field']; + $entityIds = $this->getColumnValues($ruleIdField); + + $select = $this->getConnection()->select()->from( + $this->getTable($entityInfo['associations_table']) + )->where( + $ruleIdField . ' IN (?)', + $entityIds + ); + + $associatedEntities = $this->getConnection()->fetchAll($select); + + array_map(function ($associatedEntity) use ($entityInfo, $ruleIdField, $objectField) { + $item = $this->getItemByColumnValue($ruleIdField, $associatedEntity[$ruleIdField]); + $itemAssociatedValue = $item->getData($objectField) === null ? [] : $item->getData($objectField); + $itemAssociatedValue[] = $associatedEntity[$entityInfo['entity_id_field']]; + $item->setData($objectField, $itemAssociatedValue); + }, $associatedEntities); + } + + /** + * @return $this + * @throws \Exception + */ + protected function _afterLoad() + { + $this->mapAssociatedEntities('website', 'website_ids'); + $this->mapAssociatedEntities('customer_group', 'customer_group_ids'); + + $this->setFlag('add_websites_to_result', false); + return parent::_afterLoad(); + } + + /** + * Limit rules collection by specific customer group + * + * @param int $customerGroupId + * @return $this + */ + public function addCustomerGroupFilter($customerGroupId) + { + $entityInfo = $this->_getAssociatedEntityInfo('customer_group'); + if (!$this->getFlag('is_customer_group_joined')) { + $this->setFlag('is_customer_group_joined', true); + $this->getSelect()->join( + ['customer_group' => $this->getTable($entityInfo['associations_table'])], + $this->getConnection() + ->quoteInto('customer_group.' . $entityInfo['entity_id_field'] . ' = ?', $customerGroupId) + . ' AND main_table.' . $entityInfo['rule_id_field'] . ' = customer_group.' + . $entityInfo['rule_id_field'], + [] + ); + } + return $this; + } } diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/SaveHandler.php b/app/code/Magento/CatalogRule/Model/ResourceModel/SaveHandler.php new file mode 100644 index 0000000000000..fabb45960109b --- /dev/null +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/SaveHandler.php @@ -0,0 +1,61 @@ +ruleResource = $ruleResource; + $this->metadataPool = $metadataPool; + } + + /** + * @param string $entityType + * @param array $entityData + * @return array + * @throws \Exception + */ + public function execute($entityType, $entityData) + { + $linkField = $this->metadataPool->getMetadata($entityType)->getLinkField(); + if (isset($entityData['website_ids'])) { + $websiteIds = $entityData['website_ids']; + if (!is_array($websiteIds)) { + $websiteIds = explode(',', (string)$websiteIds); + } + $this->ruleResource->bindRuleToEntity($entityData[$linkField], $websiteIds, 'website'); + } + + if (isset($entityData['customer_group_ids'])) { + $customerGroupIds = $entityData['customer_group_ids']; + if (!is_array($customerGroupIds)) { + $customerGroupIds = explode(',', (string)$customerGroupIds); + } + $this->ruleResource->bindRuleToEntity($entityData[$linkField], $customerGroupIds, 'customer_group'); + } + return $entityData; + } +} diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index dcd36cfaf3086..d89b7d6e4c143 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -12,35 +12,16 @@ * * @method \Magento\CatalogRule\Model\ResourceModel\Rule _getResource() * @method \Magento\CatalogRule\Model\ResourceModel\Rule getResource() - * @method string getName() - * @method \Magento\CatalogRule\Model\Rule setName(string $value) - * @method string getDescription() - * @method \Magento\CatalogRule\Model\Rule setDescription(string $value) - * @method string getFromDate() * @method \Magento\CatalogRule\Model\Rule setFromDate(string $value) - * @method string getToDate() * @method \Magento\CatalogRule\Model\Rule setToDate(string $value) * @method \Magento\CatalogRule\Model\Rule setCustomerGroupIds(string $value) - * @method int getIsActive() - * @method \Magento\CatalogRule\Model\Rule setIsActive(int $value) - * @method string getConditionsSerialized() - * @method \Magento\CatalogRule\Model\Rule setConditionsSerialized(string $value) - * @method string getActionsSerialized() - * @method \Magento\CatalogRule\Model\Rule setActionsSerialized(string $value) - * @method int getStopRulesProcessing() - * @method \Magento\CatalogRule\Model\Rule setStopRulesProcessing(int $value) - * @method int getSortOrder() - * @method \Magento\CatalogRule\Model\Rule setSortOrder(int $value) - * @method string getSimpleAction() - * @method \Magento\CatalogRule\Model\Rule setSimpleAction(string $value) - * @method float getDiscountAmount() - * @method \Magento\CatalogRule\Model\Rule setDiscountAmount(float $value) * @method string getWebsiteIds() * @method \Magento\CatalogRule\Model\Rule setWebsiteIds(string $value) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ -class Rule extends \Magento\Rule\Model\AbstractModel +class Rule extends \Magento\Rule\Model\AbstractModel implements \Magento\CatalogRule\Api\Data\RuleInterface { /** * Prefix of model events names @@ -138,11 +119,6 @@ class Rule extends \Magento\Rule\Model\AbstractModel */ protected $_productCollectionFactory; - /** - * @var \Magento\Framework\Stdlib\DateTime - */ - protected $dateTime; - /** * @var \Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor; */ @@ -151,28 +127,32 @@ class Rule extends \Magento\Rule\Model\AbstractModel /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Framework\Data\FormFactory $formFactory * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\CatalogRule\Model\Rule\Condition\CombineFactory $combineFactory - * @param \Magento\CatalogRule\Model\Rule\Action\CollectionFactory $actionCollectionFactory + * @param Rule\Condition\CombineFactory $combineFactory + * @param Rule\Action\CollectionFactory $actionCollectionFactory * @param \Magento\Catalog\Model\ProductFactory $productFactory * @param \Magento\Framework\Model\ResourceModel\Iterator $resourceIterator * @param \Magento\Customer\Model\Session $customerSession * @param \Magento\CatalogRule\Helper\Data $catalogRuleData * @param \Magento\Framework\App\Cache\TypeListInterface $cacheTypesList - * @param \Magento\Framework\Stdlib\DateTime $dateTime - * @param \Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor $ruleProductProcessor - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Indexer\Rule\RuleProductProcessor $ruleProductProcessor + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $relatedCacheTypes * @param array $data + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory, @@ -184,7 +164,6 @@ public function __construct( \Magento\Customer\Model\Session $customerSession, \Magento\CatalogRule\Helper\Data $catalogRuleData, \Magento\Framework\App\Cache\TypeListInterface $cacheTypesList, - \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor $ruleProductProcessor, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, @@ -201,9 +180,18 @@ public function __construct( $this->_catalogRuleData = $catalogRuleData; $this->_cacheTypesList = $cacheTypesList; $this->_relatedCacheTypes = $relatedCacheTypes; - $this->dateTime = $dateTime; $this->_ruleProductProcessor = $ruleProductProcessor; - parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data); + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $formFactory, + $localeDate, + $resource, + $resourceCollection, + $data + ); } /** @@ -587,4 +575,203 @@ protected function dataDiff($array1, $array2) } return $result; } + + //@codeCoverageIgnoreStart + /** + * {@inheritdoc} + */ + public function getRuleId() + { + return $this->getData(self::RULE_ID); + } + + /** + * {@inheritdoc} + */ + public function setRuleId($ruleId) + { + return $this->setData(self::RULE_ID, $ruleId); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->getData(self::NAME); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + return $this->setData(self::NAME, $name); + } + + /** + * {@inheritdoc} + */ + public function getDescription() + { + return $this->getData(self::DESCRIPTION); + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) + { + return $this->setData(self::DESCRIPTION, $description); + } + + /** + * {@inheritdoc} + */ + public function getIsActive() + { + return $this->getData(self::IS_ACTIVE); + } + + /** + * {@inheritdoc} + */ + public function setIsActive($isActive) + { + return $this->setData(self::IS_ACTIVE, $isActive); + } + + /** + * {@inheritdoc} + */ + public function getConditionsSerialized() + { + return $this->getData(self::CONDITIONS_SERIALIZED); + } + + /** + * {@inheritdoc} + */ + public function setConditionsSerialized($conditions) + { + return $this->setData(self::CONDITIONS_SERIALIZED, $conditions); + } + + /** + * {@inheritdoc} + */ + public function getActionsSerialized() + { + return $this->getData(self::ACTIONS_SERIALIZED); + } + + /** + * {@inheritdoc} + */ + public function setActionsSerialized($actions) + { + return $this->setData(self::ACTIONS_SERIALIZED, $actions); + } + + /** + * {@inheritdoc} + */ + public function getStopRulesProcessing() + { + return $this->getData(self::STOP_RULES_PROCESSING); + } + + /** + * {@inheritdoc} + */ + public function setStopRulesProcessing($isStopProcessing) + { + return $this->setData(self::STOP_RULES_PROCESSING, $isStopProcessing); + } + + /** + * {@inheritdoc} + */ + public function getSortOrder() + { + return $this->getData(self::SORT_ORDER); + } + + /** + * {@inheritdoc} + */ + public function setSortOrder($sortOrder) + { + return $this->setData(self::SORT_ORDER, $sortOrder); + } + + /** + * {@inheritdoc} + */ + public function getSimpleAction() + { + return $this->getData(self::SIMPLE_ACTION); + } + + /** + * {@inheritdoc} + */ + public function setSimpleAction($action) + { + return $this->setData(self::SIMPLE_ACTION, $action); + } + + /** + * {@inheritdoc} + */ + public function getDiscountAmount() + { + return $this->getData(self::DISCOUNT_AMOUNT); + } + + /** + * {@inheritdoc} + */ + public function setDiscountAmount($amount) + { + return $this->setData(self::DISCOUNT_AMOUNT, $amount); + } + + /** + * @return string + */ + public function getFromDate() + { + return $this->getData('from_date'); + } + + /** + * @return string + */ + public function getToDate() + { + return $this->getData('to_date'); + } + + /** + * {@inheritdoc} + * + * @return \Magento\CatalogRule\Api\Data\RuleExtensionInterface|null + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * {@inheritdoc} + * + * @param \Magento\CatalogRule\Api\Data\RuleExtensionInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes(\Magento\CatalogRule\Api\Data\RuleExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } + //@codeCoverageIgnoreEnd } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php new file mode 100644 index 0000000000000..8bb0522fa564f --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/CatalogRuleRepositoryTest.php @@ -0,0 +1,146 @@ +ruleResourceMock = $this->getMock('Magento\CatalogRule\Model\ResourceModel\Rule', [], [], '', false); + $this->ruleFactoryMock = $this->getMock('Magento\CatalogRule\Model\RuleFactory', ['create'], [], '', false); + $this->ruleMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); + $this->repository = new \Magento\CatalogRule\Model\CatalogRuleRepository( + $this->ruleResourceMock, + $this->ruleFactoryMock + ); + } + + public function testSave() + { + $this->ruleMock->expects($this->once())->method('getRuleId')->willReturn(null); + $this->ruleMock->expects($this->once())->method('getId')->willReturn(1); + $this->ruleResourceMock->expects($this->once())->method('save')->with($this->ruleMock); + $this->assertEquals($this->ruleMock, $this->repository->save($this->ruleMock)); + } + + public function testEditRule() + { + $ruleId = 1; + $ruleData = ['id' => $ruleId]; + $this->ruleMock->expects($this->once())->method('getData')->willReturn($ruleData); + $ruleMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); + $this->ruleMock->expects($this->exactly(2))->method('getRuleId')->willReturn($ruleId); + $ruleMock->expects($this->once())->method('addData')->with($ruleData)->willReturn($ruleMock); + $this->ruleFactoryMock->expects($this->once())->method('create')->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('load')->with($ruleId)->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getRuleId')->willReturn($ruleId); + $this->ruleResourceMock->expects($this->once())->method('save')->with($ruleMock)->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getId')->willReturn($ruleId); + $this->assertEquals($ruleMock, $this->repository->save($this->ruleMock)); + } + + /** + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedExceptionMessage Unable to save rule 1 + */ + public function testEnableSaveRule() + { + $this->ruleMock->expects($this->at(0))->method('getRuleId')->willReturn(null); + $this->ruleMock->expects($this->at(1))->method('getRuleId')->willReturn(1); + $this->ruleMock->expects($this->never())->method('getId'); + $this->ruleResourceMock + ->expects($this->once()) + ->method('save') + ->with($this->ruleMock)->willThrowException(new \Exception()); + $this->repository->save($this->ruleMock); + } + + public function testDeleteRule() + { + $this->ruleMock->expects($this->once())->method('getId')->willReturn(1); + $this->ruleResourceMock + ->expects($this->once()) + ->method('delete') + ->with($this->ruleMock); + $this->assertEquals(true, $this->repository->delete($this->ruleMock)); + } + + public function testDeleteRuleById() + { + $ruleId = 1; + $ruleMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); + $this->ruleFactoryMock->expects($this->once())->method('create')->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getRuleId')->willReturn($ruleId); + $ruleMock->expects($this->once())->method('load')->with($ruleId)->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getId')->willReturn($ruleId); + $this->ruleResourceMock + ->expects($this->once()) + ->method('delete') + ->with($ruleMock); + $this->assertEquals(true, $this->repository->deleteById($ruleId)); + } + + /** + * @expectedException \Magento\Framework\Exception\CouldNotDeleteException + * @expectedExceptionMessage Unable to remove rule 1 + */ + public function testUnableDeleteRule() + { + $this->ruleMock->expects($this->once())->method('getRuleId')->willReturn(1); + $this->ruleResourceMock + ->expects($this->once()) + ->method('delete') + ->with($this->ruleMock)->willThrowException(new \Exception()); + $this->repository->delete($this->ruleMock); + } + + public function testGetRule() + { + $ruleId = 1; + $ruleMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); + $this->ruleFactoryMock->expects($this->once())->method('create')->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('load')->with($ruleId)->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getRuleId')->willReturn($ruleId); + $this->assertEquals($ruleMock, $this->repository->get($ruleId)); + /** verify that rule was cached */ + $this->assertEquals($ruleMock, $this->repository->get($ruleId)); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage Rule with specified ID "1" not found. + */ + public function testGetNonExistentRule() + { + $ruleId = 1; + $ruleMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); + $this->ruleFactoryMock->expects($this->once())->method('create')->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('load')->with($ruleId)->willReturn($ruleMock); + $ruleMock->expects($this->once())->method('getRuleId')->willReturn(null); + $this->assertEquals($ruleMock, $this->repository->get($ruleId)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/ReadHandlerTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/ReadHandlerTest.php new file mode 100644 index 0000000000000..b6be4762de6da --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/ReadHandlerTest.php @@ -0,0 +1,78 @@ +resourceMock = $this->getMock('\Magento\CatalogRule\Model\ResourceModel\Rule', [], [], '', false); + $this->metadataMock = $this->getMock('\Magento\Framework\Model\Entity\MetadataPool', [], [], '', false); + $this->subject = new \Magento\CatalogRule\Model\ResourceModel\ReadHandler( + $this->resourceMock, + $this->metadataMock + ); + } + + public function testExecute() + { + $linkedField = 'entity_id'; + $entityId = 100; + $entityType = '\Magento\CatalogRule\Entity\Type'; + $entityData = [ + $linkedField => $entityId + ]; + + $customerGroupIds = [1, 2, 3]; + $websiteIds = [4, 5, 6]; + + $metadataMock = $this->getMock( + '\Magento\Framework\Model\Entity\EntityMetadata', + ['getLinkField'], + [], + '', + false + ); + $this->metadataMock->expects($this->once()) + ->method('getMetadata') + ->with($entityType) + ->willReturn($metadataMock); + + $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkedField); + + $this->resourceMock->expects($this->once()) + ->method('getCustomerGroupIds') + ->with($entityId) + ->willReturn($customerGroupIds); + $this->resourceMock->expects($this->once()) + ->method('getWebsiteIds') + ->with($entityId) + ->willReturn($websiteIds); + + $expectedResult = [ + $linkedField => $entityId, + 'customer_group_ids' => $customerGroupIds, + 'website_ids' => $websiteIds + ]; + + $this->assertEquals($expectedResult, $this->subject->execute($entityType, $entityData)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/SaveHandlerTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/SaveHandlerTest.php new file mode 100644 index 0000000000000..e108d1103be4b --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/ResourceModel/SaveHandlerTest.php @@ -0,0 +1,74 @@ +resourceMock = $this->getMock('\Magento\CatalogRule\Model\ResourceModel\Rule', [], [], '', false); + $this->metadataMock = $this->getMock('\Magento\Framework\Model\Entity\MetadataPool', [], [], '', false); + $this->subject = new \Magento\CatalogRule\Model\ResourceModel\SaveHandler( + $this->resourceMock, + $this->metadataMock + ); + } + + public function testExecute() + { + $linkedField = 'entity_id'; + $entityId = 100; + $entityType = '\Magento\CatalogRule\Entity\Type'; + + $customerGroupIds = '1, 2, 3'; + $websiteIds = '4, 5, 6'; + $entityData = [ + $linkedField => $entityId, + 'website_ids' => $websiteIds, + 'customer_group_ids' => $customerGroupIds + ]; + + $metadataMock = $this->getMock( + '\Magento\Framework\Model\Entity\EntityMetadata', + ['getLinkField'], + [], + '', + false + ); + $this->metadataMock->expects($this->once()) + ->method('getMetadata') + ->with($entityType) + ->willReturn($metadataMock); + $metadataMock->expects($this->once())->method('getLinkField')->willReturn($linkedField); + + $this->resourceMock->expects($this->at(0)) + ->method('bindRuleToEntity') + ->with($entityId, explode(',', (string)$websiteIds), 'website') + ->willReturnSelf(); + + $this->resourceMock->expects($this->at(1)) + ->method('bindRuleToEntity') + ->with($entityId, explode(',', (string)$customerGroupIds), 'customer_group') + ->willReturnSelf(); + + $this->assertEquals($entityData, $this->subject->execute($entityType, $entityData)); + } +} diff --git a/app/code/Magento/CatalogRule/etc/di.xml b/app/code/Magento/CatalogRule/etc/di.xml index da4a8ad03cba6..5accde1deb86c 100644 --- a/app/code/Magento/CatalogRule/etc/di.xml +++ b/app/code/Magento/CatalogRule/etc/di.xml @@ -9,9 +9,36 @@ Magento\Framework\Event\Manager\Proxy + + + catalogrule_website + rule_id + website_id + + + catalogrule_customer_group + rule_id + customer_group_id + + + + + + + + + catalogrule_website + rule_id + website_id + + + catalogrule_customer_group + rule_id + customer_group_id + + - @@ -33,4 +60,44 @@ + + + + + + + catalogrule + rule_id + + + + catalog_product + + + + + + + + Magento\Framework\Model\Operation\Read + Magento\Framework\Model\Operation\Write\Create + Magento\Framework\Model\Operation\Write\Update + Magento\Framework\Model\Operation\Write\Delete + + + + + + + + + + Magento\CatalogRule\Model\ResourceModel\ReadHandler + Magento\CatalogRule\Model\ResourceModel\SaveHandler + Magento\CatalogRule\Model\ResourceModel\SaveHandler + + + + + diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureFactory.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureFactory.php new file mode 100644 index 0000000000000..49adbc21de059 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureFactory.php @@ -0,0 +1,85 @@ +objectManager = $objectManager; + $this->scopeConfig = $scopeConfig; + $this->configPath = $configPath; + $this->structures = $structures; + } + + /** + * Create index structure + * + * @param array $data + * @return IndexStructureInterface + */ + public function create(array $data = []) + { + $currentStructure = $this->scopeConfig->getValue($this->configPath, ScopeInterface::SCOPE_STORE); + if (!isset($this->structures[$currentStructure])) { + throw new \LogicException( + 'There is no such index structure: ' . $currentStructure + ); + } + $indexStructure = $this->objectManager->create($this->structures[$currentStructure], $data); + + if (!$indexStructure instanceof IndexStructureInterface) { + throw new \InvalidArgumentException( + $indexStructure . ' doesn\'t implement \Magento\Framework\Indexer\IndexStructureInterface' + ); + } + + return $indexStructure; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureProxy.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureProxy.php new file mode 100644 index 0000000000000..fd90d6d7e4179 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexStructureProxy.php @@ -0,0 +1,64 @@ +indexStructureFactory = $indexStructureFactory; + } + + /** + * {@inheritdoc} + */ + public function delete( + $index, + array $dimensions = [] + ) { + return $this->getEntity()->delete($index, $dimensions); + } + + /** + * {@inheritdoc} + */ + public function create( + $index, + array $fields, + array $dimensions = [] + ) { + return $this->getEntity()->create($index, $fields, $dimensions); + } + + /** + * Get instance of current index structure + * + * @return IndexStructureInterface + */ + private function getEntity() + { + if (empty($this->indexStructureEntity)) { + $this->indexStructureEntity = $this->indexStructureFactory->create(); + } + return $this->indexStructureEntity; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php index fa5ca4ba80a39..048086fdcacdf 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/IndexerHandler.php @@ -9,6 +9,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Indexer\SaveHandler\IndexerInterface; +use Magento\Framework\Indexer\IndexStructureInterface; use Magento\Framework\Search\Request\Dimension; use Magento\Framework\Search\Request\IndexScopeResolverInterface; use Magento\Framework\Indexer\SaveHandler\Batch; @@ -17,7 +18,7 @@ class IndexerHandler implements IndexerInterface { /** - * @var IndexStructure + * @var IndexStructureInterface */ private $indexStructure; @@ -57,7 +58,7 @@ class IndexerHandler implements IndexerInterface private $indexScopeResolver; /** - * @param IndexStructure $indexStructure + * @param IndexStructureInterface $indexStructure * @param ResourceConnection $resource * @param Config $eavConfig * @param Batch $batch @@ -66,7 +67,7 @@ class IndexerHandler implements IndexerInterface * @param int $batchSize */ public function __construct( - IndexStructure $indexStructure, + IndexStructureInterface $indexStructure, ResourceConnection $resource, Config $eavConfig, Batch $batch, diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 815f2b223f04e..37d7cf2da1ed4 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -19,6 +19,15 @@ + + + Magento\CatalogSearch\Model\ResourceModel\EngineInterface::CONFIG_ENGINE_PATH + + Magento\CatalogSearch\Model\Indexer\IndexStructure + + + + diff --git a/app/code/Magento/CatalogWidget/Model/Rule.php b/app/code/Magento/CatalogWidget/Model/Rule.php index 5ad9c4c4877fe..70f2d98733ea0 100644 --- a/app/code/Magento/CatalogWidget/Model/Rule.php +++ b/app/code/Magento/CatalogWidget/Model/Rule.php @@ -19,16 +19,21 @@ class Rule extends \Magento\Rule\Model\AbstractModel /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Framework\Data\FormFactory $formFactory * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param Rule\Condition\CombineFactory $conditionsFactory - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\CatalogWidget\Model\Rule\Condition\CombineFactory $conditionsFactory, @@ -37,7 +42,17 @@ public function __construct( array $data = [] ) { $this->conditionsFactory = $conditionsFactory; - parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data); + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $formFactory, + $localeDate, + $resource, + $resourceCollection, + $data + ); } /** diff --git a/app/code/Magento/Cms/Model/Block.php b/app/code/Magento/Cms/Model/Block.php index 6b5c5b6d08a04..aa3109744dff6 100644 --- a/app/code/Magento/Cms/Model/Block.php +++ b/app/code/Magento/Cms/Model/Block.php @@ -6,15 +6,19 @@ namespace Magento\Cms\Model; use Magento\Cms\Api\Data\BlockInterface; +use Magento\Cms\Model\ResourceModel\Block as ResourceCmsBlock; use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Model\AbstractModel; /** * CMS block model * - * @method \Magento\Cms\Model\ResourceModel\Block _getResource() - * @method \Magento\Cms\Model\ResourceModel\Block getResource() + * @method ResourceCmsBlock _getResource() + * @method ResourceCmsBlock getResource() + * @method Block setStoreId(array $storeId) + * @method array getStoreId() */ -class Block extends \Magento\Framework\Model\AbstractModel implements BlockInterface, IdentityInterface +class Block extends AbstractModel implements BlockInterface, IdentityInterface { /** * CMS block cache tag @@ -51,7 +55,7 @@ protected function _construct() /** * Prevent blocks recursion * - * @return \Magento\Framework\Model\AbstractModel + * @return AbstractModel * @throws \Magento\Framework\Exception\LocalizedException */ public function beforeSave() diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index 04da7105c4d65..b248ad835d028 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -6,15 +6,19 @@ namespace Magento\Cms\Model; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\ResourceModel\Page as ResourceCmsPage; use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Model\AbstractModel; /** * Cms Page Model * - * @method \Magento\Cms\Model\ResourceModel\Page _getResource() - * @method \Magento\Cms\Model\ResourceModel\Page getResource() + * @method ResourceCmsPage _getResource() + * @method ResourceCmsPage getResource() + * @method Page setStoreId(array $storeId) + * @method array getStoreId() */ -class Page extends \Magento\Framework\Model\AbstractModel implements PageInterface, IdentityInterface +class Page extends AbstractModel implements PageInterface, IdentityInterface { /** * No route page id diff --git a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php index 7b2e0829cf4d3..b9f0f8006be30 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/AbstractCollection.php @@ -17,12 +17,18 @@ abstract class AbstractCollection extends \Magento\Framework\Model\ResourceModel */ protected $storeManager; + /** + * @var \Magento\Framework\Model\Entity\MetadataPool + */ + protected $metadataPool; + /** * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource */ @@ -32,45 +38,47 @@ public function __construct( \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Model\Entity\MetadataPool $metadataPool, \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null ) { - parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource); $this->storeManager = $storeManager; + $this->metadataPool = $metadataPool; + parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource); } /** * Perform operations after collection load * * @param string $tableName - * @param string $columnName + * @param string|null $linkField * @return void */ - protected function performAfterLoad($tableName, $columnName) + protected function performAfterLoad($tableName, $linkField) { - $items = $this->getColumnValues($columnName); - if (count($items)) { + $linkedIds = $this->getColumnValues($linkField); + if (count($linkedIds)) { $connection = $this->getConnection(); $select = $connection->select()->from(['cms_entity_store' => $this->getTable($tableName)]) - ->where('cms_entity_store.' . $columnName . ' IN (?)', $items); + ->where('cms_entity_store.' . $linkField . ' IN (?)', $linkedIds); $result = $connection->fetchPairs($select); if ($result) { foreach ($this as $item) { - $entityId = $item->getData($columnName); - if (!isset($result[$entityId])) { + $linkedId = $item->getData($linkField); + if (!isset($result[$linkedId])) { continue; } - if ($result[$entityId] == 0) { + if ($result[$linkedId] == 0) { $stores = $this->storeManager->getStores(false, true); $storeId = current($stores)->getId(); $storeCode = key($stores); } else { - $storeId = $result[$item->getData($columnName)]; + $storeId = $result[$linkedId]; $storeCode = $this->storeManager->getStore($storeId)->getCode(); } $item->setData('_first_store_id', $storeId); $item->setData('store_code', $storeCode); - $item->setData('store_id', [$result[$entityId]]); + $item->setData('store_id', [$result[$linkedId]]); } } } @@ -129,18 +137,18 @@ protected function performAddStoreFilter($store, $withAdmin = true) * Join store relation table if there is store filter * * @param string $tableName - * @param string $columnName + * @param string|null $linkField * @return void */ - protected function joinStoreRelationTable($tableName, $columnName) + protected function joinStoreRelationTable($tableName, $linkField) { if ($this->getFilter('store')) { $this->getSelect()->join( ['store_table' => $this->getTable($tableName)], - 'main_table.' . $columnName . ' = store_table.' . $columnName, + 'main_table.' . $linkField . ' = store_table.' . $linkField, [] )->group( - 'main_table.' . $columnName + 'main_table.' . $linkField ); } parent::_renderFiltersBefore(); diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 2794d5d3228b5..04f2cdd5c4aed 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -5,6 +5,12 @@ */ namespace Magento\Cms\Model\ResourceModel; +use Magento\Cms\Api\Data\BlockInterface; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Framework\Model\EntityManager; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Store\Model\StoreManagerInterface; + /** * CMS block model */ @@ -13,24 +19,38 @@ class Block extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** - * Construct - * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @var EntityManager + */ + protected $entityManager; + + /** + * @var MetadataPool + */ + protected $metadataPool; + + /** + * @param Context $context + * @param StoreManagerInterface $storeManager + * @param EntityManager $entityManager + * @param MetadataPool $metadataPool * @param string $connectionName */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Store\Model\StoreManagerInterface $storeManager, + Context $context, + StoreManagerInterface $storeManager, + EntityManager $entityManager, + MetadataPool $metadataPool, $connectionName = null ) { - parent::__construct($context, $connectionName); $this->_storeManager = $storeManager; + $this->entityManager = $entityManager; + $this->metadataPool = $metadataPool; + parent::__construct($context, $connectionName); } /** @@ -43,21 +63,6 @@ protected function _construct() $this->_init('cms_block', 'block_id'); } - /** - * Process block data before deleting - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Cms\Model\ResourceModel\Page - */ - protected function _beforeDelete(\Magento\Framework\Model\AbstractModel $object) - { - $condition = ['block_id = ?' => (int)$object->getId()]; - - $this->getConnection()->delete($this->getTable('cms_block_store'), $condition); - - return parent::_beforeDelete($object); - } - /** * Perform operations before object save * @@ -76,71 +81,39 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) } /** - * Perform operations after object save + * Load an object * - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this - */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) - { - $oldStores = $this->lookupStoreIds($object->getId()); - $newStores = (array)$object->getStores(); - - $table = $this->getTable('cms_block_store'); - $insert = array_diff($newStores, $oldStores); - $delete = array_diff($oldStores, $newStores); - - if ($delete) { - $where = ['block_id = ?' => (int)$object->getId(), 'store_id IN (?)' => $delete]; - - $this->getConnection()->delete($table, $where); - } - - if ($insert) { - $data = []; - - foreach ($insert as $storeId) { - $data[] = ['block_id' => (int)$object->getId(), 'store_id' => (int)$storeId]; - } - - $this->getConnection()->insertMultiple($table, $data); - } - - return parent::_afterSave($object); - } - - /** - * Load an object using 'identifier' field if there's no field specified and value is not numeric - * - * @param \Magento\Framework\Model\AbstractModel $object + * @param \Magento\Cms\Model\Block|\Magento\Framework\Model\AbstractModel $object * @param mixed $value - * @param string $field + * @param string $field field to load by (defaults to model id) * @return $this */ public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) { + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + if (!is_numeric($value) && $field === null) { $field = 'identifier'; + } elseif (!$field) { + $field = $entityMetadata->getIdentifierField(); } - return parent::load($object, $value, $field); - } - - /** - * Perform operations after object load - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this - */ - protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object) - { - if ($object->getId()) { - $stores = $this->lookupStoreIds($object->getId()); - $object->setData('store_id', $stores); - $object->setData('stores', $stores); + $isId = true; + if ($field != $entityMetadata->getIdentifierField() || $object->getStoreId()) { + $select = $this->_getLoadSelect($field, $value, $object); + $select->reset(\Magento\Framework\DB\Select::COLUMNS) + ->columns($this->getMainTable() . '.' . $entityMetadata->getIdentifierField()) + ->limit(1); + $result = $this->getConnection()->fetchCol($select); + $value = count($result) ? $result[0] : $value; + $isId = count($result); } - return parent::_afterLoad($object); + if ($isId) { + $this->entityManager->load(BlockInterface::class, $object, $value); + $this->_afterLoad($object); + } + return $this; } /** @@ -148,11 +121,14 @@ protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object) * * @param string $field * @param mixed $value - * @param \Magento\Cms\Model\Block $object + * @param \Magento\Cms\Model\Block|\Magento\Framework\Model\AbstractModel $object * @return \Magento\Framework\DB\Select */ protected function _getLoadSelect($field, $value, $object) { + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + $linkField = $entityMetadata->getLinkField(); + $select = parent::_getLoadSelect($field, $value, $object); if ($object->getStoreId()) { @@ -160,19 +136,13 @@ protected function _getLoadSelect($field, $value, $object) $select->join( ['cbs' => $this->getTable('cms_block_store')], - $this->getMainTable() . '.block_id = cbs.block_id', + $this->getMainTable() . '.' . $linkField . ' = cbs.' . $linkField, ['store_id'] - )->where( - 'is_active = ?', - 1 - )->where( - 'cbs.store_id in (?)', - $stores - )->order( - 'store_id DESC' - )->limit( - 1 - ); + ) + ->where('is_active = ?', 1) + ->where('cbs.store_id in (?)', $stores) + ->order('store_id DESC') + ->limit(1); } return $select; @@ -187,28 +157,27 @@ protected function _getLoadSelect($field, $value, $object) */ public function getIsUniqueBlockToStores(\Magento\Framework\Model\AbstractModel $object) { + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + $linkField = $entityMetadata->getLinkField(); + if ($this->_storeManager->hasSingleStore()) { $stores = [\Magento\Store\Model\Store::DEFAULT_STORE_ID]; } else { $stores = (array)$object->getData('stores'); } - $select = $this->getConnection()->select()->from( - ['cb' => $this->getMainTable()] - )->join( - ['cbs' => $this->getTable('cms_block_store')], - 'cb.block_id = cbs.block_id', - [] - )->where( - 'cb.identifier = ?', - $object->getData('identifier') - )->where( - 'cbs.store_id IN (?)', - $stores - ); + $select = $this->getConnection()->select() + ->from(['cb' => $this->getMainTable()]) + ->join( + ['cbs' => $this->getTable('cms_block_store')], + 'cb.' . $linkField . ' = cbs.' . $linkField, + [] + ) + ->where('cb.identifier = ?', $object->getData('identifier')) + ->where('cbs.store_id IN (?)', $stores); if ($object->getId()) { - $select->where('cb.block_id <> ?', $object->getId()); + $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId()); } if ($this->getConnection()->fetchRow($select)) { @@ -228,15 +197,59 @@ public function lookupStoreIds($id) { $connection = $this->getConnection(); - $select = $connection->select()->from( - $this->getTable('cms_block_store'), - 'store_id' - )->where( - 'block_id = :block_id' - ); + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + $linkField = $entityMetadata->getLinkField(); - $binds = [':block_id' => (int)$id]; + $select = $connection->select() + ->from(['cbs' => $this->getTable('cms_block_store')], 'store_id') + ->join( + ['cb' => $this->getMainTable()], + 'cbs.' . $linkField . ' = cb.' . $linkField, + [] + ) + ->where('cb.' . $entityMetadata->getIdentifierField() . ' = :block_id'); + + return $connection->fetchCol($select, ['block_id' => (int)$id]); + } - return $connection->fetchCol($select, $binds); + /** + * @param \Magento\Framework\Model\AbstractModel $object + * @return $this + * @throws \Exception + */ + public function save(\Magento\Framework\Model\AbstractModel $object) + { + if ($object->isDeleted()) { + return $this->delete($object); + } + + $this->beginTransaction(); + + try { + if (!$this->isModified($object)) { + $this->processNotModifiedSave($object); + $this->commit(); + $object->setHasDataChanges(false); + return $this; + } + $object->validateBeforeSave(); + $object->beforeSave(); + if ($object->isSaveAllowed()) { + $this->_serializeFields($object); + $this->_beforeSave($object); + $this->_checkUnique($object); + $this->objectRelationProcessor->validateDataIntegrity($this->getMainTable(), $object->getData()); + $this->entityManager->save(BlockInterface::class, $object); + $this->unserializeFields($object); + $this->processAfterSaves($object); + } + $this->addCommitCallback([$object, 'afterCommitCallback'])->commit(); + $object->setHasDataChanges(false); + } catch (\Exception $e) { + $this->rollBack(); + $object->setHasDataChanges(true); + throw $e; + } + return $this; } } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php index 5d2887f20ca05..773f92fa938b8 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Collection.php @@ -5,6 +5,7 @@ */ namespace Magento\Cms\Model\ResourceModel\Block; +use Magento\Cms\Api\Data\BlockInterface; use \Magento\Cms\Model\ResourceModel\AbstractCollection; /** @@ -24,7 +25,9 @@ class Collection extends AbstractCollection */ protected function _afterLoad() { - $this->performAfterLoad('cms_block_store', 'block_id'); + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + + $this->performAfterLoad('cms_block_store', $entityMetadata->getLinkField()); return parent::_afterLoad(); } @@ -71,6 +74,7 @@ public function addStoreFilter($store, $withAdmin = true) */ protected function _renderFiltersBefore() { - $this->joinStoreRelationTable('cms_block_store', 'block_id'); + $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); + $this->joinStoreRelationTable('cms_block_store', $entityMetadata->getLinkField()); } } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php index e658aa86cb571..d6cc7dc45bd37 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Grid/Collection.php @@ -25,6 +25,7 @@ class Collection extends BlockCollection implements SearchResultInterface * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool * @param string $mainTable * @param string $eventPrefix * @param string $eventObject @@ -41,6 +42,7 @@ public function __construct( \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Model\Entity\MetadataPool $metadataPool, $mainTable, $eventPrefix, $eventObject, @@ -55,6 +57,7 @@ public function __construct( $fetchStrategy, $eventManager, $storeManager, + $metadataPool, $connection, $resource ); diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/ReadHandler.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/ReadHandler.php new file mode 100644 index 0000000000000..32e7ccd5452b0 --- /dev/null +++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/ReadHandler.php @@ -0,0 +1,51 @@ +metadataPool = $metadataPool; + $this->resourceBlock = $resourceBlock; + } + + /** + * @param string $entityType + * @param object $entity + * @return object + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute($entityType, $entity) + { + if ($entity->getId()) { + $stores = $this->resourceBlock->lookupStoreIds((int)$entity->getId()); + $entity->setData('store_id', $stores); + $entity->setData('stores', $stores); + } + return $entity; + } +} diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/SaveHandler.php b/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/SaveHandler.php new file mode 100644 index 0000000000000..0c5f11785dd2f --- /dev/null +++ b/app/code/Magento/Cms/Model/ResourceModel/Block/Relation/Store/SaveHandler.php @@ -0,0 +1,75 @@ +metadataPool = $metadataPool; + $this->resourceBlock = $resourceBlock; + } + + /** + * @param string $entityType + * @param object $entity + * @return object + */ + public function execute($entityType, $entity) + { + $entityMetadata = $this->metadataPool->getMetadata($entityType); + $linkField = $entityMetadata->getLinkField(); + + $connection = $entityMetadata->getEntityConnection(); + + $oldStores = $this->resourceBlock->lookupStoreIds((int)$entity->getId()); + $newStores = (array)$entity->getStores(); + + $table = $this->resourceBlock->getTable('cms_block_store'); + + $delete = array_diff($oldStores, $newStores); + if ($delete) { + $where = [ + $linkField . ' = ?' => (int)$entity->getData($linkField), + 'store_id IN (?)' => $delete, + ]; + $connection->delete($table, $where); + } + + $insert = array_diff($newStores, $oldStores); + if ($insert) { + $data = []; + foreach ($insert as $storeId) { + $data[] = [ + $linkField => (int)$entity->getData($linkField), + 'store_id' => (int)$storeId, + ]; + } + $connection->insertMultiple($table, $data); + } + + return $entity; + } +} diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page.php b/app/code/Magento/Cms/Model/ResourceModel/Page.php index 334e7f66f6866..0d299cd345e01 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Page.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Page.php @@ -8,47 +8,75 @@ namespace Magento\Cms\Model\ResourceModel; +use Magento\Cms\Model\Page as CmsPage; +use Magento\Framework\DB\Select; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Stdlib\DateTime; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\Model\EntityManager; +use Magento\Cms\Api\Data\PageInterface; + /** * Cms page mysql resource + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Page extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Page extends AbstractDb { /** * Store model * - * @var null|\Magento\Store\Model\Store + * @var null|Store */ protected $_store = null; /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; /** - * @var \Magento\Framework\Stdlib\DateTime + * @var DateTime */ protected $dateTime; /** - * Construct - * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @var EntityManager + */ + protected $entityManager; + + /** + * @var MetadataPool + */ + protected $metadataPool; + + /** + * @param Context $context + * @param StoreManagerInterface $storeManager + * @param DateTime $dateTime + * @param EntityManager $entityManager + * @param MetadataPool $metadataPool * @param string $connectionName */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\Stdlib\DateTime $dateTime, + Context $context, + StoreManagerInterface $storeManager, + DateTime $dateTime, + EntityManager $entityManager, + MetadataPool $metadataPool, $connectionName = null ) { parent::__construct($context, $connectionName); $this->_storeManager = $storeManager; $this->dateTime = $dateTime; + $this->entityManager = $entityManager; + $this->metadataPool = $metadataPool; } /** @@ -61,29 +89,14 @@ protected function _construct() $this->_init('cms_page', 'page_id'); } - /** - * Process page data before deleting - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this - */ - protected function _beforeDelete(\Magento\Framework\Model\AbstractModel $object) - { - $condition = ['page_id = ?' => (int)$object->getId()]; - - $this->getConnection()->delete($this->getTable('cms_page_store'), $condition); - - return parent::_beforeDelete($object); - } - /** * Process page data before saving * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ - protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) + protected function _beforeSave(AbstractModel $object) { /* * For two attributes which represent timestamp data in DB @@ -97,13 +110,13 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) } if (!$this->isValidPageIdentifier($object)) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('The page URL key contains capital letters or disallowed symbols.') ); } if ($this->isNumericPageIdentifier($object)) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('The page URL key cannot be made of only numbers.') ); } @@ -111,73 +124,39 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) } /** - * Assign page to store views - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this - */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) - { - $oldStores = $this->lookupStoreIds($object->getId()); - $newStores = (array)$object->getStores(); - if (empty($newStores)) { - $newStores = (array)$object->getStoreId(); - } - $table = $this->getTable('cms_page_store'); - $insert = array_diff($newStores, $oldStores); - $delete = array_diff($oldStores, $newStores); - - if ($delete) { - $where = ['page_id = ?' => (int)$object->getId(), 'store_id IN (?)' => $delete]; - - $this->getConnection()->delete($table, $where); - } - - if ($insert) { - $data = []; - - foreach ($insert as $storeId) { - $data[] = ['page_id' => (int)$object->getId(), 'store_id' => (int)$storeId]; - } - - $this->getConnection()->insertMultiple($table, $data); - } - - return parent::_afterSave($object); - } - - /** - * Load an object using 'identifier' field if there's no field specified and value is not numeric + * Load an object * - * @param \Magento\Framework\Model\AbstractModel $object + * @param CmsPage|AbstractModel $object * @param mixed $value - * @param string $field + * @param string $field field to load by (defaults to model id) * @return $this */ - public function load(\Magento\Framework\Model\AbstractModel $object, $value, $field = null) + public function load(AbstractModel $object, $value, $field = null) { - if (!is_numeric($value) && is_null($field)) { + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + + if (!is_numeric($value) && $field === null) { $field = 'identifier'; + } elseif (!$field) { + $field = $entityMetadata->getIdentifierField(); } - return parent::load($object, $value, $field); - } - - /** - * Perform operations after object load - * - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this - */ - protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object) - { - if ($object->getId()) { - $stores = $this->lookupStoreIds($object->getId()); - - $object->setData('store_id', $stores); + $isId = true; + if ($field != $entityMetadata->getIdentifierField() || $object->getStoreId()) { + $select = $this->_getLoadSelect($field, $value, $object); + $select->reset(Select::COLUMNS) + ->columns($this->getMainTable() . '.' . $entityMetadata->getIdentifierField()) + ->limit(1); + $result = $this->getConnection()->fetchCol($select); + $value = count($result) ? $result[0] : $value; + $isId = count($result); } - return parent::_afterLoad($object); + if ($isId) { + $this->entityManager->load(PageInterface::class, $object, $value); + $this->_afterLoad($object); + } + return $this; } /** @@ -185,30 +164,30 @@ protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object) * * @param string $field * @param mixed $value - * @param \Magento\Cms\Model\Page $object - * @return \Magento\Framework\DB\Select + * @param CmsPage|AbstractModel $object + * @return Select */ protected function _getLoadSelect($field, $value, $object) { + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $linkField = $entityMetadata->getLinkField(); + $select = parent::_getLoadSelect($field, $value, $object); if ($object->getStoreId()) { - $storeIds = [\Magento\Store\Model\Store::DEFAULT_STORE_ID, (int)$object->getStoreId()]; + $storeIds = [ + Store::DEFAULT_STORE_ID, + (int)$object->getStoreId(), + ]; $select->join( ['cms_page_store' => $this->getTable('cms_page_store')], - $this->getMainTable() . '.page_id = cms_page_store.page_id', + $this->getMainTable() . '.' . $linkField . ' = cms_page_store.' . $linkField, [] - )->where( - 'is_active = ?', - 1 - )->where( - 'cms_page_store.store_id IN (?)', - $storeIds - )->order( - 'cms_page_store.store_id DESC' - )->limit( - 1 - ); + ) + ->where('is_active = ?', 1) + ->where('cms_page_store.store_id IN (?)', $storeIds) + ->order('cms_page_store.store_id DESC') + ->limit(1); } return $select; @@ -220,23 +199,22 @@ protected function _getLoadSelect($field, $value, $object) * @param string $identifier * @param int|array $store * @param int $isActive - * @return \Magento\Framework\DB\Select + * @return Select */ protected function _getLoadByIdentifierSelect($identifier, $store, $isActive = null) { - $select = $this->getConnection()->select()->from( - ['cp' => $this->getMainTable()] - )->join( - ['cps' => $this->getTable('cms_page_store')], - 'cp.page_id = cps.page_id', - [] - )->where( - 'cp.identifier = ?', - $identifier - )->where( - 'cps.store_id IN (?)', - $store - ); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $linkField = $entityMetadata->getLinkField(); + + $select = $this->getConnection()->select() + ->from(['cp' => $this->getMainTable()]) + ->join( + ['cps' => $this->getTable('cms_page_store')], + 'cp.' . $linkField . ' = cps.' . $linkField, + [] + ) + ->where('cp.identifier = ?', $identifier) + ->where('cps.store_id IN (?)', $store); if (!is_null($isActive)) { $select->where('cp.is_active = ?', $isActive); @@ -248,10 +226,10 @@ protected function _getLoadByIdentifierSelect($identifier, $store, $isActive = n /** * Check whether page identifier is numeric * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return bool */ - protected function isNumericPageIdentifier(\Magento\Framework\Model\AbstractModel $object) + protected function isNumericPageIdentifier(AbstractModel $object) { return preg_match('/^[0-9]+$/', $object->getData('identifier')); } @@ -259,10 +237,10 @@ protected function isNumericPageIdentifier(\Magento\Framework\Model\AbstractMode /** * Check whether page identifier is valid * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return bool */ - protected function isValidPageIdentifier(\Magento\Framework\Model\AbstractModel $object) + protected function isValidPageIdentifier(AbstractModel $object) { return preg_match('/^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/', $object->getData('identifier')); } @@ -277,9 +255,14 @@ protected function isValidPageIdentifier(\Magento\Framework\Model\AbstractModel */ public function checkIdentifier($identifier, $storeId) { - $stores = [\Magento\Store\Model\Store::DEFAULT_STORE_ID, $storeId]; + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + + $stores = [Store::DEFAULT_STORE_ID, $storeId]; $select = $this->_getLoadByIdentifierSelect($identifier, $stores, 1); - $select->reset(\Magento\Framework\DB\Select::COLUMNS)->columns('cp.page_id')->order('cps.store_id DESC')->limit(1); + $select->reset(Select::COLUMNS) + ->columns('cp.' . $entityMetadata->getIdentifierField()) + ->order('cps.store_id DESC') + ->limit(1); return $this->getConnection()->fetchOne($select); } @@ -292,13 +275,16 @@ public function checkIdentifier($identifier, $storeId) */ public function getCmsPageTitleByIdentifier($identifier) { - $stores = [\Magento\Store\Model\Store::DEFAULT_STORE_ID]; + $stores = [Store::DEFAULT_STORE_ID]; if ($this->_store) { $stores[] = (int)$this->getStore()->getId(); } $select = $this->_getLoadByIdentifierSelect($identifier, $stores); - $select->reset(\Magento\Framework\DB\Select::COLUMNS)->columns('cp.title')->order('cps.store_id DESC')->limit(1); + $select->reset(Select::COLUMNS) + ->columns('cp.title') + ->order('cps.store_id DESC') + ->limit(1); return $this->getConnection()->fetchOne($select); } @@ -312,12 +298,13 @@ public function getCmsPageTitleByIdentifier($identifier) public function getCmsPageTitleById($id) { $connection = $this->getConnection(); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); - $select = $connection->select()->from($this->getMainTable(), 'title')->where('page_id = :page_id'); - - $binds = ['page_id' => (int)$id]; + $select = $connection->select() + ->from($this->getMainTable(), 'title') + ->where($entityMetadata->getIdentifierField() . ' = :page_id'); - return $connection->fetchOne($select, $binds); + return $connection->fetchOne($select, ['page_id' => (int)$id]); } /** @@ -329,12 +316,13 @@ public function getCmsPageTitleById($id) public function getCmsPageIdentifierById($id) { $connection = $this->getConnection(); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); - $select = $connection->select()->from($this->getMainTable(), 'identifier')->where('page_id = :page_id'); + $select = $connection->select() + ->from($this->getMainTable(), 'identifier') + ->where($entityMetadata->getIdentifierField() . ' = :page_id'); - $binds = ['page_id' => (int)$id]; - - return $connection->fetchOne($select, $binds); + return $connection->fetchOne($select, ['page_id' => (int)$id]); } /** @@ -347,21 +335,25 @@ public function lookupStoreIds($pageId) { $connection = $this->getConnection(); - $select = $connection->select()->from( - $this->getTable('cms_page_store'), - 'store_id' - )->where( - 'page_id = ?', - (int)$pageId - ); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $linkField = $entityMetadata->getLinkField(); + + $select = $connection->select() + ->from(['cps' => $this->getTable('cms_page_store')], 'store_id') + ->join( + ['cp' => $this->getMainTable()], + 'cps.' . $linkField . ' = cp.' . $linkField, + [] + ) + ->where('cp.' . $entityMetadata->getIdentifierField() . ' = :page_id'); - return $connection->fetchCol($select); + return $connection->fetchCol($select, ['page_id' => (int)$pageId]); } /** * Set store model * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return $this */ public function setStore($store) @@ -373,10 +365,51 @@ public function setStore($store) /** * Retrieve store model * - * @return \Magento\Store\Model\Store + * @return Store */ public function getStore() { return $this->_storeManager->getStore($this->_store); } + + /** + * @param AbstractModel $object + * @return $this + * @throws \Exception + */ + public function save(AbstractModel $object) + { + if ($object->isDeleted()) { + return $this->delete($object); + } + + $this->beginTransaction(); + + try { + if (!$this->isModified($object)) { + $this->processNotModifiedSave($object); + $this->commit(); + $object->setHasDataChanges(false); + return $this; + } + $object->validateBeforeSave(); + $object->beforeSave(); + if ($object->isSaveAllowed()) { + $this->_serializeFields($object); + $this->_beforeSave($object); + $this->_checkUnique($object); + $this->objectRelationProcessor->validateDataIntegrity($this->getMainTable(), $object->getData()); + $this->entityManager->save(PageInterface::class, $object); + $this->unserializeFields($object); + $this->processAfterSaves($object); + } + $this->addCommitCallback([$object, 'afterCommitCallback'])->commit(); + $object->setHasDataChanges(false); + } catch (\Exception $e) { + $this->rollBack(); + $object->setHasDataChanges(true); + throw $e; + } + return $this; + } } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php index 401aac2d6900d..903448203d62f 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Collection.php @@ -5,6 +5,7 @@ */ namespace Magento\Cms\Model\ResourceModel\Page; +use Magento\Cms\Api\Data\PageInterface; use \Magento\Cms\Model\ResourceModel\AbstractCollection; /** @@ -98,7 +99,8 @@ public function addStoreFilter($store, $withAdmin = true) */ protected function _afterLoad() { - $this->performAfterLoad('cms_page_store', 'page_id'); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $this->performAfterLoad('cms_page_store', $entityMetadata->getLinkField()); $this->_previewFlag = false; return parent::_afterLoad(); @@ -111,6 +113,7 @@ protected function _afterLoad() */ protected function _renderFiltersBefore() { - $this->joinStoreRelationTable('cms_page_store', 'page_id'); + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $this->joinStoreRelationTable('cms_page_store', $entityMetadata->getLinkField()); } } diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php index 49fbeaa47d6d8..258391615b54a 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Grid/Collection.php @@ -26,6 +26,7 @@ class Collection extends PageCollection implements SearchResultInterface * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Model\Entity\MetadataPool $metadataPool * @param mixed|null $mainTable * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $eventPrefix * @param mixed $eventObject @@ -33,7 +34,7 @@ class Collection extends PageCollection implements SearchResultInterface * @param string $model * @param null $connection * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource - * + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -42,6 +43,7 @@ public function __construct( \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Model\Entity\MetadataPool $metadataPool, $mainTable, $eventPrefix, $eventObject, @@ -56,6 +58,7 @@ public function __construct( $fetchStrategy, $eventManager, $storeManager, + $metadataPool, $connection, $resource ); diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/ReadHandler.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/ReadHandler.php new file mode 100644 index 0000000000000..cc3edd8a98d03 --- /dev/null +++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/ReadHandler.php @@ -0,0 +1,50 @@ +metadataPool = $metadataPool; + $this->resourcePage = $resourcePage; + } + + /** + * @param string $entityType + * @param object $entity + * @return object + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute($entityType, $entity) + { + if ($entity->getId()) { + $stores = $this->resourcePage->lookupStoreIds((int)$entity->getId()); + $entity->setData('store_id', $stores); + } + return $entity; + } +} diff --git a/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/SaveHandler.php b/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/SaveHandler.php new file mode 100644 index 0000000000000..7bf832fdbc453 --- /dev/null +++ b/app/code/Magento/Cms/Model/ResourceModel/Page/Relation/Store/SaveHandler.php @@ -0,0 +1,78 @@ +metadataPool = $metadataPool; + $this->resourcePage = $resourcePage; + } + + /** + * @param string $entityType + * @param object $entity + * @return object + */ + public function execute($entityType, $entity) + { + $entityMetadata = $this->metadataPool->getMetadata($entityType); + $linkField = $entityMetadata->getLinkField(); + + $connection = $entityMetadata->getEntityConnection(); + + $oldStores = $this->resourcePage->lookupStoreIds((int)$entity->getId()); + $newStores = (array)$entity->getStores(); + if (empty($newStores)) { + $newStores = (array)$entity->getStoreId(); + } + + $table = $this->resourcePage->getTable('cms_page_store'); + + $delete = array_diff($oldStores, $newStores); + if ($delete) { + $where = [ + $linkField . ' = ?' => (int)$entity->getData($linkField), + 'store_id IN (?)' => $delete, + ]; + $connection->delete($table, $where); + } + + $insert = array_diff($newStores, $oldStores); + if ($insert) { + $data = []; + foreach ($insert as $storeId) { + $data[] = [ + $linkField => (int)$entity->getData($linkField), + 'store_id' => (int)$storeId + ]; + } + $connection->insertMultiple($table, $data); + } + + return $entity; + } +} diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/ReadHandlerTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/ReadHandlerTest.php new file mode 100644 index 0000000000000..724c01bd7c23f --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/ReadHandlerTest.php @@ -0,0 +1,85 @@ +metadataPool = $this->getMockBuilder('Magento\Framework\Model\Entity\MetadataPool') + ->disableOriginalConstructor() + ->getMock(); + + $this->resourceBlock = $this->getMockBuilder('Magento\Cms\Model\ResourceModel\Block') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new ReadHandler( + $this->metadataPool, + $this->resourceBlock + ); + } + + public function testExecute() + { + $entityId = 1; + $storeId = 1; + + $this->resourceBlock->expects($this->once()) + ->method('lookupStoreIds') + ->willReturn([$storeId]); + + $block = $this->getMockBuilder('Magento\Cms\Model\Block') + ->disableOriginalConstructor() + ->getMock(); + + $block->expects($this->exactly(2)) + ->method('getId') + ->willReturn($entityId); + $block->expects($this->exactly(2)) + ->method('setData') + ->willReturnMap([ + ['store_id', [$storeId], $block], + ['stores', [$storeId], $block], + ]); + + $result = $this->model->execute('', $block); + $this->assertInstanceOf('Magento\Cms\Model\Block', $result); + } + + public function testExecuteWithNoId() + { + $block = $this->getMockBuilder('Magento\Cms\Model\Block') + ->disableOriginalConstructor() + ->getMock(); + + $block->expects($this->once()) + ->method('getId') + ->willReturn(false); + + $result = $this->model->execute('', $block); + $this->assertInstanceOf('Magento\Cms\Model\Block', $result); + } +} diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/SaveHandlerTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/SaveHandlerTest.php new file mode 100644 index 0000000000000..704eed3984615 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Block/Relation/Store/SaveHandlerTest.php @@ -0,0 +1,119 @@ +metadataPool = $this->getMockBuilder('Magento\Framework\Model\Entity\MetadataPool') + ->disableOriginalConstructor() + ->getMock(); + + $this->resourceBlock = $this->getMockBuilder('Magento\Cms\Model\ResourceModel\Block') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new SaveHandler( + $this->metadataPool, + $this->resourceBlock + ); + } + + public function testExecute() + { + $entityId = 1; + $linkId = 2; + $oldStore = 1; + $newStore = 2; + $linkField = 'link_id'; + + $adapter = $this->getMockBuilder('Magento\Framework\DB\Adapter\AdapterInterface') + ->getMockForAbstractClass(); + + $whereForDelete = [ + $linkField . ' = ?' => $linkId, + 'store_id IN (?)' => [$oldStore], + ]; + $adapter->expects($this->once()) + ->method('delete') + ->with('cms_block_store', $whereForDelete) + ->willReturnSelf(); + + $whereForInsert = [ + $linkField => $linkId, + 'store_id' => $newStore, + ]; + $adapter->expects($this->once()) + ->method('insertMultiple') + ->with('cms_block_store', [$whereForInsert]) + ->willReturnSelf(); + + $entityMetadata = $this->getMockBuilder('Magento\Framework\Model\Entity\EntityMetadata') + ->disableOriginalConstructor() + ->getMock(); + $entityMetadata->expects($this->once()) + ->method('getEntityConnection') + ->willReturn($adapter); + $entityMetadata->expects($this->once()) + ->method('getLinkField') + ->willReturn($linkField); + + $this->metadataPool->expects($this->once()) + ->method('getMetadata') + ->with('Magento\Cms\Model\Block') + ->willReturn($entityMetadata); + + $this->resourceBlock->expects($this->once()) + ->method('lookupStoreIds') + ->willReturn([$oldStore]); + $this->resourceBlock->expects($this->once()) + ->method('getTable') + ->with('cms_block_store') + ->willReturn('cms_block_store'); + + $block = $this->getMockBuilder('Magento\Cms\Model\Block') + ->disableOriginalConstructor() + ->setMethods([ + 'getStores', + 'getId', + 'getData', + ]) + ->getMock(); + $block->expects($this->once()) + ->method('getStores') + ->willReturn($newStore); + $block->expects($this->once()) + ->method('getId') + ->willReturn($entityId); + $block->expects($this->exactly(2)) + ->method('getData') + ->with($linkField) + ->willReturn($linkId); + + $result = $this->model->execute('Magento\Cms\Model\Block', $block); + $this->assertInstanceOf('Magento\Cms\Model\Block', $result); + } +} diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/ReadHandlerTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/ReadHandlerTest.php new file mode 100644 index 0000000000000..7e32e6f1c8a59 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/ReadHandlerTest.php @@ -0,0 +1,83 @@ +metadataPool = $this->getMockBuilder('Magento\Framework\Model\Entity\MetadataPool') + ->disableOriginalConstructor() + ->getMock(); + + $this->resourcePage = $this->getMockBuilder('Magento\Cms\Model\ResourceModel\Page') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new ReadHandler( + $this->metadataPool, + $this->resourcePage + ); + } + + public function testExecute() + { + $entityId = 1; + $storeId = 1; + + $this->resourcePage->expects($this->once()) + ->method('lookupStoreIds') + ->willReturn([$storeId]); + + $page = $this->getMockBuilder('Magento\Cms\Model\Page') + ->disableOriginalConstructor() + ->getMock(); + + $page->expects($this->exactly(2)) + ->method('getId') + ->willReturn($entityId); + $page->expects($this->once()) + ->method('setData') + ->with('store_id', [$storeId]) + ->willReturnSelf(); + + $result = $this->model->execute('', $page); + $this->assertInstanceOf('Magento\Cms\Model\Page', $result); + } + + public function testExecuteWithNoId() + { + $page = $this->getMockBuilder('Magento\Cms\Model\Page') + ->disableOriginalConstructor() + ->getMock(); + + $page->expects($this->once()) + ->method('getId') + ->willReturn(false); + + $result = $this->model->execute('', $page); + $this->assertInstanceOf('Magento\Cms\Model\Page', $result); + } +} diff --git a/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/SaveHandlerTest.php b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/SaveHandlerTest.php new file mode 100644 index 0000000000000..1a7a486d2530b --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/Model/ResourceModel/Page/Relation/Store/SaveHandlerTest.php @@ -0,0 +1,123 @@ +metadataPool = $this->getMockBuilder('Magento\Framework\Model\Entity\MetadataPool') + ->disableOriginalConstructor() + ->getMock(); + + $this->resourcePage = $this->getMockBuilder('Magento\Cms\Model\ResourceModel\Page') + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new SaveHandler( + $this->metadataPool, + $this->resourcePage + ); + } + + public function testExecute() + { + $entityId = 1; + $linkId = 2; + $oldStore = 1; + $newStore = 2; + $linkField = 'link_id'; + + $adapter = $this->getMockBuilder('Magento\Framework\DB\Adapter\AdapterInterface') + ->getMockForAbstractClass(); + + $whereForDelete = [ + $linkField . ' = ?' => $linkId, + 'store_id IN (?)' => [$oldStore], + ]; + $adapter->expects($this->once()) + ->method('delete') + ->with('cms_page_store', $whereForDelete) + ->willReturnSelf(); + + $whereForInsert = [ + $linkField => $linkId, + 'store_id' => $newStore, + ]; + $adapter->expects($this->once()) + ->method('insertMultiple') + ->with('cms_page_store', [$whereForInsert]) + ->willReturnSelf(); + + $entityMetadata = $this->getMockBuilder('Magento\Framework\Model\Entity\EntityMetadata') + ->disableOriginalConstructor() + ->getMock(); + $entityMetadata->expects($this->once()) + ->method('getEntityConnection') + ->willReturn($adapter); + $entityMetadata->expects($this->once()) + ->method('getLinkField') + ->willReturn($linkField); + + $this->metadataPool->expects($this->once()) + ->method('getMetadata') + ->with('Magento\Cms\Model\Page') + ->willReturn($entityMetadata); + + $this->resourcePage->expects($this->once()) + ->method('lookupStoreIds') + ->willReturn([$oldStore]); + $this->resourcePage->expects($this->once()) + ->method('getTable') + ->with('cms_page_store') + ->willReturn('cms_page_store'); + + $page = $this->getMockBuilder('Magento\Cms\Model\Page') + ->disableOriginalConstructor() + ->setMethods([ + 'getStores', + 'getStoreId', + 'getId', + 'getData', + ]) + ->getMock(); + $page->expects($this->once()) + ->method('getStores') + ->willReturn(null); + $page->expects($this->once()) + ->method('getStoreId') + ->willReturn($newStore); + $page->expects($this->once()) + ->method('getId') + ->willReturn($entityId); + $page->expects($this->exactly(2)) + ->method('getData') + ->with($linkField) + ->willReturn($linkId); + + $result = $this->model->execute('Magento\Cms\Model\Page', $page); + $this->assertInstanceOf('Magento\Cms\Model\Page', $result); + } +} diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index 0c958b7bd00f7..1a06460a00949 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -100,4 +100,46 @@ CmsGirdFilterPool + + + + + cms_page + page_id + + + cms_block + block_id + + + + + + + + + + Magento\Cms\Model\ResourceModel\Page\Relation\Store\ReadHandler + + + Magento\Cms\Model\ResourceModel\Page\Relation\Store\SaveHandler + + + Magento\Cms\Model\ResourceModel\Page\Relation\Store\SaveHandler + + + + + Magento\Cms\Model\ResourceModel\Block\Relation\Store\ReadHandler + + + Magento\Cms\Model\ResourceModel\Block\Relation\Store\SaveHandler + + + Magento\Cms\Model\ResourceModel\Block\Relation\Store\SaveHandler + + + + + diff --git a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php index 03576bf2e9d2e..67637f80f7093 100644 --- a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php +++ b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php @@ -54,22 +54,35 @@ public function __construct( * @param AbstractElement $element * @return string */ - public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) + public function render(AbstractElement $element) { $this->setElement($element); - $html = $this->_getHeaderHtml($element); + $header = $this->_getHeaderHtml($element); + $elements = $this->_getChildrenElementsHtml($element); + + $footer = $this->_getFooterHtml($element); + + return $header . $elements . $footer; + } + + /** + * @param AbstractElement $element + * @return string + */ + protected function _getChildrenElementsHtml(AbstractElement $element) + { + $elements = ''; foreach ($element->getElements() as $field) { if ($field instanceof \Magento\Framework\Data\Form\Element\Fieldset) { - $html .= '' . $field->toHtml() . ''; + $elements .= '' + . '' . $field->toHtml() . ''; } else { - $html .= $field->toHtml(); + $elements .= $field->toHtml(); } } - $html .= $this->_getFooterHtml($element); - - return $html; + return $elements; } /** diff --git a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php index 689d408926d0c..ec5419771a6fd 100644 --- a/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php +++ b/app/code/Magento/Developer/Console/Command/XmlCatalogGenerateCommand.php @@ -12,7 +12,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Magento\Framework\App\Filesystem\DirectoryList; /** * Class XmlCatalogGenerateCommand Generates dictionary of URNs for the IDE @@ -42,9 +41,9 @@ class XmlCatalogGenerateCommand extends Command private $urnResolver; /** - * @var \Magento\Framework\Filesystem\Directory\ReadInterface + * @var \Magento\Framework\Filesystem\Directory\ReadFactory */ - private $rootDirRead; + private $readFactory; /** * Supported formats @@ -56,19 +55,19 @@ class XmlCatalogGenerateCommand extends Command /** * @param \Magento\Framework\App\Utility\Files $filesUtility * @param \Magento\Framework\Config\Dom\UrnResolver $urnResolver - * @param \Magento\Framework\Filesystem $filesystemFactory + * @param \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory * @param \Magento\Developer\Model\XmlCatalog\Format\FormatInterface[] $formats */ public function __construct( \Magento\Framework\App\Utility\Files $filesUtility, \Magento\Framework\Config\Dom\UrnResolver $urnResolver, - \Magento\Framework\Filesystem $filesystemFactory, + \Magento\Framework\Filesystem\Directory\ReadFactory $readFactory, array $formats = [] ) { $this->filesUtility = $filesUtility; $this->urnResolver = $urnResolver; $this->formats = $formats; - $this->rootDirRead = $filesystemFactory->getDirectoryRead(DirectoryList::ROOT); + $this->readFactory = $readFactory; parent::__construct(); } @@ -111,9 +110,10 @@ private function getUrnDictionary(OutputInterface $output) $urns = []; foreach ($files as $file) { - $content = $this->rootDirRead->readFile( - $this->rootDirRead->getRelativePath($file[0]) - ); + $fileDir = dirname($file[0]); + $fileName = basename($file[0]); + $read = $this->readFactory->create($fileDir); + $content = $read->readFile($fileName); $matches = []; preg_match_all('/schemaLocation="(urn\:magento\:[^"]*)"/i', $content, $matches); if (isset($matches[1])) { diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php index c894b93af9050..10d95dc3b6aa4 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/XmlCatalogGenerateCommandTest.php @@ -42,27 +42,23 @@ public function testExecuteBadType() )->will($this->returnValue(null)); $formats = ['phpstorm' => $phpstormFormatMock]; - $filesystem = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $readFactory = $this->getMock('Magento\Framework\Filesystem\Directory\ReadFactory', [], [], '', false); $readDirMock = $this->getMock('\Magento\Framework\Filesystem\Directory\ReadInterface', [], [], '', false); $content = file_get_contents($fixtureXmlFile); - $readDirMock->expects($this->once()) - ->method('getRelativePath') - ->with($this->equalTo($fixtureXmlFile)) - ->will($this->returnValue('test')); $readDirMock->expects($this->once()) ->method('readFile') - ->with($this->equalTo('test')) + ->with($this->equalTo('test.xml')) ->will($this->returnValue($content)); - $filesystem->expects($this->once()) - ->method('getDirectoryRead') + $readFactory->expects($this->once()) + ->method('create') ->will($this->returnValue($readDirMock)); $this->command = new XmlCatalogGenerateCommand( $filesMock, $urnResolverMock, - $filesystem, + $readFactory, $formats ); diff --git a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php index 963f61633c1ac..51c07dd02d603 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php @@ -13,7 +13,7 @@ use Magento\Framework\Model\Operation\ContextHandlerInterface; /** - * Class ReadHandler + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ReadHandler { @@ -91,43 +91,6 @@ protected function getActionContext($entityType, $data) ); } - /** - * @param string $entityType - * @param array $entityData - * @return array - * @throws \Exception - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function executeOldGoodImplementation($entityType, $entityData) - { - $data = []; - $metadata = $this->metadataPool->getMetadata($entityType); - /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ - if ($metadata->getEavEntityType()) { - foreach ($this->getAttributes($entityType) as $attribute) { - if (!$attribute->isStatic()) { - $select = $metadata->getEntityConnection()->select() - ->from($attribute->getBackend()->getTable(), ['value']) - ->where($metadata->getLinkField() . ' = ?', $entityData[$metadata->getLinkField()]) - ->where('attribute_id = ?', $attribute->getAttributeId()); - $context = $this->getActionContext($entityType, $entityData); - foreach ($context as $field => $value) { - //TODO: if (in table exists context field) - $select->where( - $metadata->getEntityConnection()->quoteIdentifier($field) . ' IN (?)', - $value - )->order($field . ' DESC'); - } - $value = $metadata->getEntityConnection()->fetchOne($select); - if ($value !== false) { - $data[$attribute->getAttributeCode()] = $value; - } - } - } - } - return $data; - } - /** * @param string $entityType * @param array $entityData diff --git a/app/code/Magento/PageCache/Helper/Data.php b/app/code/Magento/PageCache/Helper/Data.php index aff4d6d6a6a27..3334243148cca 100644 --- a/app/code/Magento/PageCache/Helper/Data.php +++ b/app/code/Magento/PageCache/Helper/Data.php @@ -20,6 +20,19 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper */ const PRIVATE_MAX_AGE_CACHE = 31536000; + /** + * @var \Magento\Framework\Session\Config + */ + protected $config; + + /** + * @param \Magento\Framework\Session\Config $config + */ + public function __construct(\Magento\Framework\Session\Config $config) + { + $this->config; + } + /** * Retrieve url * @@ -31,4 +44,12 @@ public function getUrl($route, array $params = []) { return $this->_getUrl($route, $params); } + + /** + * @return string + */ + public function getDomain() + { + return $this->config->getValidDomain(); + } } diff --git a/app/code/Magento/Payment/Gateway/CommandExecutorInterface.php b/app/code/Magento/Payment/Gateway/CommandExecutorInterface.php index 0ddb423924e01..98c6dd4dbaf43 100644 --- a/app/code/Magento/Payment/Gateway/CommandExecutorInterface.php +++ b/app/code/Magento/Payment/Gateway/CommandExecutorInterface.php @@ -7,6 +7,7 @@ /** * Interface CommandExecutorInterface + * @api */ interface CommandExecutorInterface { diff --git a/app/code/Magento/Payment/Gateway/Config/Config.php b/app/code/Magento/Payment/Gateway/Config/Config.php index 8a0035e0ddf26..8de902d878ce2 100644 --- a/app/code/Magento/Payment/Gateway/Config/Config.php +++ b/app/code/Magento/Payment/Gateway/Config/Config.php @@ -19,23 +19,23 @@ class Config implements ConfigInterface private $scopeConfig; /** - * @var string + * @var string|null */ private $methodCode; /** - * @var string + * @var string|null */ private $pathPattern; /** * @param ScopeConfigInterface $scopeConfig - * @param string $methodCode + * @param string|null $methodCode * @param string $pathPattern */ public function __construct( ScopeConfigInterface $scopeConfig, - $methodCode = '', + $methodCode = null, $pathPattern = self::DEFAULT_PATH_PATTERN ) { $this->scopeConfig = $scopeConfig; @@ -75,6 +75,10 @@ public function setPathPattern($pathPattern) */ public function getValue($field, $storeId = null) { + if ($this->methodCode === null || $this->pathPattern === null) { + return null; + } + return $this->scopeConfig->getValue( sprintf($this->pathPattern, $this->methodCode, $field), ScopeInterface::SCOPE_STORE, diff --git a/app/code/Magento/Payment/Gateway/Config/ConfigFactory.php b/app/code/Magento/Payment/Gateway/Config/ConfigFactory.php new file mode 100644 index 0000000000000..8bbb32c274ed9 --- /dev/null +++ b/app/code/Magento/Payment/Gateway/Config/ConfigFactory.php @@ -0,0 +1,45 @@ +om = $om; + } + + /** + * @param string|null $paymentCode + * @param string|null $pathPattern + * @return mixed + */ + public function create($paymentCode = null, $pathPattern = null) + { + $arguments = [ + 'methodCode' => $paymentCode + ]; + + if ($pathPattern !== null) { + $arguments['pathPattern'] = $pathPattern; + } + + return $this->om->create(Config::class, $arguments); + } +} diff --git a/app/code/Magento/Payment/Gateway/ConfigFactoryInterface.php b/app/code/Magento/Payment/Gateway/ConfigFactoryInterface.php new file mode 100644 index 0000000000000..0b4b26b8eb039 --- /dev/null +++ b/app/code/Magento/Payment/Gateway/ConfigFactoryInterface.php @@ -0,0 +1,21 @@ +ccConfig->createAsset('Magento_Payment::images/cc/' . strtolower($code) . '.png'); - $placeholder = $this->assetSource->findRelativeSourceFilePath($asset); + $placeholder = $this->assetSource->findSource($asset); if ($placeholder) { list($width, $height) = getimagesize($asset->getSourceFile()); $icons[$code] = [ diff --git a/app/code/Magento/Payment/Model/Info.php b/app/code/Magento/Payment/Model/Info.php index 4ba972b321f11..87aac12d4fa4f 100644 --- a/app/code/Magento/Payment/Model/Info.php +++ b/app/code/Magento/Payment/Model/Info.php @@ -188,9 +188,12 @@ public function unsAdditionalInformation($key = null) if ($key && isset($this->_additionalInformation[$key])) { unset($this->_additionalInformation[$key]); return $this->setData('additional_information', $this->_additionalInformation); + } elseif (null === $key) { + $this->_additionalInformation = []; + return $this->unsetData('additional_information'); } - $this->_additionalInformation = []; - return $this->unsetData('additional_information'); + + return $this; } /** diff --git a/app/code/Magento/Payment/Model/Method/Adapter.php b/app/code/Magento/Payment/Model/Method/Adapter.php index 462f4e18fba86..5c1198b7b86de 100644 --- a/app/code/Magento/Payment/Model/Method/Adapter.php +++ b/app/code/Magento/Payment/Model/Method/Adapter.php @@ -7,6 +7,7 @@ use Magento\Framework\DataObject; use Magento\Payment\Model\InfoInterface; +use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\Quote\Api\Data\CartInterface; use Magento\Payment\Model\MethodInterface; use Magento\Framework\Event\ManagerInterface; @@ -592,12 +593,21 @@ public function assignData(\Magento\Framework\DataObject $data) $this->eventManager->dispatch( 'payment_method_assign_data_' . $this->getCode(), [ - 'method' => $this, - 'data' => $data + AbstractDataAssignObserver::METHOD_CODE => $this, + AbstractDataAssignObserver::MODEL_CODE => $this->getInfoInstance(), + AbstractDataAssignObserver::DATA_CODE => $data + ] + ); + + $this->eventManager->dispatch( + 'payment_method_assign_data', + [ + AbstractDataAssignObserver::METHOD_CODE => $this, + AbstractDataAssignObserver::MODEL_CODE => $this->getInfoInstance(), + AbstractDataAssignObserver::DATA_CODE => $data ] ); - $this->getInfoInstance()->addData($data->getData()); return $this; } diff --git a/app/code/Magento/Payment/Model/MethodInterface.php b/app/code/Magento/Payment/Model/MethodInterface.php index 3819a1e62f010..53e1d73c9c8f0 100644 --- a/app/code/Magento/Payment/Model/MethodInterface.php +++ b/app/code/Magento/Payment/Model/MethodInterface.php @@ -344,6 +344,7 @@ public function assignData(DataObject $data); * * @param CartInterface|null $quote * @return bool + * @api */ public function isAvailable(CartInterface $quote = null); @@ -352,6 +353,7 @@ public function isAvailable(CartInterface $quote = null); * * @param int|null $storeId * @return bool + * @api */ public function isActive($storeId = null); @@ -365,7 +367,6 @@ public function isActive($storeId = null); * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @api - * @deprecated */ public function initialize($paymentAction, $stateObject); diff --git a/app/code/Magento/Payment/Observer/AbstractDataAssignObserver.php b/app/code/Magento/Payment/Observer/AbstractDataAssignObserver.php index 930dfc702ad2f..dcccce5680e54 100644 --- a/app/code/Magento/Payment/Observer/AbstractDataAssignObserver.php +++ b/app/code/Magento/Payment/Observer/AbstractDataAssignObserver.php @@ -8,14 +8,22 @@ use Magento\Framework\DataObject; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Payment\Model\InfoInterface; use Magento\Payment\Model\MethodInterface; +/** + * Class AbstractDataAssignObserver + * @package Magento\Payment\Observer + * @api + */ abstract class AbstractDataAssignObserver implements ObserverInterface { const METHOD_CODE = 'method'; const DATA_CODE = 'data'; + const MODEL_CODE = 'payment_model'; + /** * Reads method argument * @@ -27,6 +35,17 @@ protected function readMethodArgument(Observer $observer) return $this->readArgument($observer, static::METHOD_CODE, MethodInterface::class); } + /** + * Reads payment model argument + * + * @param Observer $observer + * @return InfoInterface + */ + protected function readPaymentModelArgument(Observer $observer) + { + return $this->readArgument($observer, static::MODEL_CODE, InfoInterface::class); + } + /** * Reads data argument * diff --git a/app/code/Magento/Payment/Test/Unit/Gateway/Request/BuilderCompositeTest.php b/app/code/Magento/Payment/Test/Unit/Gateway/Request/BuilderCompositeTest.php index 2c31c318db678..3655bbb35fa79 100644 --- a/app/code/Magento/Payment/Test/Unit/Gateway/Request/BuilderCompositeTest.php +++ b/app/code/Magento/Payment/Test/Unit/Gateway/Request/BuilderCompositeTest.php @@ -37,18 +37,13 @@ public function testBuildEmpty() static::assertEquals([], $builder->build([])); } - public function testBuild() + /** + * @param array $expected + * @covers \Magento\Payment\Gateway\Request\BuilderComposite::build + * @dataProvider buildDataProvider + */ + public function testBuild(array $expected) { - $expectedRequest = [ - 'user' => 'Mrs G. Crump', - 'url' => 'https://url.in', - 'amount' => 10.00, - 'currency' => 'pound', - 'address' => '46 Egernon Crescent', - 'item' => 'gas cooker', - 'quantity' => 1 - ]; - $tMapFactory = $this->getMockBuilder('Magento\Framework\ObjectManager\TMapFactory') ->disableOriginalConstructor() ->setMethods(['create']) @@ -67,25 +62,27 @@ public function testBuild() ->method('build') ->willReturn( [ - 'user' => 'Mrs G. Crump', - 'address' => '46 Egernon Crescent' + 'user' => $expected['user'], + 'address' => $expected['address'] ] ); $productBuilder->expects(static::once()) ->method('build') ->willReturn( [ - 'amount' => 10.00, - 'currency' => 'pound', - 'item' => 'gas cooker', - 'quantity' => 1 + 'amount' => $expected['amount'], + 'currency' => $expected['currency'], + 'item' => $expected['item'], + 'quantity' => $expected['quantity'], + 'options' => ['product' => $expected['options']['product']] ] ); $magentoBuilder->expects(static::once()) ->method('build') ->willReturn( [ - 'url' => 'https://url.in' + 'url' => $expected['url'], + 'options' => ['magento' => $expected['options']['magento']] ] ); @@ -115,6 +112,45 @@ public function testBuild() ] ); - static::assertEquals($expectedRequest, $builder->build([])); + static::assertEquals($expected, $builder->build([])); + } + + /** + * Get list of variations + */ + public function buildDataProvider() + { + return [ + [[ + 'user' => 'Mrs G. Crump', + 'address' => '46 Egernon Crescent', + 'amount' => 10.00, + 'currency' => 'pound', + 'item' => 'gas cooker', + 'quantity' => 1, + 'options' => ['product' => '', 'magento' => 'magento'], + 'url' => 'https://url.in', + ]], + [[ + 'user' => 'John Doe', + 'address' => '46 Main Street', + 'amount' => 250.00, + 'currency' => 'usd', + 'item' => 'phone', + 'quantity' => 2, + 'options' => ['product' => 'product', 'magento' => 'magento'], + 'url' => 'https://url.io', + ]], + [[ + 'user' => 'John Smit', + 'address' => '46 Egernon Crescent', + 'amount' => 1100.00, + 'currency' => 'usd', + 'item' => 'notebook', + 'quantity' => 1, + 'options' => ['product' => ['discount' => ['price' => 2.00]], 'magento' => 'magento'], + 'url' => 'http://url.ua', + ]], + ]; } } diff --git a/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php b/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php index 217ac20644e7e..233764c5316b8 100644 --- a/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php +++ b/app/code/Magento/Payment/Test/Unit/Model/CcConfigProviderTest.php @@ -77,7 +77,7 @@ public function testGetConfig() [$ccAvailableTypesMock['ae']['fileId']] )->willReturn($assetMock); $this->assetSourceMock->expects($this->atLeastOnce()) - ->method('findRelativeSourceFilePath') + ->method('findSource') ->with($assetMock) ->willReturnOnConsecutiveCalls( $ccAvailableTypesMock['vi']['path'], diff --git a/app/code/Magento/Payment/etc/di.xml b/app/code/Magento/Payment/etc/di.xml index 2d5be54204f42..b7b20ac1e4349 100644 --- a/app/code/Magento/Payment/etc/di.xml +++ b/app/code/Magento/Payment/etc/di.xml @@ -6,6 +6,9 @@ */ --> + + + payment.xml @@ -24,7 +27,6 @@ Magento\Payment\Model\Config\Data - Magento\Payment\Gateway\Config\Config diff --git a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php index 6f20d380b723d..2cd04398ecc86 100644 --- a/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php +++ b/app/code/Magento/Paypal/Block/Adminhtml/System/Config/ApiWizard.php @@ -52,9 +52,22 @@ protected function _getElementHtml(\Magento\Framework\Data\Form\Element\Abstract $originalData = $element->getOriginalData(); $this->addData( [ + // Global + 'query' => $this->createQuery( + [ + 'partnerId' => $originalData['partner_id'], + 'partnerLogoUrl' => $this->_assetRepo->getUrl($originalData['partner_logo_url']), + 'receiveCredentials' => $originalData['receive_credentials'], + 'showPermissions' => $originalData['show_permissions'], + 'displayMode' => $originalData['display_mode'], + 'productIntentID' => $originalData['product_intent_id'], + ] + ), + // Live 'button_label' => __($originalData['button_label']), 'button_url' => $originalData['button_url'], 'html_id' => $element->getHtmlId(), + // Sandbox 'sandbox_button_label' => __($originalData['sandbox_button_label']), 'sandbox_button_url' => $originalData['sandbox_button_url'], 'sandbox_html_id' => 'sandbox_' . $element->getHtmlId(), @@ -62,4 +75,21 @@ protected function _getElementHtml(\Magento\Framework\Data\Form\Element\Abstract ); return $this->_toHtml(); } + + /** + * Create request query + * + * @param array $requestData + * @return string + */ + private function createQuery(array $requestData) + { + $query = []; + + foreach ($requestData as $name => $value) { + $query[] = sprintf('%s=%s', $name, $value); + } + + return implode('&', $query); + } } diff --git a/app/code/Magento/Paypal/Model/Api/AbstractApi.php b/app/code/Magento/Paypal/Model/Api/AbstractApi.php index d5a7298b886b3..590854023a136 100644 --- a/app/code/Magento/Paypal/Model/Api/AbstractApi.php +++ b/app/code/Magento/Paypal/Model/Api/AbstractApi.php @@ -5,8 +5,8 @@ */ namespace Magento\Paypal\Model\Api; +use Magento\Payment\Helper\Formatter; use Magento\Payment\Model\Method\Logger; -use Magento\Paypal\Helper\Formatter; /** * Abstract class for Paypal API wrappers diff --git a/app/code/Magento/Paypal/Model/Config.php b/app/code/Magento/Paypal/Model/Config.php index 697b66ef7c823..cc90c97921ea1 100644 --- a/app/code/Magento/Paypal/Model/Config.php +++ b/app/code/Magento/Paypal/Model/Config.php @@ -8,7 +8,7 @@ namespace Magento\Paypal\Model; -use Magento\Paypal\Helper\Formatter; +use Magento\Payment\Helper\Formatter; /** * Config model that is aware of all \Magento\Paypal payment methods diff --git a/app/code/Magento/Paypal/Model/Hostedpro/Request.php b/app/code/Magento/Paypal/Model/Hostedpro/Request.php index ba009c1416e74..7cbacff0c7659 100644 --- a/app/code/Magento/Paypal/Model/Hostedpro/Request.php +++ b/app/code/Magento/Paypal/Model/Hostedpro/Request.php @@ -8,10 +8,10 @@ use Magento\Customer\Helper\Address; use Magento\Framework\DataObject; use Magento\Framework\Locale\Resolver; +use Magento\Payment\Helper\Formatter; use Magento\Paypal\Model\Hostedpro; use Magento\Sales\Model\Order; use Magento\Tax\Helper\Data; -use Magento\Paypal\Helper\Formatter; /** * Website Payments Pro Hosted Solution request model to get token. diff --git a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml index b95f686a3db73..31cbe04f0b1ca 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system/express_checkout.xml @@ -66,16 +66,31 @@ - Get Credentials from PayPal - - - + + Get Credentials from PayPal + + + + Sandbox Credentials - + + + + NB9WWHYEMVUMS + + Magento_Backend/web/images/logo-magento.png + + FALSE + + FALSE + + embedded + + pp_express + Magento\Paypal\Block\Adminhtml\System\Config\ApiWizard - diff --git a/app/code/Magento/Paypal/view/adminhtml/templates/system/config/api_wizard.phtml b/app/code/Magento/Paypal/view/adminhtml/templates/system/config/api_wizard.phtml index 3f729e92cbf74..1ee532657b06f 100644 --- a/app/code/Magento/Paypal/view/adminhtml/templates/system/config/api_wizard.phtml +++ b/app/code/Magento/Paypal/view/adminhtml/templates/system/config/api_wizard.phtml @@ -10,22 +10,27 @@ */ ?>
- - +
+ + + escapeHtml($block->getButtonLabel()); ?> + + escapeHtml($block->getSandboxButtonLabel()); ?> +
diff --git a/app/code/Magento/Rule/Model/AbstractModel.php b/app/code/Magento/Rule/Model/AbstractModel.php index 07bc7957bfbf8..6f2ca4f045fb8 100644 --- a/app/code/Magento/Rule/Model/AbstractModel.php +++ b/app/code/Magento/Rule/Model/AbstractModel.php @@ -4,12 +4,13 @@ * See COPYING.txt for license details. */ +namespace Magento\Rule\Model; + /** * Abstract Rule entity data model + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -namespace Magento\Rule\Model; - -abstract class AbstractModel extends \Magento\Framework\Model\AbstractModel +abstract class AbstractModel extends \Magento\Framework\Model\AbstractExtensibleModel { /** * Store rule combine conditions model @@ -75,19 +76,23 @@ abstract public function getActionsInstance(); protected $_localeDate; /** - * Constructor + * AbstractModel constructor. * * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Framework\Data\FormFactory $formFactory * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, @@ -96,7 +101,15 @@ public function __construct( ) { $this->_formFactory = $formFactory; $this->_localeDate = $localeDate; - parent::__construct($context, $registry, $resource, $resourceCollection, $data); + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $resource, + $resourceCollection, + $data + ); } /** @@ -118,13 +131,13 @@ public function beforeSave() // Serialize conditions if ($this->getConditions()) { $this->setConditionsSerialized(serialize($this->getConditions()->asArray())); - $this->unsConditions(); + $this->_conditions = null; } // Serialize actions if ($this->getActions()) { $this->setActionsSerialized(serialize($this->getActions()->asArray())); - $this->unsActions(); + $this->_actions = null; } /** diff --git a/app/code/Magento/Rule/Model/ResourceModel/AbstractResource.php b/app/code/Magento/Rule/Model/ResourceModel/AbstractResource.php index 0655ddb25a975..0e92eb3ffe60b 100644 --- a/app/code/Magento/Rule/Model/ResourceModel/AbstractResource.php +++ b/app/code/Magento/Rule/Model/ResourceModel/AbstractResource.php @@ -43,14 +43,14 @@ abstract class AbstractResource extends \Magento\Framework\Model\ResourceModel\D */ public function _beforeSave(\Magento\Framework\Model\AbstractModel $object) { - $fromDate = $object->getFromDate(); + $fromDate = $object->getData('from_date'); if ($fromDate instanceof \DateTime) { $object->setFromDate($fromDate->format('Y-m-d H:i:s')); } elseif (!is_string($fromDate) || empty($fromDate)) { $object->setFromDate(null); } - $toDate = $object->getToDate(); + $toDate = $object->getData('to_date'); if ($toDate instanceof \DateTime) { $object->setToDate($toDate->format('Y-m-d H:i:s')); } elseif (!is_string($toDate) || empty($toDate)) { diff --git a/app/code/Magento/Sales/Api/Data/TransactionInterface.php b/app/code/Magento/Sales/Api/Data/TransactionInterface.php index 1e0de41ea000d..1f2eb75645d0e 100644 --- a/app/code/Magento/Sales/Api/Data/TransactionInterface.php +++ b/app/code/Magento/Sales/Api/Data/TransactionInterface.php @@ -13,6 +13,24 @@ */ interface TransactionInterface extends \Magento\Framework\Api\ExtensibleDataInterface { + /**#@+ + * Supported transaction types + * @var string + */ + const TYPE_PAYMENT = 'payment'; + + const TYPE_ORDER = 'order'; + + const TYPE_AUTH = 'authorization'; + + const TYPE_CAPTURE = 'capture'; + + const TYPE_VOID = 'void'; + + const TYPE_REFUND = 'refund'; + + /**#@-*/ + /**#@+ * Constants for keys of data array. Identical to the name of the getter in snake case. */ diff --git a/app/code/Magento/Sales/Model/Order/Payment/Info.php b/app/code/Magento/Sales/Model/Order/Payment/Info.php index 0abf5b513b82f..83f75d29e3c7c 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Info.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Info.php @@ -193,9 +193,12 @@ public function unsAdditionalInformation($key = null) if ($key && isset($this->additionalInformation[$key])) { unset($this->additionalInformation[$key]); return $this->setData('additional_information', $this->additionalInformation); + } elseif (null === $key) { + $this->additionalInformation = []; + return $this->unsetData('additional_information'); } - $this->additionalInformation = []; - return $this->unsetData('additional_information'); + + return $this; } /** diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 91269d66fbd98..37819329e8dd2 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -26,24 +26,6 @@ */ class Transaction extends AbstractModel implements TransactionInterface { - /**#@+ - * Supported transaction types - * @var string - */ - const TYPE_PAYMENT = 'payment'; - - const TYPE_ORDER = 'order'; - - const TYPE_AUTH = 'authorization'; - - const TYPE_CAPTURE = 'capture'; - - const TYPE_VOID = 'void'; - - const TYPE_REFUND = 'refund'; - - /**#@-*/ - /** * Raw details key in additional info */ diff --git a/app/code/Magento/SalesRule/Model/Rule.php b/app/code/Magento/SalesRule/Model/Rule.php index 4ecc75f77dd89..b6c23fc4cb2a5 100644 --- a/app/code/Magento/SalesRule/Model/Rule.php +++ b/app/code/Magento/SalesRule/Model/Rule.php @@ -175,22 +175,26 @@ class Rule extends \Magento\Rule\Model\AbstractModel /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Framework\Data\FormFactory $formFactory * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\SalesRule\Model\CouponFactory $couponFactory - * @param \Magento\SalesRule\Model\Coupon\CodegeneratorFactory $codegenFactory - * @param \Magento\SalesRule\Model\Rule\Condition\CombineFactory $condCombineFactory - * @param \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory $condProdCombineF - * @param \Magento\SalesRule\Model\ResourceModel\Coupon\Collection $couponCollection + * @param CouponFactory $couponFactory + * @param Coupon\CodegeneratorFactory $codegenFactory + * @param Rule\Condition\CombineFactory $condCombineFactory + * @param Rule\Condition\Product\CombineFactory $condProdCombineF + * @param ResourceModel\Coupon\Collection $couponCollection * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, + \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, + \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, \Magento\Framework\Data\FormFactory $formFactory, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\SalesRule\Model\CouponFactory $couponFactory, @@ -209,7 +213,17 @@ public function __construct( $this->_condProdCombineF = $condProdCombineF; $this->_couponCollection = $couponCollection; $this->_storeManager = $storeManager; - parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data); + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $formFactory, + $localeDate, + $resource, + $resourceCollection, + $data + ); } /** diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php index 5293d31d42a06..65243ee594e6e 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/RuleTest.php @@ -18,6 +18,16 @@ class RuleTest extends \PHPUnit_Framework_TestCase */ protected $coupon; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\SalesRule\Model\Rule\Condition\CombineFactory + */ + protected $conditionCombineFactoryMock; + + /** + * @var \Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $condProdCombineFactoryMock; + public function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -35,10 +45,22 @@ public function setUp() ->method('create') ->willReturn($this->coupon); + $this->conditionCombineFactoryMock = $this->getMockBuilder( + '\Magento\SalesRule\Model\Rule\Condition\CombineFactory' + )->disableOriginalConstructor() + ->getMock(); + + $this->condProdCombineFactoryMock = $this->getMockBuilder( + '\Magento\SalesRule\Model\Rule\Condition\Product\CombineFactory' + )->disableOriginalConstructor() + ->getMock(); + $this->model = $objectManager->getObject( 'Magento\SalesRule\Model\Rule', [ - 'couponFactory' => $couponFactory + 'couponFactory' => $couponFactory, + 'condCombineFactory' => $this->conditionCombineFactoryMock, + 'condProdCombineF' => $this->condProdCombineFactoryMock, ] ); } @@ -70,4 +92,62 @@ public function testLoadCouponCode() $this->model->loadCouponCode(); $this->assertEquals(1, $this->model->getUsesPerCoupon()); } + + public function testBeforeSaveResetConditionToNull() + { + $conditionMock = $this->setupConditionMock(); + + //Make sure that we reset _condition in beforeSave method + $this->conditionCombineFactoryMock->expects($this->exactly(2)) + ->method('create') + ->willReturn($conditionMock); + + $prodConditionMock = $this->setupProdConditionMock(); + $this->condProdCombineFactoryMock->expects($this->exactly(2)) + ->method('create') + ->willReturn($prodConditionMock); + + $this->model->beforeSave(); + $this->model->getConditions(); + $this->model->getActions(); + } + + protected function setupProdConditionMock() + { + $prodConditionMock = $this->getMockBuilder('\Magento\SalesRule\Model\Rule\Condition\Product\Combine') + ->disableOriginalConstructor() + ->setMethods(['setRule', 'setId', 'loadArray', 'getConditions']) + ->getMock(); + + $prodConditionMock->expects($this->any()) + ->method('setRule') + ->willReturnSelf(); + $prodConditionMock->expects($this->any()) + ->method('setId') + ->willReturnSelf(); + $prodConditionMock->expects($this->any()) + ->method('getConditions') + ->willReturn([]); + + return $prodConditionMock; + } + + protected function setupConditionMock() + { + $conditionMock = $this->getMockBuilder('\Magento\SalesRule\Model\Rule\Condition\Combine') + ->disableOriginalConstructor() + ->setMethods(['setRule', 'setId', 'loadArray', 'getConditions']) + ->getMock(); + $conditionMock->expects($this->any()) + ->method('setRule') + ->willReturnSelf(); + $conditionMock->expects($this->any()) + ->method('setId') + ->willReturnSelf(); + $conditionMock->expects($this->any()) + ->method('getConditions') + ->willReturn([]); + + return $conditionMock; + } } diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Cms/Page.php b/app/code/Magento/Sitemap/Model/ResourceModel/Cms/Page.php index 07640bb6a7f2e..0baf5374fbfd5 100644 --- a/app/code/Magento/Sitemap/Model/ResourceModel/Cms/Page.php +++ b/app/code/Magento/Sitemap/Model/ResourceModel/Cms/Page.php @@ -5,6 +5,10 @@ */ namespace Magento\Sitemap\Model\ResourceModel\Cms; +use Magento\Cms\Api\Data\PageInterface; +use Magento\Framework\Model\Entity\MetadataPool; +use Magento\Framework\Model\ResourceModel\Db\Context; + /** * Sitemap cms page collection model * @@ -12,6 +16,25 @@ */ class Page extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { + /** + * @var MetadataPool + */ + protected $metadataPool; + + /** + * @param Context $context + * @param MetadataPool $metadataPool + * @param string $connectionName + */ + public function __construct( + Context $context, + MetadataPool $metadataPool, + $connectionName = null + ) { + $this->metadataPool = $metadataPool; + parent::__construct($context, $connectionName); + } + /** * Init resource model (catalog/category) * @@ -30,14 +53,15 @@ protected function _construct() */ public function getCollection($storeId) { - $pages = []; + $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class); + $linkField = $entityMetadata->getLinkField(); $select = $this->getConnection()->select()->from( ['main_table' => $this->getMainTable()], [$this->getIdFieldName(), 'url' => 'identifier', 'updated_at' => 'update_time'] )->join( ['store_table' => $this->getTable('cms_page_store')], - 'main_table.page_id = store_table.page_id', + "main_table.{$linkField} = store_table.$linkField", [] )->where( 'main_table.is_active = 1' @@ -49,6 +73,7 @@ public function getCollection($storeId) [0, $storeId] ); + $pages = []; $query = $this->getConnection()->query($select); while ($row = $query->fetch()) { $page = $this->_prepareObject($row); diff --git a/app/code/Magento/Vault/Api/Data/PaymentTokenInterface.php b/app/code/Magento/Vault/Api/Data/PaymentTokenInterface.php index e42aee5094645..acb372a4e3725 100644 --- a/app/code/Magento/Vault/Api/Data/PaymentTokenInterface.php +++ b/app/code/Magento/Vault/Api/Data/PaymentTokenInterface.php @@ -10,7 +10,7 @@ * * @api */ -interface PaymentTokenInterface extends \Magento\Framework\Api\ExtensibleDataInterface +interface PaymentTokenInterface { /**#@+ * Constants for keys of data array. Identical to the name of the getter in snake case. @@ -55,6 +55,10 @@ interface PaymentTokenInterface extends \Magento\Framework\Api\ExtensibleDataInt * Is vault payment record active. */ const IS_ACTIVE = 'is_active'; + /* + * Is vault payment token visible. + */ + const IS_VISIBLE = 'is_visible'; /** * Gets the entity ID. @@ -208,17 +212,18 @@ public function getIsActive(); public function setIsActive($isActive); /** - * Retrieve existing extension attributes object or create a new one. + * Gets is vault payment record visible. * - * @return \Magento\Vault\Api\Data\PaymentTokenExtensionInterface|null + * @return bool Is visible. + * @SuppressWarnings(PHPMD.BooleanGetMethodName) */ - public function getExtensionAttributes(); + public function getIsVisible(); /** - * Set an extension attributes object. + * Sets is vault payment record visible. * - * @param \Magento\Vault\Api\Data\PaymentTokenExtensionInterface $extensionAttributes + * @param bool $isVisible * @return $this */ - public function setExtensionAttributes(PaymentTokenExtensionInterface $extensionAttributes); + public function setIsVisible($isVisible); } diff --git a/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php b/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php index 684d792401f2a..e80a7f4b834db 100644 --- a/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php +++ b/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php @@ -5,7 +5,7 @@ */ namespace Magento\Vault\Api; -use Magento\Sales\Model\Order\Payment; +use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Vault\Api\Data\PaymentTokenInterface; /** @@ -32,18 +32,28 @@ public function getListByCustomerId($customerId); public function getByPaymentId($paymentId); /** - * Get payment token by gateway token Id. + * Get payment token by gateway token. * - * @param int $customerId Customer ID. * @param string $token The gateway token. - * @return \Magento\Vault\Api\Data\PaymentTokenInterface Payment token interface. + * @param string $paymentMethodCode + * @param int $customerId Customer ID. + * @return PaymentTokenInterface|null Payment token interface. + */ + public function getByGatewayToken($token, $paymentMethodCode, $customerId); + + /** + * Get payment token by public hash. + * + * @param string $hash Public hash. + * @param int $customerId Customer ID. + * @return PaymentTokenInterface|null Payment token interface. */ - public function getByGatewayToken($customerId, $token); + public function getByPublicHash($hash, $customerId); /** * @param PaymentTokenInterface $token - * @param Payment $payment + * @param OrderPaymentInterface $payment * @return bool */ - public function saveTokenWithPaymentLink(PaymentTokenInterface $token, Payment $payment); + public function saveTokenWithPaymentLink(PaymentTokenInterface $token, OrderPaymentInterface $payment); } diff --git a/app/code/Magento/Vault/Block/System/Config/EmptyFieldsetDecorator.php b/app/code/Magento/Vault/Block/System/Config/EmptyFieldsetDecorator.php new file mode 100644 index 0000000000000..fb45da905c322 --- /dev/null +++ b/app/code/Magento/Vault/Block/System/Config/EmptyFieldsetDecorator.php @@ -0,0 +1,26 @@ +_getChildrenElementsHtml($element); + if (empty($childrenHtml)) { + return ''; + } + + return parent::render($element); + } +} diff --git a/app/code/Magento/Vault/Block/System/Config/EmptySelectDecorator.php b/app/code/Magento/Vault/Block/System/Config/EmptySelectDecorator.php new file mode 100644 index 0000000000000..f2bc2604dcc1a --- /dev/null +++ b/app/code/Magento/Vault/Block/System/Config/EmptySelectDecorator.php @@ -0,0 +1,29 @@ +getData('values'))) { + return ''; + } + + return parent::render($element); + } +} diff --git a/app/code/Magento/Vault/Gateway/Config/ActiveHandler.php b/app/code/Magento/Vault/Gateway/Config/ActiveHandler.php index 84ab987a09f0d..ae98ee3556814 100644 --- a/app/code/Magento/Vault/Gateway/Config/ActiveHandler.php +++ b/app/code/Magento/Vault/Gateway/Config/ActiveHandler.php @@ -6,7 +6,7 @@ namespace Magento\Vault\Gateway\Config; use Magento\Payment\Gateway\ConfigInterface; -use Magento\Vault\Model\Adminhtml\Source\VaultPayment; +use Magento\Vault\Model\Adminhtml\Source\VaultProvidersMap; use Magento\Payment\Gateway\Config\ValueHandlerInterface; /** @@ -32,10 +32,10 @@ public function __construct(ConfigInterface $config) */ public function handle(array $subject, $storeId = null) { - $vaultPaymentCode = $this->config->getValue(VaultPayment::VALUE_CODE, $storeId); + $vaultPaymentCode = $this->config->getValue(VaultProvidersMap::VALUE_CODE, $storeId); return (int) ((int)$this->config->getValue('active', $storeId) === 1 && $vaultPaymentCode - && $vaultPaymentCode !== VaultPayment::EMPTY_VALUE); + && $vaultPaymentCode !== VaultProvidersMap::EMPTY_VALUE); } } diff --git a/app/code/Magento/Vault/Model/Adminhtml/Source/VaultPayment.php b/app/code/Magento/Vault/Model/Adminhtml/Source/VaultPayment.php deleted file mode 100644 index 0c3cc362ad433..0000000000000 --- a/app/code/Magento/Vault/Model/Adminhtml/Source/VaultPayment.php +++ /dev/null @@ -1,49 +0,0 @@ -options = array_merge( - $options, - [ - 'value' => self::EMPTY_VALUE, - 'label' => __('Select a payment solution') - ] - ); - } - - /** - * Return array of options as value-label pairs - * - * @return array Format: array(array('value' => '', 'label' => '
diff --git a/app/code/Magento/Vault/etc/frontend/di.xml b/app/code/Magento/Vault/etc/frontend/di.xml index 4fad674cc8900..b9c48ad37bb4c 100644 --- a/app/code/Magento/Vault/etc/frontend/di.xml +++ b/app/code/Magento/Vault/etc/frontend/di.xml @@ -9,7 +9,8 @@ - Magento\Vault\Model\Ui\ConfigProvider + Magento\Vault\Model\Ui\VaultConfigProvider + Magento\Vault\Model\Ui\TokensConfigProvider diff --git a/app/code/Magento/Vault/etc/module.xml b/app/code/Magento/Vault/etc/module.xml index cfc1e5735fef4..43d1d4dbbcb4f 100644 --- a/app/code/Magento/Vault/etc/module.xml +++ b/app/code/Magento/Vault/etc/module.xml @@ -9,8 +9,10 @@ + + diff --git a/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js b/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js index c9652c06e0834..1b6259822fb33 100644 --- a/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js +++ b/app/code/Magento/Vault/view/frontend/web/js/view/payment/method-renderer/vault.js @@ -15,8 +15,7 @@ define( return Component.extend({ defaults: { - template: 'Magento_Vault/payment/form', - transactionResult: '' + template: 'Magento_Vault/payment/form' }, /** diff --git a/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault-enabler.js b/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault-enabler.js new file mode 100644 index 0000000000000..1b8b68106bf67 --- /dev/null +++ b/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault-enabler.js @@ -0,0 +1,60 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define( + [ + 'uiElement' + ], + function ( + Component + ) { + 'use strict'; + + return Component.extend({ + defaults: { + isActivePaymentTokenEnabler: true + }, + + /** + * @param {String} paymentCode + */ + setPaymentCode: function (paymentCode) { + this.paymentCode = paymentCode; + }, + + /** + * @returns {Object} + */ + initObservable: function () { + this._super() + .observe([ + 'isActivePaymentTokenEnabler' + ]); + + return this; + }, + + /** + * @param {Object} data + */ + visitAdditionalData: function (data) { + if (!this.isVaultEnabled()) { + return; + } + + data['additional_data']['is_active_payment_token_enabler'] = this.isActivePaymentTokenEnabler(); + }, + + /** + * @returns {Boolean} + */ + isVaultEnabled: function () { + return window.checkoutConfig.vault['is_enabled'] === true && + window.checkoutConfig.vault['vault_provider_code'] === this.paymentCode; + } + }); + } +); diff --git a/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault.js b/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault.js index 5c1ab3d52c5fa..5075088e2ebd9 100644 --- a/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault.js +++ b/app/code/Magento/Vault/view/frontend/web/js/view/payment/vault.js @@ -21,8 +21,8 @@ define( rendererList.push( { type: index, - config: config, - component: 'Magento_Vault/js/view/payment/method-renderer/vault' + config: config.config, + component: config.component } ); }); diff --git a/dev/tests/functional/composer.json b/dev/tests/functional/composer.json index 532ccf5e82912..826255be7dfd1 100644 --- a/dev/tests/functional/composer.json +++ b/dev/tests/functional/composer.json @@ -1,6 +1,6 @@ { "require": { - "magento/mtf": "1.0.0-rc37", + "magento/mtf": "1.0.0-rc38", "php": "~5.5.0|~5.6.0|~7.0.0", "phpunit/phpunit": "4.1.0", "phpunit/phpunit-selenium": ">=1.2" diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Conditions.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Conditions.php index ce80fa82d4074..63977fbf16630 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Conditions.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Handler/Conditions.php @@ -52,6 +52,8 @@ abstract class Conditions extends Curl 'is not' => '!=', 'equal to' => '==', 'matches' => '==', + 'greater than' => '>', + 'equals or greater than' => '>=', ], 'value_type' => [ 'same_as' => 'the Same as Matched Product Categories', @@ -60,9 +62,15 @@ abstract class Conditions extends Curl 'California' => '12', 'United States' => 'US', '[flatrate] Fixed' => 'flatrate_flatrate', + 'FOUND' => '1', + 'TRUE' => '1', ], 'aggregator' => [ 'ALL' => 'all', + 'ANY' => 'any', + ], + 'attribute'=> [ + 'total quantity' => 'qty', ], ]; @@ -181,7 +189,7 @@ private function convertSingleCondition($condition) ); } - return $typeParam + $ruleParam; + return $ruleParam + $typeParam; } /** diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Cc.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Cc.php index be7fada0f9b6c..729e796617d79 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Cc.php +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Cc.php @@ -6,11 +6,7 @@ namespace Magento\Braintree\Test\Block\Form; - -use Magento\Mtf\Block\Mapper; use Magento\Mtf\Client\Locator; -use Magento\Mtf\Block\BlockFactory; -use Magento\Mtf\Client\BrowserInterface; use Magento\Mtf\Client\Element\SimpleElement; use Magento\Mtf\Fixture\FixtureInterface; use Magento\Payment\Test\Block\Form\Cc as CreditCard; @@ -37,8 +33,21 @@ public function fill(FixtureInterface $fixture, SimpleElement $element = null) { $mapping = $this->dataMapping($fixture->getData()); foreach ($this->braintreeForm as $field => $iframe) { + $element = $this->browser->find('body'); + $this->browser->waitUntil( + function () use ($element, $iframe) { + $fieldElement = $element->find($iframe); + return $fieldElement->isVisible() ? true : null; + } + ); $this->browser->switchToFrame(new Locator($iframe)); $element = $this->browser->find('body'); + $this->browser->waitUntil( + function () use ($element) { + $fieldElement = $element->find('input'); + return $fieldElement->isVisible() ? true : null; + } + ); $this->_fill([$mapping[$field]], $element); $this->browser->switchToFrame(); } diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.php new file mode 100644 index 0000000000000..d7e947b7a7e9e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.php @@ -0,0 +1,70 @@ +browser->switchToFrame(new Locator($locator, Locator::SELECTOR_XPATH)); + $this->browser->switchToFrame(new Locator($locator, Locator::SELECTOR_XPATH)); + } + + /** + * Click Submit button. + * + * @return void + */ + public function submit() + { + $this->browser->find($this->submitButton)->click(); + } + + /** + * Fill the 3D Secure form and submit it. + * + * @param FixtureInterface $fixture + * @param SimpleElement|null $element + * @return $this|void + */ + public function fill(FixtureInterface $fixture, SimpleElement $element = null) + { + $mapping = $this->dataMapping($fixture->getData()); + $this->switchToFrame($this->braintree3dSecure); + $element = $this->browser->find('body'); + $this->_fill([$mapping['secure3d_password']], $element); + $this->submit(); + $this->browser->switchToFrame(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.xml new file mode 100644 index 0000000000000..c35c811b49880 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/Secure3d.xml @@ -0,0 +1,14 @@ + + + + + + input[name="external.field.password"] + + + \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Info.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Info.php new file mode 100644 index 0000000000000..2039ac0244cc1 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Info.php @@ -0,0 +1,38 @@ +_rootElement->getElements($this->info, Locator::SELECTOR_XPATH); + foreach ($elements as $row) { + $key = rtrim($row->find('./th', Locator::SELECTOR_XPATH)->getText(), ':'); + $value = $row->find('./td', Locator::SELECTOR_XPATH)->getText(); + $result[$key] = $value; + } + return $result; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/Assert3dSecureInfoIsPresent.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/Assert3dSecureInfoIsPresent.php new file mode 100644 index 0000000000000..c34569487855b --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Constraint/Assert3dSecureInfoIsPresent.php @@ -0,0 +1,47 @@ +getBraintreeInfoBlock()->getPaymentInfo(); + foreach ($paymentInformation as $key => $value) { + \PHPUnit_Framework_Assert::assertArrayHasKey( + $key, + $actualPaymentInformation, + '3D Secure information is not present.' + ); + \PHPUnit_Framework_Assert::assertEquals( + $paymentInformation[$key], + $value, + '3D Secure information is not equal to information from data set.' + ); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return '3D Secure information is present and equals to information from data set.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Fixture/Secure3dBraintree.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Fixture/Secure3dBraintree.xml new file mode 100644 index 0000000000000..d0ab27253ecf4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Fixture/Secure3dBraintree.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/CheckoutOnepage.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/CheckoutOnepage.xml index 4b885217858a1..604ffb277408e 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/CheckoutOnepage.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/CheckoutOnepage.xml @@ -7,6 +7,7 @@ --> - + + \ No newline at end of file diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/adminhtml/SalesOrderView.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/adminhtml/SalesOrderView.xml new file mode 100644 index 0000000000000..ec11c0e63bbfb --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Page/adminhtml/SalesOrderView.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/ConfigData.xml index 080906f33d860..bce3cf16c8859 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/ConfigData.xml @@ -32,19 +32,19 @@ Yes PAYMENT_BRAINTREETWO_MERCHANT_ACCOUNT_ID - + payment 1 Authorize authorize - + payment 1 Yes 1 - + payment 1 Yes @@ -111,5 +111,100 @@ 0 + + + payment + 1 + Yes + 1 + + + + + payment + 1 + Yes + 0 + + + + + payment + 1 + Yes + 1 + + + payment + 1 + Yes + 1 + + + payment + 1 + + GB + + + + + + payment + 1 + Yes + 0 + + + payment + 1 + Yes + 0 + + + + + payment + 1 + Yes + 1 + + + payment + 1 + Yes + 300 + + + + + payment + 1 + Yes + 0 + + + payment + 1 + Yes + 0 + + + + + payment + 1 + Braintree + braintreetwo + + + + + payment + 1 + Select vault provider + null + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml index 855fc144880db..fad1bac29cd70 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/CreditCard.xml @@ -13,5 +13,17 @@ 2020 123 + + 4000000000000002 + 01 + 20 + 123 + + + 4000000000000028 + 01 + 2020 + 123 + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/Secure3d.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/Secure3d.xml new file mode 100644 index 0000000000000..31aef3a0cd283 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Repository/Secure3d.xml @@ -0,0 +1,14 @@ + + + + + + 1234 + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOnlineInvoiceEntityTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOnlineInvoiceEntityTest.xml new file mode 100644 index 0000000000000..ac570ec147ee3 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOnlineInvoiceEntityTest.xml @@ -0,0 +1,67 @@ + + + + + + Full capture of order placed within Braintree Credit Card + catalogProductSimple::product_10_dollar, bundleProduct::bundle_fixed_100_dollar_product + default + us_ca_ny_rule + US_address_1_without_email + No + Flat Rate + Fixed + + 139.9 + + braintreetwo + credit_card_braintree + visa_braintree + braintreetwo + Processing + Back, Send Email, Credit Memo, Hold, Ship, Reorder + - + No + comments + test_type:3rd_party_test + + + + + + + Partial capture for order placed within Braintree Credit Card + catalogProductSimple::product_100_dollar + default + us_ca_ny_rule + US_address_1_without_email + No + Flat Rate + Fixed + Yes + + 108.25 + 118.25 + + braintreetwo + credit_card_braintree + visa_braintree + braintreetwo + Processing + Back, Send Email, Credit Memo, Hold, Ship, Reorder + 1 + No + comments + test_type:3rd_party_test + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml index f0a974b5b3983..3180271db86cb 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/CreateOrderBackendTest.xml @@ -29,6 +29,7 @@ + @@ -44,6 +45,9 @@ 145.98 + + 145.98 + braintreetwo credit_card_braintree visa_braintree @@ -54,6 +58,7 @@ + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutTest.xml index 106f5d9c4ffba..40f8c0d5ef5eb 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutTest.xml @@ -7,8 +7,49 @@ --> - - test_type:extended_acceptance_test, test_type:3rd_party_test + + catalogProductSimple::product_10_dollar, configurableProduct::with_one_option, bundleProduct::bundle_fixed_100_dollar_product + default + us_ca_ny_rule + US_address_1_without_email + login + Flat Rate + Fixed + + 145.98 + + braintreetwo + credit_card_braintree + visa_braintree_3dsecure + braintreetwo, braintreetwo_3d_secure_not_triggered_due_threshold + Processing + test_type:3rd_party_test + + + + + + catalogProductSimple::product_10_dollar, configurableProduct::with_one_option, bundleProduct::bundle_fixed_100_dollar_product + default + us_ca_ny_rule + US_address_1 + guest + Flat Rate + Fixed + + 145.98 + + braintreetwo + credit_card_braintree + visa_braintree_3dsecure + braintreetwo, braintreetwo_3d_secure_uk + Processing + test_type:3rd_party_test + + + + + catalogProductSimple::product_10_dollar, configurableProduct::with_one_option, bundleProduct::bundle_fixed_100_dollar_product default us_ca_ny_rule @@ -24,13 +65,13 @@ visa_braintree braintreetwo Processing + test_type:extended_acceptance_test, test_type:3rd_party_test - - test_type:3rd_party_test + catalogProductSimple::product_10_dollar, configurableProduct::with_one_option, bundleProduct::bundle_fixed_100_dollar_product default us_ca_ny_rule @@ -41,15 +82,19 @@ 145.98 + + 145.98 + braintreetwo credit_card_braintree visa_braintree braintreetwo_sale Processing + test_type:3rd_party_test - + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.php new file mode 100644 index 0000000000000..ea169a58e2ee7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.php @@ -0,0 +1,54 @@ +executeScenario(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.xml new file mode 100644 index 0000000000000..106b7deaf3a75 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/OnePageCheckoutWith3dSecureTest.xml @@ -0,0 +1,39 @@ + + + + + + Registered Checkout with Braintree Credit Card from Storefront with success 3D Secure verification + catalogProductSimple::product_10_dollar, configurableProduct::with_one_option, bundleProduct::bundle_fixed_100_dollar_product + default + us_ca_ny_rule + US_address_1_without_email + login + Flat Rate + Fixed + + 145.98 + + braintreetwo + credit_card_braintree + visa_braintree_3dsecure + + 1 + 1 + + secure3d_braintree + braintreetwo, braintreetwo_3d_secure + Processing + test_type:3rd_party_test + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/UseVaultOnCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/UseVaultOnCheckoutTest.xml new file mode 100644 index 0000000000000..2be837de7a835 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestCase/UseVaultOnCheckoutTest.xml @@ -0,0 +1,32 @@ + + + + + + Use saved for Braintree credit card on checkout + catalogProductSimple::product_10_dollar + default + US_address_1_without_email + login + Flat Rate + Fixed + braintreetwo + credit_card_braintree + visa_braintree + Yes + braintreetwo, braintreetwo_use_vault + Processing + test_type:3rd_party_test + Processing + Back, Cancel, Send Email, Hold, Invoice, Ship + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/TestStep/PlaceOrderWith3dSecureStep.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestStep/PlaceOrderWith3dSecureStep.php new file mode 100644 index 0000000000000..b05aa585b5627 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/TestStep/PlaceOrderWith3dSecureStep.php @@ -0,0 +1,92 @@ +checkoutOnepage = $checkoutOnepage; + $this->secure3d = $secure3d; + $this->assertGrandTotalOrderReview = $assertGrandTotalOrderReview; + $this->checkoutOnepageSuccess = $checkoutOnepageSuccess; + $this->prices = $prices; + } + + /** + * Place order after checking order totals and passing 3D Secure on review step. + * + * @return array + */ + public function run() + { + if (isset($this->prices['grandTotal'])) { + $this->assertGrandTotalOrderReview->processAssert($this->checkoutOnepage, $this->prices['grandTotal']); + } + $this->checkoutOnepage->getPaymentBlock()->getSelectedPaymentMethodBlock()->clickPlaceOrder(); + + $this->checkoutOnepage->getBraintree3dSecureBlock()->fill($this->secure3d); + return ['orderId' => $this->checkoutOnepageSuccess->getSuccessBlock()->getGuestOrderId()]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/di.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/di.xml new file mode 100644 index 0000000000000..7e766e837a4ff --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/di.xml @@ -0,0 +1,14 @@ + + + + + + high + + + diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/testcase.xml new file mode 100644 index 0000000000000..31e5d04fe186f --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/etc/testcase.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml index 9b01310fbbc8c..3276e7cb8d4a3 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml @@ -402,7 +402,7 @@ - default + custom_attribute_set_with_colors Simple Product %isolation% sku_simple_product_%isolation% diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php index e101d911c0a84..d34763629a58a 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment.php @@ -20,7 +20,7 @@ class Payment extends Block * * @var string */ - protected $paymentMethodInput = '#%s'; + protected $paymentMethodInput = '[id^="%s"]'; /** * Labels for payment methods. @@ -34,7 +34,7 @@ class Payment extends Block * * @var string */ - protected $paymentMethodLabel = '[for="%s"]'; + protected $paymentMethodLabel = '[for^="%s"]'; /** * Continue checkout button. @@ -71,7 +71,6 @@ class Payment extends Block */ protected $activePaymentMethodSelector = '.payment-method._active'; - /** * Select payment method. * diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php index 59b43989031ff..5be56bef91f97 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Payment/Method.php @@ -7,6 +7,7 @@ namespace Magento\Checkout\Test\Block\Onepage\Payment; use Magento\Mtf\Block\Block; +use Magento\Mtf\Client\Locator; /** * Checkout payment method block. @@ -34,6 +35,13 @@ class Method extends Block */ protected $billingAddressSelector = '.payment-method-billing-address'; + /** + * Save credit card check box. + * + * @var string + */ + protected $vaultCheckbox = '#%s_vault_enabler'; + /** * Place order. * @@ -59,4 +67,17 @@ public function getBillingBlock() ['element' => $element] ); } + + /** + * Save credit card. + * + * @param string $paymentMethod + * @param string $creditCardSave + * @return void + */ + public function saveCreditCard($paymentMethod, $creditCardSave) + { + $saveCard = sprintf($this->vaultCheckbox, $paymentMethod); + $this->_rootElement->find($saveCard, Locator::SELECTOR_CSS, 'checkbox')->setValue($creditCardSave); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractItems.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractItems.php index 88af288c29ce6..8a9b5bdb72ab6 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractItems.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/AbstractItems.php @@ -9,69 +9,68 @@ use Magento\Mtf\Block\Block; /** - * Class AbstractItems - * Base Items block on Credit Memo, Invoice, Shipment view page + * Base Items block on Credit Memo, Invoice, Shipment view page. */ class AbstractItems extends Block { /** - * Locator for row item + * Locator for row item. * * @var string */ - protected $rowItem = 'tbody tr'; + protected $rowItem = 'tbody'; /** - * Locator for "Product" column + * Locator for "Product" column. * * @var string */ protected $product = '.col-product'; /** - * Locator for "Price" column + * Locator for "Price" column. * * @var string */ protected $price = '.col-price .price'; /** - * Locator for "Qty" column + * Locator for "Qty" column. * * @var string */ protected $qty = '.col-qty'; /** - * Locator for "Subtotal" column + * Locator for "Subtotal" column. * * @var string */ protected $subtotal = '.col-subtotal .price'; /** - * Locator for "Tax Amount" column + * Locator for "Tax Amount" column. * * @var string */ protected $taxAmount = '.col-tax .price'; /** - * Locator for "Discount Amount" column + * Locator for "Discount Amount" column. * * @var string */ protected $discountAmount = '.col-discount .price'; /** - * Locator for "Row total" column + * Locator for "Row total" column. * * @var string */ protected $rowTotal = '.col-total .price'; /** - * Get items data + * Get items data. * * @return array */ @@ -98,14 +97,14 @@ public function getData() } /** - * Parse product name to title and sku product + * Parse product name to title and sku product. * * @param string $product * @return array */ protected function parseProductName($product) { - $data = array_map('trim', explode('SKU:', $product)); + $data = array_map('trim', explode('SKU:', str_replace("\n", '', $product))); return [ 'product' => $data[0], 'sku' => isset($data[1]) ? $data[1] : '' @@ -113,7 +112,7 @@ protected function parseProductName($product) } /** - * Prepare price + * Prepare price. * * @var string $price * @return string diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php index 17d1dd67238c0..25ed6613a8e09 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/History.php @@ -10,35 +10,33 @@ use Magento\Mtf\Client\Locator; /** - * Class Totals - * Order totals block - * + * Order comments block. */ class History extends Block { /** - * Comment history Id + * Comment history Id. * * @var string */ protected $commentHistory = '.note-list-comment'; /** - * Captured Amount from IPN + * Captured Amount from IPN. * * @var string */ - protected $capturedAmount = '//div[@class="note-list-comment"][contains(text(), "captured amount of")]'; + protected $capturedAmount = '//div[@class="note-list-comment"][contains(text(), "Captured amount of")]'; /** - * Note list locator + * Note list locator. * * @var string */ protected $noteList = '.note-list'; /** - * Get comments history + * Get comments history. * * @return string */ @@ -49,18 +47,23 @@ public function getCommentsHistory() } /** - * Get the captured amount from the comments history + * Get the captured amount from the comments history. * - * @return string + * @return array */ public function getCapturedAmount() { + $result = []; $this->waitCommentsHistory(); - return $this->_rootElement->find($this->capturedAmount, Locator::SELECTOR_XPATH)->getText(); + $captureComments = $this->_rootElement->getElements($this->capturedAmount, Locator::SELECTOR_XPATH); + foreach ($captureComments as $captureComment) { + $result[] = $captureComment->getText(); + } + return $result; } /** - * Wait for comments history is visible + * Wait for comments history is visible. * * @return void */ diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php index 07dd426adc07f..1b697a839740b 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/View/Tab/Invoices/Grid.php @@ -7,13 +7,12 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Invoices; /** - * Class Grid - * Invoices grid on order view page + * Invoices grid on order view page. */ class Grid extends \Magento\Backend\Test\Block\Widget\Grid { /** - * Locator value for link in action column + * Locator value for link in action column. * * @var string */ @@ -24,10 +23,10 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid * * @var string */ - protected $invoiceId = 'tbody td[data-column="increment_id"]'; + protected $invoiceId = 'tbody td:nth-child(2)'; /** - * Filters array mapping + * Filters array mapping. * * @var array */ @@ -48,7 +47,7 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid ]; /** - * Get invoice ids + * Get invoice ids. * * @return array */ diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCaptureInCommentsHistory.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCaptureInCommentsHistory.php index 3c56ce05d439b..c3b51812e7cce 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCaptureInCommentsHistory.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertCaptureInCommentsHistory.php @@ -26,25 +26,26 @@ class AssertCaptureInCommentsHistory extends AbstractConstraint * @param SalesOrderView $salesOrderView * @param OrderIndex $salesOrder * @param string $orderId - * @param array $prices + * @param array $capturedPrices * @return void */ public function processAssert( SalesOrderView $salesOrderView, OrderIndex $salesOrder, $orderId, - array $prices + array $capturedPrices ) { $salesOrder->open(); $salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $orderId]); - $actualCapturedAmount = $salesOrderView->getOrderHistoryBlock()->getCommentsHistory(); - - \PHPUnit_Framework_Assert::assertContains( - self::CAPTURED_AMOUNT . $prices['grandTotal'], - $actualCapturedAmount, - 'Incorrect captured amount value for the order #' . $orderId - ); + $actualCapturedAmount = $salesOrderView->getOrderHistoryBlock()->getCapturedAmount(); + foreach ($capturedPrices as $key => $capturedPrice) { + \PHPUnit_Framework_Assert::assertContains( + self::CAPTURED_AMOUNT . $capturedPrice, + $actualCapturedAmount[$key], + 'Incorrect captured amount value for the order #' . $orderId + ); + } } /** diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceItems.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceItems.php index dc3c3dea3aa1d..8337a29a3fe99 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceItems.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Constraint/AssertInvoiceItems.php @@ -11,13 +11,12 @@ use Magento\Sales\Test\Page\Adminhtml\SalesInvoiceView; /** - * Class AssertInvoiceItems - * Assert invoice items on invoice view page + * Assert invoice items on invoice view page. */ class AssertInvoiceItems extends AbstractAssertItems { /** - * Assert invoice items on invoice view page + * Assert invoice items on invoice view page. * * @param InvoiceIndex $invoiceIndex * @param SalesInvoiceView $salesInvoiceView @@ -33,7 +32,6 @@ public function processAssert( array $ids, array $data = null ) { - $invoiceIndex->open(); $orderId = $order->getId(); $productsData = $this->prepareOrderProducts($order, $data['items_data']); foreach ($ids['invoiceIds'] as $invoiceId) { @@ -41,6 +39,7 @@ public function processAssert( 'order_id' => $orderId, 'id' => $invoiceId, ]; + $invoiceIndex->open(); $invoiceIndex->getInvoicesGrid()->searchAndOpen($filter); $itemsData = $this->preparePageItems($salesInvoiceView->getItemsBlock()->getData()); $error = $this->verifyData($productsData, $itemsData); @@ -49,7 +48,7 @@ public function processAssert( } /** - * Returns a string representation of the object + * Returns a string representation of the object. * * @return string */ diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOnlineInvoiceEntityTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOnlineInvoiceEntityTest.php new file mode 100644 index 0000000000000..498289bd5f227 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOnlineInvoiceEntityTest.php @@ -0,0 +1,55 @@ + Orders. + * 3. Click Create New Order. + * 4. Select Customer created in preconditions. + * 5. Add Product. + * 6. Fill data according dataset. + * 7. Click Update Product qty. + * 8. Fill data according dataset. + * 9. Click Get Shipping Method and rates. + * 10. Fill data according dataset. + * 11. Submit Order. + * 12. Go to Sales > Orders. + * 13. Select created order in the grid and open it. + * 14. Click 'Invoice' button. + * 15. Fill data according to dataset. + * 16. Click 'Submit Invoice' button. + * 17. Perform assertions. + * + * @group Order_Management_(CS) + * @ZephyrId MAGETWO-47010 + */ +class CreateOnlineInvoiceEntityTest extends Scenario +{ + /* tags */ + const MVP = 'yes'; + const DOMAIN = 'CS'; + const TEST_TYPE = '3rd_party_test'; + /* end tags */ + + /** + * Runs sales order on backend. + * + * @return void + */ + public function test() + { + $this->executeScenario(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateInvoiceStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateInvoiceStep.php index 6f419896aa618..2840263ad9a86 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateInvoiceStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateInvoiceStep.php @@ -68,6 +68,13 @@ class CreateInvoiceStep implements TestStepInterface */ protected $data; + /** + * Whether Invoice is partial. + * + * @var string + */ + protected $isInvoicePartial; + /** * @construct * @param OrderIndex $orderIndex @@ -77,6 +84,7 @@ class CreateInvoiceStep implements TestStepInterface * @param OrderInjectable $order * @param OrderShipmentView $orderShipmentView * @param array|null $data [optional] + * @param string $isInvoicePartial [optional] */ public function __construct( OrderIndex $orderIndex, @@ -85,7 +93,8 @@ public function __construct( OrderInvoiceView $orderInvoiceView, OrderInjectable $order, OrderShipmentView $orderShipmentView, - $data = null + $data = null, + $isInvoicePartial = null ) { $this->orderIndex = $orderIndex; $this->salesOrderView = $salesOrderView; @@ -94,6 +103,7 @@ public function __construct( $this->order = $order; $this->orderShipmentView = $orderShipmentView; $this->data = $data; + $this->isInvoicePartial = $isInvoicePartial; } /** @@ -113,6 +123,10 @@ public function run() ); $this->orderInvoiceNew->getFormBlock()->updateQty(); $this->orderInvoiceNew->getFormBlock()->fillFormData($this->data); + if (isset($this->isInvoicePartial)) { + $this->orderInvoiceNew->getFormBlock()->submit(); + $this->salesOrderView->getPageActions()->invoice(); + } } $this->orderInvoiceNew->getFormBlock()->submit(); $invoiceIds = $this->getInvoiceIds(); @@ -121,8 +135,10 @@ public function run() } return [ - 'invoiceIds' => $invoiceIds, - 'shipmentIds' => isset($shipmentIds) ? $shipmentIds : null, + 'ids' => [ + 'invoiceIds' => $invoiceIds, + 'shipmentIds' => isset($shipmentIds) ? $shipmentIds : null, + ] ]; } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml index 6c5b7cdd1b66a..9d68c4263c6e6 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml @@ -48,4 +48,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php index 9a2b007711a0d..62c9b3207a7b1 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.php @@ -6,6 +6,8 @@ namespace Magento\SalesRule\Test\Block\Adminhtml\Promo\Quote\Edit; use Magento\Backend\Test\Block\Widget\FormTabs; +use Magento\Mtf\Client\Element\SimpleElement; +use Magento\Mtf\Fixture\FixtureInterface; /** * Sales rule edit form. @@ -25,4 +27,45 @@ class PromoQuoteForm extends FormTabs * @var boolean */ protected $waitForSelectorVisible = false; + + /** + * Fill form with tabs. + * + * @param FixtureInterface $fixture + * @param SimpleElement $element + * @param array $replace + * @return $this|FormTabs + */ + public function fill(FixtureInterface $fixture, SimpleElement $element = null, array $replace = null) + { + $tabs = $this->getFieldsByTabs($fixture); + if ($replace) { + $tabs = $this->prepareData($tabs, $replace); + } + $this->fillTabs($tabs, $element); + } + + /** + * Replace placeholders in each values of data. + * + * @param array $tabs + * @param array $replace + * @return array + */ + protected function prepareData(array $tabs, array $replace) + { + foreach ($replace as $tabName => $fields) { + foreach ($fields as $key => $pairs) { + if (isset($tabs[$tabName][$key])) { + $tabs[$tabName][$key]['value'] = str_replace( + array_keys($pairs), + array_values($pairs), + $tabs[$tabName][$key]['value'] + ); + } + } + } + + return $tabs; + } } diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php index a9307ce813213..6249fb5f56efd 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php @@ -88,6 +88,13 @@ abstract class AssertCartPriceRuleApplying extends AbstractConstraint */ protected $productForSalesRule2; + /** + * Cart prices to compare. + * + * @array cartPrice + */ + protected $cartPrice; + /** * Implementation assert. * @@ -120,15 +127,16 @@ abstract protected function assert(); * @param CustomerAccountLogout $customerAccountLogout * @param CatalogCategoryView $catalogCategoryView * @param CatalogProductView $catalogProductView - * @param Customer $customer * @param SalesRule $salesRule * @param SalesRule $salesRuleOrigin * @param array $productQuantity * @param CatalogProductSimple $productForSalesRule1 * @param CatalogProductSimple $productForSalesRule2 + * @param Customer $customer * @param Address $address * @param int|null $isLoggedIn * @param array $shipping + * @param array $cartPrice * @return void * * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -140,15 +148,16 @@ public function processAssert( CustomerAccountLogout $customerAccountLogout, CatalogCategoryView $catalogCategoryView, CatalogProductView $catalogProductView, - Customer $customer, SalesRule $salesRule, SalesRule $salesRuleOrigin, array $productQuantity, CatalogProductSimple $productForSalesRule1, CatalogProductSimple $productForSalesRule2 = null, + Customer $customer = null, Address $address = null, $isLoggedIn = null, - array $shipping = [] + array $shipping = [], + array $cartPrice = [] ) { $this->checkoutCart = $checkoutCart; $this->cmsIndex = $cmsIndex; @@ -156,9 +165,12 @@ public function processAssert( $this->customerAccountLogout = $customerAccountLogout; $this->catalogCategoryView = $catalogCategoryView; $this->catalogProductView = $catalogProductView; - $this->customer = $customer; $this->productForSalesRule1 = $productForSalesRule1; $this->productForSalesRule2 = $productForSalesRule2; + $this->cartPrice = $cartPrice; + if ($customer !== null) { + $this->customer = $customer; + } $isLoggedIn ? $this->login() : $this->customerAccountLogout->open(); $this->checkoutCart->open()->getCartBlock()->clearShoppingCart(); $this->addProductsToCart($productQuantity); diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleConditionIsApplied.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleConditionIsApplied.php index 55a3450a6e108..e4cc6a5f55f2e 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleConditionIsApplied.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleConditionIsApplied.php @@ -7,28 +7,28 @@ namespace Magento\SalesRule\Test\Constraint; /** - * Check that shopping cart subtotal not equals with grand total(excluding shipping price if exist). + * Assert that Catalog Price Rule is applied in Shopping Cart. */ class AssertCartPriceRuleConditionIsApplied extends AssertCartPriceRuleApplying { /** - * Assert that shopping cart subtotal not equals with grand total. + * Assert that Catalog Price Rule is applied in Shopping Cart. * * @return void */ protected function assert() { - $subTotal = $this->checkoutCart->getTotalsBlock()->getSubtotal(); - $grandTotal = $this->checkoutCart->getTotalsBlock()->getGrandTotal(); + $this->checkoutCart->getTotalsBlock()->waitForShippingPriceBlock(); + $this->checkoutCart->getTotalsBlock()->waitForUpdatedTotals(); + $actualPrices['sub_total'] = $this->checkoutCart->getTotalsBlock()->getSubtotal(); + $actualPrices['grand_total'] = $this->checkoutCart->getTotalsBlock()->getGrandTotal(); + $actualPrices['discount'] = $this->checkoutCart->getTotalsBlock()->getDiscount(); + $expectedPrices = $this->cartPrice; - if ($this->checkoutCart->getTotalsBlock()->isVisibleShippingPriceBlock()) { - $shippingPrice = $this->checkoutCart->getTotalsBlock()->getShippingPrice(); - $grandTotal = number_format(($grandTotal - $shippingPrice), 2); - } - \PHPUnit_Framework_Assert::assertNotEquals( - $subTotal, - $grandTotal, - 'Shopping cart subtotal: \'' . $subTotal . '\' equals with grand total: \'' . $grandTotal . '\'' + \PHPUnit_Framework_Assert::assertEquals( + $expectedPrices, + $actualPrices, + 'Wrong total cart prices are displayed.' ); } diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Handler/SalesRule/Curl.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Handler/SalesRule/Curl.php index e6a910a76fa65..65266446a3596 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Handler/SalesRule/Curl.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Handler/SalesRule/Curl.php @@ -41,11 +41,27 @@ class Curl extends Conditions implements SalesRuleInterface 'type' => 'Magento\SalesRule\Model\Rule\Condition\Address', 'attribute' => 'base_subtotal', ], + 'Total Items Quantity' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Address', + 'attribute' => 'total_qty', + ], 'Conditions combination' => [ 'type' => 'Magento\SalesRule\Model\Rule\Condition\Combine', 'aggregator' => 'all', 'value' => '1', ], + 'Products subselection' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product\Subselect', + 'attribute' => 'qty', + 'operator' => '==', + 'value' => '1', + 'aggregator' => 'all', + ], + 'Product attribute combination' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product\Found', + 'value' => '1', + 'aggregator' => 'all', + ], 'Shipping Country' => [ 'type' => 'Magento\SalesRule\Model\Rule\Condition\Address', 'attribute' => 'country_id', @@ -57,6 +73,18 @@ class Curl extends Conditions implements SalesRuleInterface 'Category' => [ 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product', 'attribute' => 'category_ids', + ], + 'Price in cart' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product', + 'attribute' => 'quote_item_price', + ], + 'Quantity in cart' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product', + 'attribute' => 'quote_item_qty', + ], + 'Row total in cart' => [ + 'type' => 'Magento\SalesRule\Model\Rule\Condition\Product', + 'attribute' => 'quote_item_row_total', ] ]; diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml index 5f0d0df94604f..6250b30614abc 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml @@ -189,5 +189,95 @@ Percent of product price discount 50 + + + Cart Price Rule with product subselection %isolation% + Cart Price Rule with product subselection + Active + + Main Website + + + NOT LOGGED IN + + No Coupon + 0 + Yes + {Products subselection|total quantity|is|2|:[[Price in cart|is|50]]} + [Price in cart|is|50] + Percent of product price discount + 10 + 1 + 0 + No + No + No + + Cart Price Rule with subselection + Cart Price Rule with subselection + + + + + Cart price rule with attribute conditions %isolation% + Active + + Main Website + + + NOT LOGGED IN + + No Coupon + 0 + Yes + {Product attribute combination|FOUND|ANY|:[[Price in cart|greater than|99][Quantity in cart|greater than|3]]} + {Conditions combination|ANY|TRUE|:[[Price in cart|greater than|99][Quantity in cart|greater than|3]]} + Fixed amount discount + 6 + No + No + No + + + + Cart price rule with row total condition %isolation% + Active + + Main Website + + + NOT LOGGED IN + + No Coupon + 0 + Yes + {Product attribute combination:[[Row total in cart|greater than|50]]} + [Row total in cart|greater than|50] + Fixed amount discount + 6 + No + No + No + + + + Cart price rule with total items %isolation% + Active + + Main Website + + + NOT LOGGED IN + + No Coupon + 1 + Yes + [Total Items Quantity|equals or greater than|3]{Product attribute combination|FOUND|ALL|:[[Category|is|2]]} + Percent of product price discount + 25 + No + No + No + diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.php new file mode 100644 index 0000000000000..ae907548b3ef0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.php @@ -0,0 +1,87 @@ +fixtureFactory = $fixtureFactory; + } + + /** + * Apply several sales rules. + * + * @param array $salesRules + * @param CatalogProductSimple $productForSalesRule1 + * @param CatalogProductSimple $productForSalesRule2 + */ + public function testApplySeveralSalesRules( + array $salesRules, + CatalogProductSimple $productForSalesRule1, + CatalogProductSimple $productForSalesRule2 + ) { + // Preconditions + $productForSalesRule1->persist(); + $productForSalesRule2->persist(); + + // Create sales rules + foreach ($salesRules as $key => $dataSet) { + $salesRule[$key] = $this->fixtureFactory->createByCode( + 'salesRule', + ['dataset' => $dataSet] + ); + $salesRule[$key]->persist(); + } + } + + /** + * Clear data after test. + * + * @return void + */ + public function tearDown() + { + $this->objectManager->create('\Magento\SalesRule\Test\TestStep\DeleteAllSalesRuleStep')->run(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml new file mode 100644 index 0000000000000..1d419bbe3389f --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/ApplySeveralSalesRuleEntityTest.xml @@ -0,0 +1,54 @@ + + + + + + active_sales_rule_product_subselection + active_sales_rule_product_attribute + 200.00 + 204.00 + 11.00 + simple_for_salesrule_1 + simple_for_salesrule_2 + 1 + 2 + + + + active_sales_rule_for_all_groups_no_coupon + active_sales_rule_row_total + 100.00 + 55.00 + 50.00 + simple_for_salesrule_1 + simple_for_salesrule_2 + 1 + + + + active_sales_rule_product_attribute + active_sales_total_items + 250.00 + 193.50 + 71.50 + simple_for_salesrule_1 + simple_for_salesrule_2 + 2 + 1 + + + + active_sales_rule_row_total + active_sales_total_items + simple_for_salesrule_1 + simple_for_salesrule_2 + 1 + + + + diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php index cdb01841dd5f0..562e15ac5b001 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php @@ -12,6 +12,8 @@ use Magento\SalesRule\Test\Page\Adminhtml\PromoQuoteNew; use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\TestCase\Injectable; +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Customer\Test\Fixture\Customer; /** * Precondition: @@ -64,69 +66,102 @@ class CreateSalesRuleEntityTest extends Injectable */ protected $salesRuleName; + /** + * Fixture factory. + * + * @var FixtureFactory + */ + protected $fixtureFactory; + /** * Inject pages. * * @param PromoQuoteNew $promoQuoteNew * @param PromoQuoteIndex $promoQuoteIndex * @param PromoQuoteEdit $promoQuoteEdit + * @param FixtureFactory $fixtureFactory * @return void */ public function __inject( PromoQuoteNew $promoQuoteNew, PromoQuoteIndex $promoQuoteIndex, - PromoQuoteEdit $promoQuoteEdit + PromoQuoteEdit $promoQuoteEdit, + FixtureFactory $fixtureFactory ) { $this->promoQuoteNew = $promoQuoteNew; $this->promoQuoteIndex = $promoQuoteIndex; $this->promoQuoteEdit = $promoQuoteEdit; + $this->fixtureFactory = $fixtureFactory; } /** - * Create customer and 2 simple products with categories before run test. + * Create Sales Price Rule. * - * @param FixtureFactory $fixtureFactory - * @return array + * @param SalesRule $salesRule + * @param CatalogProductSimple $productForSalesRule1 + * @param CatalogProductSimple $productForSalesRule2 + * @param Customer $customer + * @param string $conditionEntity */ - public function __prepare(FixtureFactory $fixtureFactory) - { - $customer = $fixtureFactory->createByCode('customer', ['dataset' => 'default']); - $customer->persist(); + public function testCreateSalesRule( + SalesRule $salesRule, + CatalogProductSimple $productForSalesRule1, + CatalogProductSimple $productForSalesRule2 = null, + Customer $customer = null, + $conditionEntity = null + ) { + $replace = null; + $this->salesRuleName = $salesRule->getName(); - $productForSalesRule1 = $fixtureFactory->createByCode( - 'catalogProductSimple', - ['dataset' => 'simple_for_salesrule_1'] - ); + // Prepare data + if ($customer !== null) { + $customer->persist(); + } $productForSalesRule1->persist(); + if ($productForSalesRule2 !== null) { + $productForSalesRule2->persist(); + if ($conditionEntity !== null) { + $replace = $this->prepareCondition($productForSalesRule2, $conditionEntity); + } + } - $productForSalesRule2 = $fixtureFactory->createByCode( - 'catalogProductSimple', - ['dataset' => 'simple_for_salesrule_2'] - ); - $productForSalesRule2->persist(); + // Steps + $this->promoQuoteNew->open(); + $this->promoQuoteNew->getSalesRuleForm()->fill($salesRule, null, $replace); + $this->promoQuoteNew->getFormPageActions()->save(); - return [ - 'customer' => $customer, - 'productForSalesRule1' => $productForSalesRule1, - 'productForSalesRule2' => $productForSalesRule2 - ]; } /** - * Create Sales Rule Entity. + * Prepare condition for Sales rule. * - * @param SalesRule $salesRule - * @return void + * @param CatalogProductSimple $productSimple + * @param string $conditionEntity + * @return array */ - public function testCreateSalesRule(SalesRule $salesRule) + protected function prepareCondition(CatalogProductSimple $productSimple, $conditionEntity) { - // Preconditions - $this->salesRuleName = $salesRule->getName(); + $result = []; + + switch ($conditionEntity) { + case 'category': + $result['%category_id%'] = $productSimple->getDataFieldConfig('category_ids')['source']->getIds()[0]; + break; + case 'attribute': + /** @var \Magento\Catalog\Test\Fixture\CatalogProductAttribute[] $attrs */ + $attributes = $productSimple->getDataFieldConfig('attribute_set_id')['source'] + ->getAttributeSet()->getDataFieldConfig('assigned_attributes')['source']->getAttributes(); + + $result['%attribute_name%'] = $attributes[0]->getFrontendLabel(); + $result['%attribute_value%'] = $attributes[0]->getOptions()[0]['view']; + break; + } - // Steps - $this->promoQuoteNew->open(); - $this->promoQuoteNew->getSalesRuleForm()->fill($salesRule); - $this->promoQuoteNew->getFormPageActions()->save(); + return [ + 'conditions' => [ + 'conditions_serialized' => $result, + ], + ]; } /** diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index 0720e78078111..8b139628a11b7 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -19,6 +19,10 @@ No No Sales Cart Rule labels + 100.00 + 55.00 + 50.00 + simple_for_salesrule_1 1 @@ -37,7 +41,11 @@ No No Coupon code+fixed amount discount + simple_for_salesrule_1 2 + 200.00 + 140.00 + 70.00 @@ -55,7 +63,11 @@ No No Coupon code+Fixed amount discount for whole cart + simple_for_salesrule_1 3 + 300.00 + 255.00 + 60.00 @@ -74,7 +86,11 @@ No No Buy X get Y free + simple_for_salesrule_1 4 + 400.00 + 320.00 + 100.00 @@ -82,6 +98,7 @@ test_type:extended_acceptance_test 1 + default Cart Price Rule5 %isolation% Cart Price Rule Description %isolation% Active @@ -93,7 +110,11 @@ 2 No No + simple_for_salesrule_1 3 + 300.00 + 215.00 + 100.00 @@ -115,8 +136,13 @@ Yes No Apply discount to Shipping Amount + simple_for_salesrule_1 + simple_for_salesrule_2 1 1 + 150.00 + 80.00 + 80.00 @@ -138,14 +164,24 @@ No For matching items only Free Shipping in conditions + simple_for_salesrule_1 + simple_for_salesrule_2 1 1 + 150.00 + 75.00 + 75.00 + United States + California + 95814 + Flat Rate + Fixed Cart Price Rule8 %isolation% Cart Price Rule Description %isolation% Active @@ -158,8 +194,13 @@ No No Sales Cart Rule labels + simple_for_salesrule_1 + simple_for_salesrule_2 1 1 + 150.00 + 85.00 + 75.00 @@ -171,12 +212,13 @@ Main Website NOT LOGGED IN No Coupon - [Subtotal|is|150] + [Total Items Quantity|is|3] Percent of product price discount 50 No No - Subtotal Action-Condition test + Total Items Quantity-Not Applied test + simple_for_salesrule_1 2 @@ -189,13 +231,20 @@ Main Website NOT LOGGED IN No Coupon - [Total Items Quantity|is|2] + category + {Product attribute combination:[Category|is|%category_id%]} Percent of product price discount 50 No No - Total Items Quantity is 2 + Product attribute combination - Category + simple_for_salesrule_1 + simple_for_salesrule_2 2 + 1 + 250.00 + 140.00 + 125.00 @@ -207,14 +256,19 @@ Main Website NOT LOGGED IN No Coupon - [Total Weight|is|150] + [Total Weight|is|200] Percent of product price discount 50 No No - Total Weight is 150 + Total Weight is 200 + simple_for_salesrule_1 + simple_for_salesrule_2 1 - 1 + 2 + 200.00 + 115.00 + 100.00 @@ -237,8 +291,13 @@ No No Rule applied conditions combination + simple_for_salesrule_1 + simple_for_salesrule_2 1 1 + 150.00 + 85.00 + 75.00 @@ -257,7 +316,35 @@ No No Product attribute discount + simple_for_salesrule_1 1 + 100.00 + 55.00 + 50.00 + + + + + + Cart Price Rule14 %isolation% + Cart Price Rule Description %isolation% + Active + Main Website + NOT LOGGED IN + No Coupon + attribute + {Product attribute combination:[%attribute_name%|is|%attribute_value%]} + Percent of product price discount + 50 + No + No + Product attribute combination - attribute + simple_for_salesrule_1 + simple_for_salesrule_2 + 1 + 50.00 + 30.00 + 25.00 diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/UpdateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/UpdateSalesRuleEntityTest.xml index 51362965e0f22..14d787e33ddeb 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/UpdateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/UpdateSalesRuleEntityTest.xml @@ -20,6 +20,9 @@ 95814 Flat Rate Fixed + 400.00 + 300.00 + 100.00 diff --git a/dev/tests/functional/tests/app/Magento/Vault/Test/TestCase/UseVaultOnCheckoutTest.php b/dev/tests/functional/tests/app/Magento/Vault/Test/TestCase/UseVaultOnCheckoutTest.php new file mode 100644 index 0000000000000..a05decda594f2 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Vault/Test/TestCase/UseVaultOnCheckoutTest.php @@ -0,0 +1,54 @@ +executeScenario(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/SaveCreditCardStep.php b/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/SaveCreditCardStep.php new file mode 100644 index 0000000000000..47bafd49f7b03 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/SaveCreditCardStep.php @@ -0,0 +1,66 @@ +checkoutOnepage = $checkoutOnepage; + $this->payment = $payment; + $this->creditCardSave = $creditCardSave; + } + + /** + * Run step that saves credit card. + * + * @return void + */ + public function run() + { + $this->checkoutOnepage->getPaymentBlock()->getSelectedPaymentMethodBlock()->saveCreditCard( + $this->payment['method'], + $this->creditCardSave + ); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/UseSavedCreditCardStep.php b/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/UseSavedCreditCardStep.php new file mode 100644 index 0000000000000..02c7489d2c279 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Vault/Test/TestStep/UseSavedCreditCardStep.php @@ -0,0 +1,52 @@ +checkoutOnepage = $checkoutOnepage; + $this->payment = $payment; + } + + /** + * Run step that selects saved credit card. + * + * @return void + */ + public function run() + { + $this->payment['method'] .= '_vault_item_'; + $this->checkoutOnepage->getPaymentBlock()->selectPaymentMethod($this->payment); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Vault/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Vault/Test/etc/testcase.xml new file mode 100644 index 0000000000000..3c106e36f1172 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Vault/Test/etc/testcase.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/3rd_party.xml b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/3rd_party.xml new file mode 100644 index 0000000000000..80574d554209c --- /dev/null +++ b/dev/tests/functional/testsuites/Magento/Mtf/TestSuite/InjectableTests/3rd_party.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/dev/tests/integration/etc/install-config-mysql.travis.php.dist b/dev/tests/integration/etc/install-config-mysql.travis.php.dist index 1359c82a334bd..22004fc345e64 100644 --- a/dev/tests/integration/etc/install-config-mysql.travis.php.dist +++ b/dev/tests/integration/etc/install-config-mysql.travis.php.dist @@ -6,7 +6,7 @@ return [ 'db-host' => '127.0.0.1', - 'db-user' => 'travis', + 'db-user' => 'root', 'db-password' => '', 'db-name' => 'magento_integration_tests', 'db-prefix' => 'trv_', diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php index 68c6cefb29a53..3ffc97f1129c3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php @@ -8,7 +8,9 @@ require dirname(dirname(__DIR__)) . '/Store/_files/second_store.php'; require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute.php'; -$productModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +$productModel = $objectManager->create('Magento\Catalog\Model\Product'); $customOptions = [ 1 => [ @@ -57,4 +59,17 @@ [$product->getId() => ['position' => 1]] ); -$product->setOptions($customOptions)->save(); +$options = []; + +/** @var \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory $customOptionFactory */ +$customOptionFactory = $objectManager->create('Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory'); + +foreach ($customOptions as $option) { + /** @var \Magento\Catalog\Api\Data\ProductCustomOptionInterface $option */ + $option = $customOptionFactory->create(['data' => $option]); + $option->setProductSku($productModel->getSku()); + + $options[] = $option; +} + +$productModel->setOptions($options)->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php index 6701e35791b4e..b0d9b80409f7c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php @@ -19,6 +19,10 @@ ->setDiscountAmount(10) ->setWebsiteIds([0 => 1]) ->setSimpleAction('by_percent') + ->setStopRulesProcessing(false) + ->setSortOrder(0) + ->setSubIsEnable(0) + ->setSubDiscountAmount(0) ->save(); /** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute.php index faa1927901fc5..0324199f93702 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_attribute.php @@ -11,12 +11,16 @@ $rule->loadPost([ 'name' => 'test_rule', 'is_active' => '1', + 'stop_rules_processing' => 0, 'website_ids' => [1], 'customer_group_ids' => [0, 1], 'discount_amount' => 2, 'simple_action' => 'by_percent', 'from_date' => '', 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, 'conditions' => [ '1' => [ 'type' => 'Magento\CatalogRule\Model\Rule\Condition\Combine', diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules.php index 2f6e0cd466b2c..8a35005923700 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/two_rules.php @@ -18,6 +18,9 @@ 'simple_action' => 'by_percent', 'from_date' => '', 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, 'conditions' => [ '1' => [ 'type' => 'Magento\CatalogRule\Model\Rule\Condition\Combine', @@ -42,6 +45,9 @@ 'simple_action' => 'by_fixed', 'from_date' => '', 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, 'conditions' => [ '1' => [ 'type' => 'Magento\CatalogRule\Model\Rule\Condition\Combine', 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 index 274825a41ca5a..936ee8e20590e 100644 --- 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 @@ -116,10 +116,7 @@ public function testXmlConvertedConfigurationAndCompereStructure() */ protected function getActualContent() { - $files = $this->fileUtility->getFiles( - [$this->fileUtility->getPathToSource() . static::ACTUAL], - 'config.xml' - ); + $files = $this->fileUtility->getFiles([BP . static::ACTUAL], 'config.xml'); return file_get_contents(reset($files)); } @@ -129,10 +126,7 @@ protected function getActualContent() */ protected function getExpectedContent() { - $files = $this->fileUtility->getFiles( - [$this->fileUtility->getPathToSource() . static::EXPECTED], - 'config.xml' - ); + $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 index 814a75cd06a4d..550aebc1c33d7 100644 --- 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 @@ -599,16 +599,30 @@ - Get Credentials from PayPal - - - + Get Credentials from PayPal + + + + Sandbox Credentials - + + + + NB9WWHYEMVUMS + + Magento_Backend/web/images/logo-magento.png + + FALSE + + FALSE + + embedded + + pp_express + Magento\Paypal\Block\Adminhtml\System\Config\ApiWizard - diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index e6ba349fee089..1b67d126ac6e9 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -17,7 +17,6 @@ class QuoteManagementTest extends \PHPUnit_Framework_TestCase */ public function testSubmit() { - $this->markTestSkipped('Skipped because of MAGETWO-47215'); /** * Preconditions: * Load quote with Bundle product that has at least to child products diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_bundle.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_bundle.php index 79d351914277d..833f08ee88214 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_bundle.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quote_with_bundle.php @@ -33,7 +33,10 @@ ->setCategoryIds([2]) ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) ->save(); - +$productRepository = $objectManager->get(Magento\Catalog\Api\ProductRepositoryInterface::class); +/** + * @var \Magento\Catalog\Model\Product $product + */ $product = $objectManager->create('Magento\Catalog\Model\Product'); $product ->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) @@ -96,24 +99,66 @@ ] ], ] - ) + )->setCustomAttributes([ + "price_type" => [ + 'attribute_code' => 'price_type', + 'value' => \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC + ], + "price_view" => [ + "attribute_code" => "price_view", + "value" => "1", + ], + ]) ->setCanSaveBundleSelections(true) - ->setAffectBundleProductSelections(true) - ->save(); + ->setHasOptions(false) + ->setAffectBundleProductSelections(true); +if ($product->getBundleOptionsData()) { + $options = []; + foreach ($product->getBundleOptionsData() as $key => $optionData) { + if (!(bool)$optionData['delete']) { + $option = $objectManager->create('Magento\Bundle\Api\Data\OptionInterfaceFactory') + ->create(['data' => $optionData]); + $option->setSku($product->getSku()); + $option->setOptionId(null); -//Load options -$typeInstance = $product->getTypeInstance(); -$typeInstance->setStoreFilter($product->getStoreId(), $product); -$optionCollection = $typeInstance->getOptionsCollection($product); -$selectionCollection = $typeInstance->getSelectionsCollection($typeInstance->getOptionsIds($product), $product); + $links = []; + $bundleLinks = $product->getBundleSelectionsData(); + if (!empty($bundleLinks[$key])) { + foreach ($bundleLinks[$key] as $linkData) { + if (!(bool)$linkData['delete']) { + /** @var \Magento\Bundle\Api\Data\LinkInterface$link */ + $link = $objectManager->create('Magento\Bundle\Api\Data\LinkInterfaceFactory') + ->create(['data' => $linkData]); + $linkProduct = $productRepository->getById($linkData['product_id']); + $link->setSku($linkProduct->getSku()); + $link->setQty($linkData['selection_qty']); + if (isset($linkData['selection_can_change_qty'])) { + $link->setCanChangeQuantity($linkData['selection_can_change_qty']); + } + $links[] = $link; + } + } + $option->setProductLinks($links); + $options[] = $option; + } + } + } + $extension = $product->getExtensionAttributes(); + $extension->setBundleProductOptions($options); + $product->setExtensionAttributes($extension); +} +$productRepository->save($product); +$product = $productRepository->get($product->getSku()); $bundleOptions = []; $bundleOptionsQty = []; /** @var $option \Magento\Bundle\Model\Option */ -foreach ($optionCollection as $option) { - /** @var $selection \Magento\Bundle\Model\Selection */ - foreach ($selectionCollection as $selection) { - $bundleOptions[$option->getId()][] = $selection->getSelectionId(); +foreach ($product->getExtensionAttributes()->getBundleProductOptions() as $option) { + foreach ($option->getProductLinks() as $selection) { + /** + * @var \Magento\Bundle\Api\Data\LinkInterface $selection + */ + $bundleOptions[$option->getId()][] = $selection->getId(); $bundleOptionsQty[$option->getId()][] = 1; } } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nCollectPhrasesCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nCollectPhrasesCommandTest.php index 2d5e9026ceefe..cb8ee43f86cf3 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nCollectPhrasesCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nCollectPhrasesCommandTest.php @@ -75,4 +75,22 @@ public function testExecuteNonExistingPath() ] ); } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Directory path is not needed when --magento flag is set. + */ + public function testExecuteMagentoFlagDirectoryPath() + { + $this->tester->execute(['directory' => 'a', '--magento' => true]); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Directory path is needed when --magento flag is not set. + */ + public function testExecuteNoMagentoFlagNoDirectoryPath() + { + $this->tester->execute([]); + } } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nPackCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nPackCommandTest.php index 6833e447bc13e..40f70f65c5192 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nPackCommandTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/I18nPackCommandTest.php @@ -30,9 +30,17 @@ public function setUp() public function tearDown() { - if (file_exists(__DIR__ . '/_files/output/pack')) { + $this->removeCsv('A'); + $this->removeCsv('B'); + $this->removeCsv('C'); + $this->removeCsv('D'); + } + + private function removeCsv($module) + { + if (file_exists(__DIR__ . "/_files/root/app/code/Magento/{$module}/i18n")) { $helper = new \Magento\Framework\Backup\Filesystem\Helper(); - $helper->rm(__DIR__ . '/_files/output/pack', [], true); + $helper->rm(__DIR__ . "/_files/root/app/code/Magento/{$module}/i18n", [], true); } } @@ -41,15 +49,13 @@ public function testExecute() $this->tester->execute( [ 'source' => BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/i18n.csv', - 'pack' => BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/output/pack', 'locale' => 'de_DE', '--allow-duplicates' => true, ] ); $this->assertEquals('Successfully saved de_DE language package.' . PHP_EOL, $this->tester->getDisplay()); - $basePath = BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/output/pack/' - . 'dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code'; + $basePath = BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/app/code'; $this->assertFileExists($basePath . '/Magento/A/i18n/de_DE.csv'); $this->assertFileExists($basePath . '/Magento/B/i18n/de_DE.csv'); $this->assertFileExists($basePath . '/Magento/C/i18n/de_DE.csv'); @@ -67,7 +73,6 @@ public function testExecuteNonExistingPath() $this->tester->execute( [ 'source' => $nonExistPath, - 'pack' => BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/output/pack', 'locale' => 'de_DE', '--allow-duplicates' => true, ] @@ -83,7 +88,6 @@ public function testExecuteInvalidMode() $this->tester->execute( [ 'source' => BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/i18n.csv', - 'pack' => BP . '/dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/output/pack', 'locale' => 'de_DE', '--allow-duplicates' => true, '--mode' => 'invalid' diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Dictionary/GeneratorTest.php b/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Dictionary/GeneratorTest.php index 5b467bf37a88a..2df0426e34bca 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Dictionary/GeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Dictionary/GeneratorTest.php @@ -104,6 +104,12 @@ public function testGenerationWithContext() { $this->generator->generate($this->source, $this->outputFileName, true); - $this->assertFileEquals($this->expectedDir . '/with_context.csv', $this->outputFileName); + $expected = explode(PHP_EOL, file_get_contents($this->expectedDir . '/with_context.csv')); + $output = file_get_contents($this->outputFileName); + foreach ($expected as $line) { + if ($line) { + $this->assertContains($line, $output); + } + } } } diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Pack/GeneratorTest.php b/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Pack/GeneratorTest.php index b9905d27bf4cb..218dd6890ce46 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Pack/GeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Module/I18n/Pack/GeneratorTest.php @@ -61,7 +61,6 @@ protected function setUp() "/app/code/Magento/FirstModule/i18n/{$this->_locale}.csv", "/app/code/Magento/SecondModule/i18n/{$this->_locale}.csv", "/app/design/adminhtml/default/i18n/{$this->_locale}.csv", - "/lib/web/i18n/{$this->_locale}.csv", ]; $this->_generator = ServiceLocator::getPackGenerator(); @@ -92,19 +91,20 @@ public function testGeneration() ComponentRegistrar::register( ComponentRegistrar::MODULE, 'Magento_FirstModule', - BP . '/app/code/Magento/FirstModule' + $this->_packPath . '/app/code/Magento/FirstModule' ); ComponentRegistrar::register( ComponentRegistrar::MODULE, 'Magento_SecondModule', - BP. '/app/code/Magento/SecondModule' + $this->_packPath. '/app/code/Magento/SecondModule' ); ComponentRegistrar::register( ComponentRegistrar::THEME, 'adminhtml/default', - BP. '/app/design/adminhtml/default' + $this->_packPath. '/app/design/adminhtml/default' ); - $this->_generator->generate($this->_dictionaryPath, $this->_packPath, $this->_locale); + + $this->_generator->generate($this->_dictionaryPath, $this->_locale); foreach ($this->_expectedFiles as $file) { $this->assertFileEquals($this->_expectedDir . $file, $this->_packPath . $file); diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/BlockInstantiationTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/BlockInstantiationTest.php index 47e281a20d423..a896a632e8ba6 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/BlockInstantiationTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/Modular/BlockInstantiationTest.php @@ -76,6 +76,7 @@ public function allBlocksDataProvider() } $templateBlocks = $this->_addBlock($module, $blockClass, $class, $templateBlocks); } + asort($templateBlocks); return $templateBlocks; } catch (\Exception $e) { trigger_error( diff --git a/dev/tests/integration/testsuite/Magento/Vault/Model/VaultConfigMock.php b/dev/tests/integration/testsuite/Magento/Vault/Model/VaultConfigMock.php deleted file mode 100644 index a5c0080127473..0000000000000 --- a/dev/tests/integration/testsuite/Magento/Vault/Model/VaultConfigMock.php +++ /dev/null @@ -1,46 +0,0 @@ -setId(null)->setAddressType('shipping'); +/** @var \Magento\Sales\Model\Order\Payment $payment */ $payment = $objectManager->create('Magento\Sales\Model\Order\Payment'); $payment->setMethod('checkmo'); @@ -65,6 +66,21 @@ $tokenString = 'asdfg'; $payment->setTransactionId('trx1'); $payment->addTransaction(\Magento\Sales\Model\Order\Payment\Transaction::TYPE_AUTH); -$payment->setTransactionAdditionalInfo('token', $tokenString); +//$payment->setTransactionAdditionalInfo('token', $tokenString); + + +/** @var \Magento\Vault\Model\PaymentToken $paymentToken */ +$paymentToken = $objectManager->create('Magento\Vault\Model\PaymentToken'); +$paymentToken->setCustomerId($customerIdFromFixture); +$paymentToken->setGatewayToken($tokenString); +$paymentToken->setPaymentMethodCode($payment->getMethod()); +$paymentToken->setCreatedAt('2015-12-08 18:06:10'); + +/** @var \Magento\Sales\Api\Data\OrderPaymentExtensionFactory $orderPaymentExtensionFactory */ +$orderPaymentExtensionFactory = $objectManager->create('Magento\Sales\Api\Data\OrderPaymentExtensionFactory'); +$orderPaymentExtension = $orderPaymentExtensionFactory->create(); +$orderPaymentExtension->setVaultPaymentToken($paymentToken); +$payment->setExtensionAttributes($orderPaymentExtension); + $order->save(); diff --git a/dev/tests/integration/testsuite/Magento/Vault/etc/di.php b/dev/tests/integration/testsuite/Magento/Vault/etc/di.php index 1b537934038a7..b1015029a8854 100644 --- a/dev/tests/integration/testsuite/Magento/Vault/etc/di.php +++ b/dev/tests/integration/testsuite/Magento/Vault/etc/di.php @@ -5,5 +5,7 @@ */ return [ - 'VaultPaymentConfig' => ['type' => 'Magento\Vault\Model\VaultConfigMock'], + 'preferences' => [ + 'Magento\Vault\Model\VaultPaymentInterface' => 'Magento\Vault\Model\VaultPaymentMock', + ], ]; diff --git a/dev/tests/static/framework/Magento/TestFramework/Inspection/WordsFinder.php b/dev/tests/static/framework/Magento/TestFramework/Inspection/WordsFinder.php index 9ee716602a8f6..0cf519d29a910 100644 --- a/dev/tests/static/framework/Magento/TestFramework/Inspection/WordsFinder.php +++ b/dev/tests/static/framework/Magento/TestFramework/Inspection/WordsFinder.php @@ -79,18 +79,27 @@ class WordsFinder */ protected $_baseDir; + /** + * Component Registrar + * + * @var \Magento\Framework\Component\ComponentRegistrar + */ + protected $componentRegistrar; + /** * @param string|array $configFiles * @param string $baseDir + * @param \Magento\Framework\Component\ComponentRegistrar $componentRegistrar * @param bool $isCopyrightChecked * @throws \Magento\TestFramework\Inspection\Exception */ - public function __construct($configFiles, $baseDir, $isCopyrightChecked = false) + public function __construct($configFiles, $baseDir, $componentRegistrar, $isCopyrightChecked = false) { if (!is_dir($baseDir)) { throw new \Magento\TestFramework\Inspection\Exception("Base directory {$baseDir} does not exist"); } $this->_baseDir = str_replace('\\', '/', realpath($baseDir)); + $this->componentRegistrar = $componentRegistrar; // Load config files if (!is_array($configFiles)) { @@ -176,7 +185,15 @@ protected function _extractWhitelist(\SimpleXMLElement $configXml) 'A "path" must be defined for the whitelisted item' ); } - $path = $this->_baseDir . '/' . (string)$path[0]; + $component = $node->xpath('component'); + if ($component) { + $componentType = $component[0]->xpath('@type')[0]; + $componentName = $component[0]->xpath('@name')[0]; + $path = $this->componentRegistrar->getPath((string)$componentType, (string)$componentName) + . '/' . (string)$path[0]; + } else { + $path = $this->_baseDir . '/' . (string)$path[0]; + } // Words $words = []; @@ -241,13 +258,12 @@ public function findWords($file) protected function _findWords($file) { $checkContents = !$this->_isBinaryFile($file); - - $relPath = $this->_getRelPath($file); + $path = $this->getSearchablePath($file); $contents = $checkContents ? file_get_contents($file) : ''; $foundWords = []; foreach ($this->_words as $word) { - if (stripos($relPath, $word) !== false || stripos($contents, $word) !== false) { + if (stripos($path, $word) !== false || stripos($contents, $word) !== false) { $foundWords[] = $word; } } @@ -314,13 +330,16 @@ protected function _removeWhitelistedWords($path, $foundWords) } /** - * Return file path relative to base dir + * Return the path for words search * * @param string $file * @return string */ - protected function _getRelPath($file) + protected function getSearchablePath($file) { + if (strpos($file, $this->_baseDir) === false) { + return $file; + } return substr($file, strlen($this->_baseDir) + 1); } } diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Inspection/WordsFinderTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Inspection/WordsFinderTest.php index 7ac952b65276a..cf6ede2e04635 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Inspection/WordsFinderTest.php +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Inspection/WordsFinderTest.php @@ -5,6 +5,8 @@ */ namespace Magento\TestFramework\Inspection; +use Magento\Framework\Component\ComponentRegistrar; + class WordsFinderTest extends \PHPUnit_Framework_TestCase { /** @@ -15,7 +17,7 @@ class WordsFinderTest extends \PHPUnit_Framework_TestCase */ public function testConstructorException($configFile, $baseDir) { - new \Magento\TestFramework\Inspection\WordsFinder($configFile, $baseDir); + new \Magento\TestFramework\Inspection\WordsFinder($configFile, $baseDir, new ComponentRegistrar()); } public function constructorExceptionDataProvider() @@ -40,7 +42,8 @@ public function testFindWords($configFiles, $file, $expected) { $wordsFinder = new \Magento\TestFramework\Inspection\WordsFinder( $configFiles, - __DIR__ . '/_files/words_finder' + __DIR__ . '/_files/words_finder', + new ComponentRegistrar() ); $actual = $wordsFinder->findWords($file); $this->assertEquals($expected, $actual); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFiles.php b/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFiles.php index 5d2b0626b4b4b..04c90dacf004e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFiles.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFiles.php @@ -25,7 +25,7 @@ protected function setUp() */ public function getLocalePlacePath() { - $pathToSource = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); + $pathToSource = BP; $places = []; $componentRegistrar = new ComponentRegistrar(); foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $modulePath) { diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFilesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFilesTest.php index b5409544a60cd..b096322e8169c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFilesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/App/Language/TranslationFilesTest.php @@ -52,7 +52,7 @@ public function defaultLocaleDataProvider() $parser = $this->prepareParser(); $optionResolverFactory = new ResolverFactory(); - $optionResolver = $optionResolverFactory->create(Files::init()->getPathToSource(), true); + $optionResolver = $optionResolverFactory->create(BP, true); $parser->parse($optionResolver->getOptions()); @@ -80,8 +80,7 @@ public function defaultLocaleDataProvider() protected function buildFilePath($phrase, $context) { $path = $this->getContext()->buildPathToLocaleDirectoryByContext($phrase->getContextType(), $context); - $sourcePath = Files::init()->getPathToSource(); - return $sourcePath . '/' . $path . Locale::DEFAULT_SYSTEM_LOCALE . '.' . Csv::FILE_EXTENSION; + return $path . Locale::DEFAULT_SYSTEM_LOCALE . '.' . Csv::FILE_EXTENSION; } /** diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php index bae290d83ff8e..4d08ff438ea1e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php @@ -209,11 +209,7 @@ public function testClassNamespaces() * @param array $file */ function ($file) { - $relativePath = str_replace( - Files::init()->getPathToSource() . "/", - "", - $file - ); + $relativePath = str_replace(BP . "/", "", $file); // exceptions made for fixture files from tests if (strpos($relativePath, '/_files/') !== false) { return; @@ -291,11 +287,7 @@ public function testClassReferences() * @param string $file */ function ($file) { - $relativePath = str_replace( - Files::init()->getPathToSource(), - "", - $file - ); + $relativePath = str_replace(BP, "", $file); // Due to the examples given with the regex patterns, we skip this test file itself if ($relativePath == "/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php") { return; @@ -575,7 +567,7 @@ private function removeSpecialCasesForAllOthers($componentRegistrar, $namespaceP '/dev/tests/static/testsuite/', '/setup/src/', ]; - $pathToSource = Files::init()->getPathToSource(); + $pathToSource = BP; $libraryPaths = $this->getLibraryPaths($componentRegistrar, $pathToSource); $directories = array_merge($directories, $libraryPaths); // Full list of directories where there may be namespace classes diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 7391f2f0727ae..085270afadeab 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -53,7 +53,7 @@ public static function setUpBeforeClass() } self::$shell = self::createShell(); self::$isComposerAvailable = self::isComposerAvailable(); - self::$root = Files::init()->getPathToSource(); + self::$root = BP; self::$rootJson = json_decode(file_get_contents(self::$root . '/composer.json'), true); self::$dependencies = []; } @@ -84,7 +84,7 @@ function ($dir, $packageType) { */ public function validateComposerJsonDataProvider() { - $root = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); + $root = BP; $componentRegistrar = new ComponentRegistrar(); $result = []; foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $dir) { diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php index 673a6ffe87b94..e21884aecb8e9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php @@ -143,7 +143,7 @@ class DependencyTest extends \PHPUnit_Framework_TestCase */ public static function setUpBeforeClass() { - $root = Files::init()->getPathToSource(); + $root = BP; $rootJson = json_decode(file_get_contents($root . '/composer.json'), true); if (preg_match('/magento\/project-*/', $rootJson['name']) == 1) { @@ -274,23 +274,24 @@ public function testUndeclared() */ function ($fileType, $file) { // Validates file when it is belonged to default themes - $filename = self::_getRelativeFilename($file); - $isThemeFile = (bool)preg_match(self::$_defaultThemes, $filename); - $componentRegistrar = new ComponentRegistrar(); - $foundModuleName = ''; - if (!$isThemeFile) { - foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) { - if (strpos($file, $moduleDir . '/') !== false) { - $foundModuleName = str_replace('_', '\\', $moduleName); - break; - } - } - if (empty($foundModuleName)) { + foreach ($componentRegistrar->getPaths(ComponentRegistrar::THEME) as $themeDir) { + if (strpos($file, $themeDir . '/') !== false) { return; } } + $foundModuleName = ''; + foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) { + if (strpos($file, $moduleDir . '/') !== false) { + $foundModuleName = str_replace('_', '\\', $moduleName); + break; + } + } + if (empty($foundModuleName)) { + return; + } + $module = $foundModuleName; $contents = $this->_getCleanedFileContents($fileType, $file); @@ -435,19 +436,6 @@ public function testRedundant() } } - /** - * Extract Magento relative filename from absolute filename - * - * @param string $absoluteFilename - * @return string - */ - protected static function _getRelativeFilename($absoluteFilename) - { - $pathToSource = Files::init()->getPathToSource(); - $relativeFileName = str_replace($pathToSource, '', $absoluteFilename); - return trim(str_replace('\\', '/', $relativeFileName), '/'); - } - /** * Convert file list to data provider structure * diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Di/CompilerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Di/CompilerTest.php index 3678497fafab1..3c3878f4906fa 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Di/CompilerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Di/CompilerTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Api\Code\Generator\Mapper; use Magento\Framework\Api\Code\Generator\SearchResults; +use Magento\Framework\Component\ComponentRegistrar; use Magento\Framework\ObjectManager\Code\Generator\Converter; use Magento\Framework\ObjectManager\Code\Generator\Factory; use Magento\Framework\ObjectManager\Code\Generator\Repository; @@ -66,7 +67,7 @@ class CompilerTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->_shell = new \Magento\Framework\Shell(new \Magento\Framework\Shell\CommandRenderer()); - $basePath = Files::init()->getPathToSource(); + $basePath = BP; $basePath = str_replace('\\', '/', $basePath); @@ -195,25 +196,27 @@ protected function _classExistsAsReal($instanceName) */ protected function _phpClassesDataProvider() { - $basePath = Files::init()->getPathToSource(); - - $libPath = 'lib\\internal'; - $appPath = 'app\\code'; - $generationPathPath = str_replace('/', '\\', str_replace($basePath . '/', '', $this->_generationDir)); + $generationPath = str_replace('/', '\\', $this->_generationDir); $files = Files::init()->getPhpFiles(Files::INCLUDE_APP_CODE | Files::INCLUDE_LIBS); - $patterns = [ - '/' . preg_quote($libPath) . '/', - '/' . preg_quote($appPath) . '/', - '/' . preg_quote($generationPathPath) . '/', - ]; - $replacements = ['', '', '']; + $patterns = ['/' . preg_quote($generationPath) . '/',]; + $replacements = ['']; + + $componentRegistrar = new ComponentRegistrar(); + foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) { + $patterns[] = '/' . preg_quote(str_replace('/', '\\', $modulePath)) . '/'; + $replacements[] = '\\' . str_replace('_', '\\', $moduleName); + } + + foreach ($componentRegistrar->getPaths(ComponentRegistrar::LIBRARY) as $libPath) { + $patterns[] = '/' . preg_quote(str_replace('/', '\\', $libPath)) . '/'; + $replacements[] = '\\Magento\\Framework'; + } /** Convert file names into class name format */ $classes = []; foreach ($files as $file) { - $file = str_replace($basePath . '/', '', $file); $file = str_replace('/', '\\', $file); $filePath = preg_replace($patterns, $replacements, $file); $className = substr($filePath, 0, -4); @@ -258,10 +261,7 @@ protected function _buildInheritanceHierarchyTree($className, array $allowedFile $parent = $class->getParentClass(); $file = false; if ($parent) { - $basePath = Files::init()->getPathToSource(); $file = str_replace('\\', DIRECTORY_SEPARATOR, $parent->getFileName()); - $basePath = str_replace('\\', DIRECTORY_SEPARATOR, $basePath); - $file = str_replace($basePath . DIRECTORY_SEPARATOR, '', $file); } /** Prevent analysis of non Magento classes */ if ($parent && in_array($file, $allowedFiles)) { diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php index a81176dbee21c..fbeb4fa665f97 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/HhvmCompatibilityTest.php @@ -72,7 +72,7 @@ protected function getFiles() | Files::INCLUDE_NON_CLASSES ), Files::init()->getPhtmlFiles(false, false), - Files::init()->getFiles([Files::init()->getPathToSource() . '/dev/'], '*.php') + Files::init()->getFiles([BP . '/dev/'], '*.php') ); } @@ -95,10 +95,9 @@ protected function parseDirectives($file) */ protected function createMessage($deniedDirectives) { - $rootPath = Files::init()->getPathToSource(); $message = 'HHVM-incompatible ini_get/ini_set options were found:'; foreach ($deniedDirectives as $file => $fileDeniedDirectives) { - $message .= "\n" . str_replace($rootPath, '', $file) . ': [' . implode(', ', $fileDeniedDirectives) . ']'; + $message .= "\n" . $file . ': [' . implode(', ', $fileDeniedDirectives) . ']'; } return $message; } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php index 7cc87d570344c..b2ef80867c9a5 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Library/DependencyTest.php @@ -79,18 +79,21 @@ function ($file) { public function testAppCodeUsage() { $files = Files::init(); - $path = $files->getPathToSource(); + $componentRegistrar = new ComponentRegistrar(); + $libPaths = $componentRegistrar->getPaths(ComponentRegistrar::LIBRARY); $invoker = new AggregateInvoker($this); $invoker( - function ($file) use ($path) { + function ($file) use ($libPaths) { $content = file_get_contents($file); - if (strpos($file, $path . '/lib/') === 0) { - $this->assertSame( - 0, - preg_match('~(?|function\\s)__\\s*\\(~iS', $content), - 'Function __() is defined outside of the library and must not be used there. ' . - 'Replacement suggestion: new \\Magento\\Framework\\Phrase()' - ); + foreach ($libPaths as $libPath) { + if (strpos($file, $libPath) === 0) { + $this->assertSame( + 0, + preg_match('~(?|function\\s)__\\s*\\(~iS', $content), + 'Function __() is defined outside of the library and must not be used there. ' . + 'Replacement suggestion: new \\Magento\\Framework\\Phrase()' + ); + } } }, $files->getPhpFiles( diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Phrase/AbstractTestCase.php b/dev/tests/static/testsuite/Magento/Test/Integrity/Phrase/AbstractTestCase.php index 6d062cc46bc49..89b5070fe07de 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/Phrase/AbstractTestCase.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Phrase/AbstractTestCase.php @@ -9,6 +9,7 @@ */ namespace Magento\Test\Integrity\Phrase; +use Magento\Framework\Component\ComponentRegistrar; use Magento\Setup\Module\I18n\FilesCollector; class AbstractTestCase extends \PHPUnit_Framework_TestCase @@ -36,10 +37,14 @@ protected function _createMissedPhraseError($phrase) */ protected function _getFiles() { - $filesCollector = new \Magento\Setup\Module\I18n\FilesCollector(); - + $filesCollector = new FilesCollector(); + $componentRegistrar = new ComponentRegistrar(); + $paths = array_merge( + $componentRegistrar->getPaths(ComponentRegistrar::MODULE), + $componentRegistrar->getPaths(ComponentRegistrar::LIBRARY) + ); return $filesCollector->getFiles( - [\Magento\Framework\App\Utility\Files::init()->getPathToSource()], + $paths, '/\.(php|phtml)$/' ); } diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/TestPlacementTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/TestPlacementTest.php index 9cc806fa91028..81e66f1c51c34 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/TestPlacementTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/TestPlacementTest.php @@ -24,7 +24,7 @@ class TestPlacementTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->root = Files::init()->getPathToSource(); + $this->root = BP; } public function testUnitTestFilesPlacement() diff --git a/dev/tests/static/testsuite/Magento/Test/Js/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Js/LiveCodeTest.php index 94913c98be5ad..1894917502382 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Js/LiveCodeTest.php @@ -58,7 +58,7 @@ protected static function _scanJsFile($path) */ public static function setUpBeforeClass() { - $reportDir = Files::init()->getPathToSource() . '/dev/tests/static/report'; + $reportDir = BP . '/dev/tests/static/report'; if (!is_dir($reportDir)) { mkdir($reportDir, 0770); } diff --git a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt index b78c57607c0ec..9e71665b5e1d6 100644 --- a/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt +++ b/dev/tests/static/testsuite/Magento/Test/Js/_files/blacklist/magento.txt @@ -1055,3 +1055,4 @@ vendor/magento/theme-adminhtml-backend/web/js/theme.js vendor/magento/theme-frontend-blank/web/js/navigation-menu.js vendor/magento/theme-frontend-blank/web/js/responsive.js vendor/magento/theme-frontend-blank/web/js/theme.js +app/code/Magento/BraintreeTwo/view/frontend/requirejs-config.js diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/FilesystemTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/FilesystemTest.php index d3fe7736e7f60..bb025beb6f444 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/FilesystemTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/FilesystemTest.php @@ -22,7 +22,7 @@ public function testRelocations() */ function ($path) { $this->assertFileNotExists( - \Magento\Framework\App\Utility\Files::init()->getPathToSource() . '/' . $path + BP . '/' . $path ); }, $this->relocationsDataProvider() @@ -58,19 +58,22 @@ public function relocationsDataProvider() public function testObsoleteDirectories() { - $area = '*'; - $theme = '*'; - $root = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); - $dirs = glob("{$root}/app/design/{$area}/{$theme}/template", GLOB_ONLYDIR); + $componentRegistrar = new ComponentRegistrar(); + $dirs = []; + foreach ($componentRegistrar->getPaths(ComponentRegistrar::THEME) as $path) { + $dirs = array_merge($dirs, glob($path . '/template', GLOB_ONLYDIR)); + } $msg = []; if ($dirs) { $msg[] = 'Theme "template" directories are obsolete. Relocate files as follows:'; foreach ($dirs as $dir) { - $msg[] = str_replace($root, '', "{$dir} => " . realpath($dir . '/..') . '/Namespace_Module/*'); + $msg[] = "{$dir} => " . realpath($dir . '/..') . '/Namespace_Module/*'; } } - - $dirs = glob("{$root}/app/design/{$area}/{$theme}/layout", GLOB_ONLYDIR); + $dirs = []; + foreach ($componentRegistrar->getPaths(ComponentRegistrar::THEME) as $path) { + $dirs = array_merge($dirs, glob($path . '/layout', GLOB_ONLYDIR)); + } if ($dirs) { $msg[] = 'Theme "layout" directories are obsolete. Relocate layout files into the root of theme directory.'; $msg = array_merge($msg, $dirs); @@ -86,26 +89,26 @@ public function testObsoleteViewPaths() $allowedFiles = ['requirejs-config.js', 'layouts.xml']; $allowedThemeFiles = array_merge( $allowedFiles, - ['composer.json', 'theme.xml', 'LICENSE.txt', 'LICENSE_AFL.txt'] + ['composer.json', 'theme.xml', 'LICENSE.txt', 'LICENSE_EE.txt', 'LICENSE_AFL.txt', 'registration.php'] ); $areas = '{frontend,adminhtml,base}'; - $ns = '*'; - $mod = '*'; - $pathsToCheck = [ - BP . "/app/design/{$areas}/{$ns}/{$mod}/*" => [ + $componentRegistrar = new ComponentRegistrar(); + $pathsToCheck = []; + foreach ($componentRegistrar->getPaths(ComponentRegistrar::THEME) as $themeDir) { + $pathsToCheck[$themeDir . '/*'] = [ 'allowed_files' => $allowedThemeFiles, 'allowed_dirs' => ['layout', 'page_layout', 'templates', 'web', 'etc', 'i18n', 'media', '\w+_\w+'], - ], - BP . "app/design/{$areas}/{$ns}/{$mod}/{$ns}_{$mod}/*" => [ + ]; + $pathsToCheck[$themeDir . '/*_*/*'] = [ 'allowed_files' => $allowedThemeFiles, - 'allowed_dirs' => ['layout', 'page_layout', 'templates', 'web'], - ], - ]; - $componentRegistrar = new ComponentRegistrar(); + 'allowed_dirs' => ['layout', 'page_layout', 'templates', 'web', 'email'], + ]; + } + foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleDir) { $pathsToCheck[$moduleDir . "/view/{$areas}/*"] = [ 'allowed_files' => $allowedFiles, - 'allowed_dirs' => ['layout', 'page_layout', 'templates', 'web'] + 'allowed_dirs' => ['layout', 'page_layout', 'templates', 'web', 'ui_component', 'email'] ]; } $errors = []; @@ -114,7 +117,7 @@ public function testObsoleteViewPaths() $allowedDirs = $allowed['allowed_dirs']; $foundFiles = glob($path, GLOB_BRACE); if (!$foundFiles) { - $this->fail("Glob pattern returned empty result: {$path}"); + continue; } foreach ($foundFiles as $file) { $baseName = basename($file); diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/LibraryLocationTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/LibraryLocationTest.php index 9f09259a3da34..4c456327ef8c3 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/LibraryLocationTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/LibraryLocationTest.php @@ -20,7 +20,7 @@ class LibraryLocationTest extends \PHPUnit_Framework_TestCase public static function setUpBeforeClass() { - self::$root = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); + self::$root = BP; } public function testOldWebLibrariesLocation() diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/LicenseTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/LicenseTest.php index 2e1d4a214f858..1b270f3fe731a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/LicenseTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/LicenseTest.php @@ -37,29 +37,13 @@ function ($filename) { public function legacyCommentDataProvider() { - $root = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); - $recursiveIterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS) - ); - - $rootFolderName = substr(strrchr($root, '/'), 1); - $extensions = '(xml|css|php|phtml|js|dist|sample|additional)'; - $paths = [ - $rootFolderName . '/[^/]+\.' . $extensions, - $rootFolderName . '/app/.+\.' . $extensions, - $rootFolderName . '/dev/(?!tests/integration/tmp|tests/functional).+\.' . $extensions, - $rootFolderName . '/lib/internal/(Mage|Magento|Varien)/.+\.' . $extensions, - $rootFolderName . '/pub/.+\.' . $extensions, - ]; - $regexIterator = new \RegexIterator($recursiveIterator, '#(' . implode(' | ', $paths) . ')$#x'); - + $allFiles = \Magento\Framework\App\Utility\Files::init()->getAllFiles(); $result = []; - foreach ($regexIterator as $fileInfo) { - $filename = (string)$fileInfo; - if (!file_exists($filename) || !is_readable($filename)) { + foreach ($allFiles as $file) { + if (!file_exists($file[0]) || !is_readable($file[0])) { continue; } - $result[] = [$filename]; + $result[] = [$file[0]]; } return $result; } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php index 813aababf3b6b..2fd1427270d33 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteCodeTest.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Utility\Files; use Magento\Framework\App\Utility\AggregateInvoker; +use Magento\Framework\Component\ComponentRegistrar; use Magento\TestFramework\Utility\ChangedFiles; /** @@ -111,7 +112,7 @@ public function testPhpFiles() $changedFiles = ChangedFiles::getPhpFiles(__DIR__ . '/_files/changed_files*'); $blacklistFiles = $this->getBlacklistFiles(); foreach ($blacklistFiles as $blacklistFile) { - unset($changedFiles[$blacklistFile]); + unset($changedFiles[BP . $blacklistFile]); } $invoker( function ($file) { @@ -289,7 +290,7 @@ protected function _testObsoleteMethods($content, $file) } /** - * Assert that obsolete pathes are not used in the content + * Assert that obsolete paths are not used in the content * * This method will search the content for references to class * that start with obsolete namespace @@ -300,7 +301,7 @@ protected function _testObsoletePaths($file) { foreach (self::$_paths as $row) { list($obsoletePath, , $replacementPath) = $row; - $relativePath = str_replace(Files::init()->getPathToSource(), "", $file); + $relativePath = str_replace(BP, '', $file); $message = $this->_suggestReplacement( "Path '{$obsoletePath}' is obsolete.", $replacementPath @@ -324,13 +325,16 @@ protected function _testObsoletePaths($file) */ protected function _testGetChildSpecialCase($content, $file) { - if (0 === strpos($file, Files::init()->getPathToSource() . '/app/')) { - $this->_assertNotRegexp( - '/[^a-z\d_]getChild\s*\(/iS', - $content, - 'Block method getChild() is obsolete. ' . - 'Replacement suggestion: \Magento\Framework\View\Element\AbstractBlock::getChildBlock()' - ); + $componentRegistrar = new ComponentRegistrar(); + foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $modulePath) { + if (0 === strpos($file, $modulePath)) { + $this->_assertNotRegexp( + '/[^a-z\d_]getChild\s*\(/iS', + $content, + 'Block method getChild() is obsolete. ' . + 'Replacement suggestion: \Magento\Framework\View\Element\AbstractBlock::getChildBlock()' + ); + } } } @@ -933,7 +937,7 @@ private function getBlacklistFiles($absolutePath = false) { $blackList = include __DIR__ . '/_files/blacklist/obsolete_mage.php'; $ignored = []; - $appPath = Files::init()->getPathToSource(); + $appPath = BP; foreach ($blackList as $file) { if ($absolutePath) { $ignored = array_merge($ignored, glob($appPath . DIRECTORY_SEPARATOR . $file, GLOB_NOSORT)); diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteConnectionTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteConnectionTest.php index 9fe3039599956..2adede623a62a 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteConnectionTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteConnectionTest.php @@ -3,9 +3,10 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Test\Legacy; +use Magento\Framework\Component\ComponentRegistrar; + /** * Temporary test * Test verifies obsolete usages in modules that were refactored to work with getConnection. @@ -34,7 +35,6 @@ class ObsoleteConnectionTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->appPath = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); $this->obsoleteMethods = [ '_getReadConnection', '_getWriteConnection', @@ -106,13 +106,15 @@ function ($file) { public function modulesFilesDataProvider() { $filesList = []; - - foreach ($this->getFilesData('whitelist/refactored_modules*') as $refactoredFolder) { - $files = \Magento\Framework\App\Utility\Files::init()->getFiles( - [$this->appPath . $refactoredFolder], - '*.php' - ); - $filesList = array_merge($filesList, $files); + $componentRegistrar = new ComponentRegistrar(); + foreach ($this->getFilesData('whitelist/refactored_modules*') as $refactoredModule) { + if ($componentRegistrar->getPath(ComponentRegistrar::MODULE, $refactoredModule)) { + $files = \Magento\Framework\App\Utility\Files::init()->getFiles( + [$componentRegistrar->getPath(ComponentRegistrar::MODULE, $refactoredModule)], + '*.php' + ); + $filesList = array_merge($filesList, $files); + } } $result = array_map('realpath', $filesList); @@ -126,8 +128,10 @@ public function modulesFilesDataProvider() protected function getBlackList() { $blackListFiles = []; - foreach ($this->getFilesData('blacklist/files_list*') as $file) { - $blackListFiles[] = realpath($this->appPath . $file); + $componentRegistrar = new ComponentRegistrar(); + foreach ($this->getFilesData('blacklist/files_list*') as $fileInfo) { + $blackListFiles[] = $componentRegistrar->getPath(ComponentRegistrar::MODULE, $fileInfo[0]) + . DIRECTORY_SEPARATOR . $fileInfo[1]; } return $blackListFiles; } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteResponseTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteResponseTest.php index 73282abd58718..b35780fa22708 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteResponseTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ObsoleteResponseTest.php @@ -3,9 +3,10 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Test\Legacy; +use Magento\Framework\Component\ComponentRegistrar; + /** * Temporary test that will be removed in scope of MAGETWO-28356. * Test verifies obsolete usages in modules that were refactored to work with ResultInterface. @@ -29,7 +30,6 @@ class ObsoleteResponseTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->appPath = \Magento\Framework\App\Utility\Files::init()->getPathToSource(); $this->obsoleteMethods = include __DIR__ . '/_files/response/obsolete_response_methods.php'; $this->filesBlackList = $this->getBlackList(); } @@ -64,13 +64,15 @@ function ($file) { public function modulesFilesDataProvider() { $filesList = []; - - foreach ($this->getFilesData('whitelist/refactored_modules*') as $refactoredFolder) { - $files = \Magento\Framework\App\Utility\Files::init()->getFiles( - [$this->appPath . $refactoredFolder], - '*.php' - ); - $filesList = array_merge($filesList, $files); + $componentRegistrar = new ComponentRegistrar(); + foreach ($this->getFilesData('whitelist/refactored_modules*') as $refactoredModule) { + if ($componentRegistrar->getPath(ComponentRegistrar::MODULE, $refactoredModule)) { + $files = \Magento\Framework\App\Utility\Files::init()->getFiles( + [$componentRegistrar->getPath(ComponentRegistrar::MODULE, $refactoredModule)], + '*.php' + ); + $filesList = array_merge($filesList, $files); + } } $result = array_map('realpath', $filesList); @@ -84,8 +86,10 @@ public function modulesFilesDataProvider() protected function getBlackList() { $blackListFiles = []; - foreach ($this->getFilesData('blacklist/files_list*') as $file) { - $blackListFiles[] = realpath($this->appPath . $file); + $componentRegistrar = new ComponentRegistrar(); + foreach ($this->getFilesData('blacklist/files_list*') as $fileInfo) { + $blackListFiles[] = $componentRegistrar->getPath(ComponentRegistrar::MODULE, $fileInfo[0]) + . DIRECTORY_SEPARATOR . $fileInfo[1]; } return $blackListFiles; } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php index 12b052cab56f1..cb6e9e69d8288 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/RestrictedCodeTest.php @@ -9,6 +9,8 @@ */ namespace Magento\Test\Legacy; +use Magento\Framework\Component\ComponentRegistrar; + class RestrictedCodeTest extends \PHPUnit_Framework_TestCase { /**@#+ @@ -47,7 +49,7 @@ protected static function _loadData(array &$data, $filePattern) $relativePath = str_replace( '\\', '/', - str_replace(\Magento\Framework\App\Utility\Files::init()->getPathToSource(), '', $file) + str_replace(BP, '', $file) ); array_push(self::$_fixtureFiles, $relativePath); $data = array_merge_recursive($data, self::_readList($file)); @@ -74,8 +76,8 @@ public function testPhpFiles() $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); $testFiles = \Magento\TestFramework\Utility\ChangedFiles::getPhpFiles(__DIR__ . '/_files/changed_files*'); foreach (self::$_fixtureFiles as $fixtureFile) { - if (array_key_exists($fixtureFile, $testFiles)) { - unset($testFiles[$fixtureFile]); + if (array_key_exists(BP . $fixtureFile, $testFiles)) { + unset($testFiles[BP . $fixtureFile]); } } $invoker( @@ -95,33 +97,24 @@ function ($file) { protected function _testRestrictedClasses($file) { $content = file_get_contents($file); + $componentRegistrar = new ComponentRegistrar(); foreach (self::$_classes as $restrictedClass => $classRules) { - foreach ($classRules['exclude'] as $skippedPath) { - if ($this->_isFileInPath($skippedPath, $file)) { + foreach ($classRules['exclude'] as $skippedPathInfo) { + $skippedPath = $componentRegistrar->getPath($skippedPathInfo['type'], $skippedPathInfo['name']) + . '/' . $skippedPathInfo['path']; + if (strpos($file, $skippedPath) === 0) { continue 2; } } $this->assertFalse( \Magento\TestFramework\Utility\CodeCheck::isClassUsed($restrictedClass, $content), sprintf( - "Class '%s' is restricted. Suggested replacement: %s", + "Class '%s' is restricted in %s. Suggested replacement: %s", $restrictedClass, + $file, $classRules['replacement'] ) ); } } - - /** - * Checks if the file path (relative to Magento folder) starts with the given path - * - * @param string $subPath - * @param string $file - * @return bool - */ - protected function _isFileInPath($subPath, $file) - { - $relativePath = str_replace(\Magento\Framework\App\Utility\Files::init()->getPathToSource(), '', $file); - return 0 === strpos($relativePath, $subPath); - } } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/WordsTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/WordsTest.php index d70e846742640..0b8b92e00c79e 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/WordsTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/WordsTest.php @@ -9,6 +9,8 @@ */ namespace Magento\Test\Legacy; +use Magento\Framework\Component\ComponentRegistrar; + class WordsTest extends \PHPUnit_Framework_TestCase { /** @@ -20,7 +22,8 @@ public static function setUpBeforeClass() { self::$_wordsFinder = new \Magento\TestFramework\Inspection\WordsFinder( glob(__DIR__ . '/_files/words_*.xml'), - \Magento\Framework\App\Utility\Files::init()->getPathToSource() + BP, + new ComponentRegistrar() ); } @@ -32,7 +35,7 @@ public function testWords() * @param string $file */ function ($file) { - $words = self::$_wordsFinder->findWords($file); + $words = self::$_wordsFinder->findWords(realpath($file)); if ($words) { $this->fail("Found words: '" . implode("', '", $words) . "' in '{$file}' file"); } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php index 7b4ddc53272ae..faa315fdb70ed 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php @@ -8,11 +8,11 @@ * Files that excluded from results */ return [ - '/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Export/AdvancedPricingTest.php', - '/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php', - '/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php', - '/app/code/Magento/CatalogImportExport/Model/Export/Product.php', - '/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php', - '/app/code/Magento/EncryptionKey/Model/Resource/Key/Change.php', - //example '/app/code/Magento/Backend/Model/View.php', + ['Magento_AdvancedPricingImportExport', 'Test/Unit/Model/Export/AdvancedPricingTest.php'], + ['Magento_AdvancedPricingImportExport', 'Model/Export/AdvancedPricing.php'], + ['Magento_CatalogImportExport', 'Test/Unit/Model/Export/ProductTest.php'], + ['Magento_CatalogImportExport' , 'Model/Export/Product.php'], + ['Magento_EncryptionKey', 'Controller/Adminhtml/Crypt/Key/Index.php'], + ['Magento_EncryptionKey', 'Model/Resource/Key/Change.php'], + //example ['Magento_Backend', 'Model/View.php'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/whitelist/refactored_modules.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/whitelist/refactored_modules.php index 784541bfad676..82dbce0e2c366 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/whitelist/refactored_modules.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/whitelist/refactored_modules.php @@ -8,18 +8,18 @@ * Modules that were refactored */ return [ - '/app/code/Magento/AdminNotification', - '/app/code/Magento/AdvancedPricingImportExport', - '/app/code/Magento/Authorization', - '/app/code/Magento/Authorizenet', - '/app/code/Magento/Backend', - '/app/code/Magento/Captcha', - '/app/code/Magento/Catalog', - '/app/code/Magento/CatalogImportExport', - '/app/code/Magento/CatalogInventory', - '/app/code/Magento/CatalogRule', - '/app/code/Magento/CatalogSearch', - '/app/code/Magento/CatalogUrlRewrite', - '/app/code/Magento/CatalogWidget', - // example '/app/code/Magento/Catalog', + 'Magento_AdminNotification', + 'Magento_AdvancedPricingImportExport', + 'Magento_Authorization', + 'Magento_Authorizenet', + 'Magento_Backend', + 'Magento_Captcha', + 'Magento_Catalog', + 'Magento_CatalogImportExport', + 'Magento_CatalogInventory', + 'Magento_CatalogRule', + 'Magento_CatalogSearch', + 'Magento_CatalogUrlRewrite', + 'Magento_CatalogWidget', + // example 'Magento_Catalog', ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php index 0777e3e2b5287..5d5b4ccb0a025 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_methods.php @@ -2505,4 +2505,12 @@ ['get', 'Magento\Quote\Api\ShippingMethodManagementInterface', 'Magento\Quote\Model\ShippingMethodManagementInterface::get'], ['set', 'Magento\Quote\Api\ShippingMethodManagementInterface', 'Magento\Quote\Model\ShippingMethodManagementInterface::get'], ['getTypeSwitcherData', 'Magento\Catalog\Block\Adminhtml\Product'], + ['_afterLoad', 'Magento\CatalogRule\Model\ResourceModel\Rule'], + ['_afterSave', 'Magento\CatalogRule\Model\ResourceModel\Rule'], + ['_beforeDelete', 'Magento\Cms\Model\ResourceModel\Page'], + ['_afterSave', 'Magento\Cms\Model\ResourceModel\Page', 'Magento\Cms\Model\ResourceModel\Page\Relation\Store\SaveHandler::execute'], + ['_afterLoad', 'Magento\Cms\Model\ResourceModel\Page', 'Magento\Cms\Model\ResourceModel\Page\Relation\Store\ReadHandler::execute'], + ['_beforeDelete', 'Magento\Cms\Model\ResourceModel\Block'], + ['_afterSave', 'Magento\Cms\Model\ResourceModel\Block', 'Magento\Cms\Model\ResourceModel\Block\Relation\Store\SaveHandler::execute'], + ['_afterLoad', 'Magento\Cms\Model\ResourceModel\Block', 'Magento\Cms\Model\ResourceModel\Block\Relation\Store\ReadHandler::execute'], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_response_methods.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_response_methods.php deleted file mode 100644 index 8102968e13d82..0000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_response_methods.php +++ /dev/null @@ -1,22 +0,0 @@ - [ 'replacement' => '\Magento\Framework\DB\Select', 'exclude' => [ - '/lib/internal/Magento/Framework/DB/Select.php', - '/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php', - '/lib/internal/Magento/Framework/Model/Resource/Iterator.php', + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Select.php'], + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'Model/ResourceModel/Iterator.php'], ] ], 'Zend_Db_Adapter_Pdo_Mysql' => [ 'replacement' => '\Magento\Framework\DB\Adapter\Pdo\Mysql', 'exclude' => [ - '/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php', + ['type' => 'library', 'name' => 'magento/framework', 'path' => 'DB/Adapter/Pdo/Mysql.php'], ] ], ]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/whitelist/refactored_controllers.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/whitelist/refactored_controllers.php deleted file mode 100644 index e75568b7125f0..0000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/whitelist/refactored_controllers.php +++ /dev/null @@ -1,17 +0,0 @@ -dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RequestIdOverrideTest.php overriden + + composer.lock + diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index 4a2aef8eaac43..c667e01ecad22 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -39,7 +39,7 @@ class LiveCodeTest extends PHPUnit_Framework_TestCase */ public static function setUpBeforeClass() { - self::$pathToSource = Utility\Files::init()->getPathToSource(); + self::$pathToSource = BP; self::$reportDir = self::$pathToSource . '/dev/tests/static/report'; if (!is_dir(self::$reportDir)) { mkdir(self::$reportDir, 0770); diff --git a/lib/internal/Magento/Framework/App/ResourceConnection.php b/lib/internal/Magento/Framework/App/ResourceConnection.php index 56d220ebb57f4..2747bef243e5b 100644 --- a/lib/internal/Magento/Framework/App/ResourceConnection.php +++ b/lib/internal/Magento/Framework/App/ResourceConnection.php @@ -152,6 +152,18 @@ public function getTableName($modelEntity, $connectionName = self::DEFAULT_CONNE return $this->getConnection($connectionName)->getTableName($tableName); } + /** + * Gets table placeholder by table name + * + * @param string $tableName + * @return string + */ + public function getTablePlaceholder($tableName) + { + $tableName = preg_replace('/^' . preg_quote($this->getTablePrefix()) . '_/', '', $tableName); + return $tableName; + } + /** * Build a trigger name * diff --git a/lib/internal/Magento/Framework/App/Utility/Classes.php b/lib/internal/Magento/Framework/App/Utility/Classes.php index 8956ce88c342d..d8d5c1aef7ccc 100644 --- a/lib/internal/Magento/Framework/App/Utility/Classes.php +++ b/lib/internal/Magento/Framework/App/Utility/Classes.php @@ -7,7 +7,7 @@ */ namespace Magento\Framework\App\Utility; -use Magento\Framework\App\Utility\Files; +use Magento\Framework\Component\ComponentRegistrar; class Classes { @@ -186,21 +186,18 @@ public static function collectLayoutClasses(\SimpleXMLElement $xml) */ public static function collectModuleClasses($subTypePattern = '[A-Za-z]+') { - $pattern = '/^' . preg_quote( - Files::init()->getPathToSource(), - '/' - ) . '\/app\/code\/([A-Za-z]+)\/([A-Za-z]+)\/(' . $subTypePattern . '\/.+)\.php$/'; + $componentRegistrar = new ComponentRegistrar(); $result = []; - foreach (Files::init()->getPhpFiles(Files::INCLUDE_APP_CODE | Files::INCLUDE_NON_CLASSES) as $file) { - if (preg_match($pattern, $file, $matches)) { - $module = "{$matches[1]}_{$matches[2]}"; - $class = "{$module}" . '\\' . str_replace( - '/', - '\\', - $matches[3] - ); - $key = str_replace('_', '\\', $class); - $result[$key] = $module; + foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $modulePath) { + $pattern = '/^' . preg_quote($modulePath, '/') . '\/(' . $subTypePattern . '\/.+)\.php$/'; + foreach (Files::init()->getFiles([$modulePath], '*.php') as $file) { + if (preg_match($pattern, $file)) { + $partialFileName = substr($file, strlen($modulePath) + 1); + $partialFileName = substr($partialFileName, 0, strlen($partialFileName) - strlen('.php')); + $partialClassName = str_replace('/', '\\', $partialFileName); + $className = str_replace('_', '\\', $moduleName) . '\\' . $partialClassName; + $result[$className] = $moduleName; + } } } return $result; diff --git a/lib/internal/Magento/Framework/App/Utility/Files.php b/lib/internal/Magento/Framework/App/Utility/Files.php index 79891dc769296..d405454e3024e 100644 --- a/lib/internal/Magento/Framework/App/Utility/Files.php +++ b/lib/internal/Magento/Framework/App/Utility/Files.php @@ -106,7 +106,7 @@ public static function composeDataSets(array $files) { $result = []; foreach ($files as $file) { - $result[substr($file, strlen(BP))] = [$file]; + $result[$file] = [$file]; } return $result; } @@ -339,7 +339,7 @@ public function getXmlFiles() */ public function getMainConfigFiles($asDataSet = true) { - $cacheKey = __METHOD__ . '|' . BP . '|' . serialize(func_get_args()); + $cacheKey = __METHOD__ . '|' . serialize(func_get_args()); if (!isset(self::$_cache[$cacheKey])) { $configXmlPaths = []; foreach ($this->componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleDir) { @@ -375,7 +375,7 @@ public function getConfigFiles( $excludedFileNames = ['wsdl.xml', 'wsdl2.xml', 'wsi.xml'], $asDataSet = true ) { - $cacheKey = __METHOD__ . '|' . BP . '|' . serialize(func_get_args()); + $cacheKey = __METHOD__ . '|' . serialize(func_get_args()); if (!isset(self::$_cache[$cacheKey])) { $files = $this->dirSearch->collectFiles(ComponentRegistrar::MODULE, "/etc/{$fileNamePattern}"); $files = array_filter( @@ -406,7 +406,7 @@ public function getXmlCatalogFiles( $excludedFileNames = [], $asDataSet = true ) { - $cacheKey = __METHOD__ . '|' . BP . '|' . serialize(func_get_args()); + $cacheKey = __METHOD__ . '|' . serialize(func_get_args()); if (!isset(self::$_cache[$cacheKey])) { $files = $this->getFilesSubset( $this->componentRegistrar->getPaths(ComponentRegistrar::MODULE), @@ -457,7 +457,7 @@ function ($file) use ($excludedFileNames) { */ public function getLayoutConfigFiles($fileNamePattern = '*.xml', $asDataSet = true) { - $cacheKey = __METHOD__ . '|' . BP . '|' . serialize(func_get_args()); + $cacheKey = __METHOD__ . '|' . serialize(func_get_args()); if (!isset(self::$_cache[$cacheKey])) { self::$_cache[$cacheKey] = $this->dirSearch->collectFiles( ComponentRegistrar::THEME, @@ -538,7 +538,7 @@ protected function getLayoutXmlFiles($location, $incomingParams = [], $asDataSet $params[$key] = $incomingParams[$key]; } } - $cacheKey = md5(BP . '|' . $location . '|' . implode('|', $params)); + $cacheKey = md5($location . '|' . implode('|', $params)); if (!isset(self::$_cache[__METHOD__][$cacheKey])) { $files = []; @@ -689,7 +689,7 @@ public function getPageTypeFiles($incomingParams = [], $asDataSet = true) $params[$key] = $incomingParams[$key]; } } - $cacheKey = md5(BP . '|' . implode('|', $params)); + $cacheKey = md5(implode('|', $params)); if (!isset(self::$_cache[__METHOD__][$cacheKey])) { self::$_cache[__METHOD__][$cacheKey] = self::getFiles( @@ -795,7 +795,7 @@ private function getThemePaths($area, $module, $subFolder) */ public function getStaticHtmlFiles($area = '*', $themePath = '*/*', $namespace = '*', $module = '*') { - $key = $area . $themePath . $namespace . $module . __METHOD__ . BP; + $key = $area . $themePath . $namespace . $module . __METHOD__; if (isset(self::$_cache[$key])) { return self::$_cache[$key]; } @@ -829,7 +829,7 @@ public function getStaticHtmlFiles($area = '*', $themePath = '*/*', $namespace = */ public function getStaticPreProcessingFiles($filePattern = '*') { - $key = __METHOD__ . BP . '|' . $filePattern; + $key = __METHOD__ . '|' . $filePattern; if (isset(self::$_cache[$key])) { return self::$_cache[$key]; } @@ -1032,7 +1032,7 @@ public function getJsFilesForArea($area) */ public function getPhtmlFiles($withMetaInfo = false, $asDataSet = true) { - $key = __METHOD__ . BP . '|' . (int)$withMetaInfo; + $key = __METHOD__ . (int)$withMetaInfo; if (!isset(self::$_cache[$key])) { $result = []; $this->accumulateModuleTemplateFiles($withMetaInfo, $result); @@ -1129,7 +1129,7 @@ private function accumulateModuleTemplateFiles($withMetaInfo, array &$result) */ public function getEmailTemplates() { - $key = __METHOD__ . BP; + $key = __METHOD__; if (isset(self::$_cache[$key])) { return self::$_cache[$key]; } @@ -1343,7 +1343,7 @@ private function classFileExistsCheckContent($fullPath, $namespace, $className) */ public function getNamespaces() { - $key = __METHOD__ . BP; + $key = __METHOD__; if (isset(self::$_cache[$key])) { return self::$_cache[$key]; } @@ -1405,7 +1405,7 @@ public function getModulePhpFiles($module, $asDataSet = true) */ public function getComposerFiles($componentType, $asDataSet = true) { - $key = __METHOD__ . '|' . BP . '|' . serialize(func_get_args()); + $key = __METHOD__ . '|' . serialize(func_get_args()); if (!isset(self::$_cache[$key])) { $excludes = $componentType == ComponentRegistrar::MODULE ? $this->getModuleTestDirsRegex() : []; $files = $this->getFilesSubset( @@ -1456,7 +1456,7 @@ public function readLists($globPattern) * Note that glob() for directories will be returned as is, * but passing directory is supported by the tools (phpcpd, phpmd, phpcs) */ - $files = glob($this->getPathToSource() . '/' . $pattern, GLOB_BRACE); + $files = glob(BP . '/' . $pattern, GLOB_BRACE); } else { throw new \UnexpectedValueException( "Incorrect pattern record '$pattern'. Supported formats: " diff --git a/lib/internal/Magento/Framework/DB/Ddl/Sequence.php b/lib/internal/Magento/Framework/DB/Ddl/Sequence.php index 1f0a4ca99c807..895243dd52903 100644 --- a/lib/internal/Magento/Framework/DB/Ddl/Sequence.php +++ b/lib/internal/Magento/Framework/DB/Ddl/Sequence.php @@ -15,16 +15,18 @@ class Sequence * * @param string $name * @param int $startNumber + * @param string $columnType + * @param bool|true $unsigned * @return string */ - public function getCreateSequenceDdl($name, $startNumber = 1) + public function getCreateSequenceDdl($name, $startNumber = 1, $columnType = Table::TYPE_INTEGER, $unsigned = true) { $format = "CREATE TABLE %s ( - sequence_value %s UNSIGNED NOT NULL AUTO_INCREMENT, + sequence_value %s %s NOT NULL AUTO_INCREMENT, PRIMARY KEY (sequence_value) ) AUTO_INCREMENT = %d"; - return sprintf($format, $name, Table::TYPE_INTEGER, $startNumber); + return sprintf($format, $name, $columnType, $unsigned ? 'UNSIGNED' : '', $startNumber); } /** diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/CreateEntityRow.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/CreateEntityRow.php index a2c3d624f2cd6..cc90efda449ea 100755 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/CreateEntityRow.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/CreateEntityRow.php @@ -42,7 +42,7 @@ protected function prepareData(EntityMetadata $metadata, $data) } if (isset($data[strtolower($column['COLUMN_NAME'])])) { $output[strtolower($column['COLUMN_NAME'])] = $data[strtolower($column['COLUMN_NAME'])]; - } else { + } elseif ($column['DEFAULT'] === null) { $output[strtolower($column['COLUMN_NAME'])] = null; } } diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/ExtensionPool.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/ExtensionPool.php index 6146d9765d0a4..745d4afac8963 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/ExtensionPool.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/ExtensionPool.php @@ -45,10 +45,10 @@ public function getActions($entityType, $actionName) { $actions = []; foreach ($this->extensionActions as $name => $actionGroup) { - if (!isset($actionGroup[$entityType][$actionName])) { - $actions[$name] = $this->objectManager->get($actionGroup['default'][$actionName]); - } else { + if (isset($actionGroup[$entityType][$actionName])) { $actions[$name] = $this->objectManager->get($actionGroup[$entityType][$actionName]); + } elseif (isset($actionGroup['default'])) { + $actions[$name] = $this->objectManager->get($actionGroup['default'][$actionName]); } } return $actions; diff --git a/lib/internal/Magento/Framework/Module/Setup.php b/lib/internal/Magento/Framework/Module/Setup.php index 3a3d6049074d2..d201ae71b2fdf 100644 --- a/lib/internal/Magento/Framework/Module/Setup.php +++ b/lib/internal/Magento/Framework/Module/Setup.php @@ -80,6 +80,17 @@ public function setTable($tableName, $realTableName) return $this; } + /** + * Gets table placeholder by table name + * + * @param string $tableName + * @return string + */ + public function getTablePlaceholder($tableName) + { + return $this->resourceModel->getTablePlaceholder($tableName); + } + /** * Get table name (validated by db adapter) by table placeholder * diff --git a/lib/internal/Magento/Framework/Setup/ExternalFKSetup.php b/lib/internal/Magento/Framework/Setup/ExternalFKSetup.php new file mode 100644 index 0000000000000..762db7708f9a6 --- /dev/null +++ b/lib/internal/Magento/Framework/Setup/ExternalFKSetup.php @@ -0,0 +1,223 @@ +setup = $setup; + $this->entityTable = $entityTable; + $this->entityColumn = $entityColumn; + $this->externalTable = $externalTable; + $this->externalColumn = $externalColumn; + + $this->execute(); + } + + /** + * Set external foreign key + * + * @return void + */ + protected function execute() + { + $entityTableInfo = $this->setup->getConnection()->describeTable( + $this->setup->getTable($this->entityTable) + ); + if (!$entityTableInfo[$this->entityColumn]['PRIMARY']) { + $this->dropOldForeignKey(); + $this->addForeignKeys(); + } else { + $this->addDefaultForeignKey(); + } + } + + /** + * Get foreign keys for tables and columns + * + * @param string $refTable + * @param string $refColumn + * @param string $targetTable + * @param string $targetColumn + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function getForeignKeys( + $targetTable, + $targetColumn, + $refTable, + $refColumn + ) { + $foreignKeys = $this->setup->getConnection()->getForeignKeys( + $this->setup->getTable($targetTable) + ); + $foreignKeys = array_filter( + $foreignKeys, + function ($key) use ($targetColumn, $refTable, $refColumn) { + return $key['COLUMN_NAME'] == $targetColumn + && $key['REF_TABLE_NAME'] == $refTable; + } + ); + return $foreignKeys; + } + + /** + * Remove foreign key if exists + * + * @param string $targetTable + * @param string $targetColumn + * @param string $refTable + * @param string $refColumn + * @return void + */ + protected function clearForeignKey( + $targetTable, + $targetColumn, + $refTable, + $refColumn + ) { + $foreignKeys = $this->getForeignKeys($targetTable, $targetColumn, $refTable, $refColumn); + foreach ($foreignKeys as $foreignKey) { + $this->setup->getConnection()->dropForeignKey( + $foreignKey['TABLE_NAME'], + $foreignKey['FK_NAME'] + ); + } + } + + /** + * Add default foreign key + * + * @return void + */ + protected function addDefaultForeignKey() + { + if (!count($this->getForeignKeys( + $this->externalTable, + $this->externalColumn, + $this->entityTable, + $this->entityColumn + ))) { + $this->setup->getConnection()->addForeignKey( + $this->setup->getFkName( + $this->externalTable, + $this->externalColumn, + $this->entityTable, + $this->entityColumn + ), + $this->setup->getTable($this->externalTable), + $this->externalColumn, + $this->setup->getTable($this->entityTable), + $this->entityColumn + ); + } + } + + /** + * Add foreign keys to entity table + * + * @return void + */ + protected function addForeignKeys() + { + $foreignKeys = $this->setup->getConnection()->getForeignKeys( + $this->setup->getTable($this->entityTable) + ); + $foreignKeys = array_filter( + $foreignKeys, + function ($key) { + return $key['COLUMN_NAME'] == $this->entityColumn; + } + ); + foreach ($foreignKeys as $foreignKeyInfo) { + if (!count($this->getForeignKeys( + $this->externalTable, + $this->externalColumn, + $this->setup->getTablePlaceholder($foreignKeyInfo['REF_TABLE_NAME']), + $foreignKeyInfo['REF_COLUMN_NAME'] + ))) { + $this->setup->getConnection()->addForeignKey( + $this->setup->getFkName( + $this->externalTable, + $this->externalColumn, + $this->setup->getTablePlaceholder($foreignKeyInfo['REF_TABLE_NAME']), + $foreignKeyInfo['REF_COLUMN_NAME'] + ), + $this->setup->getTable($this->externalTable), + $this->externalColumn, + $foreignKeyInfo['REF_TABLE_NAME'], + $foreignKeyInfo['REF_COLUMN_NAME'] + ); + } + } + } + + /** + * Drop old foreign key + * + * @return void + */ + protected function dropOldForeignKey() + { + if (count($this->getForeignKeys( + $this->externalTable, + $this->externalColumn, + $this->entityTable, + $this->entityColumn + ))) { + $this->clearForeignKey( + $this->externalTable, + $this->externalColumn, + $this->entityTable, + $this->entityColumn + ); + } + } +} diff --git a/lib/internal/Magento/Framework/Setup/SetupInterface.php b/lib/internal/Magento/Framework/Setup/SetupInterface.php index 1274112345d42..7bd097812ec4c 100644 --- a/lib/internal/Magento/Framework/Setup/SetupInterface.php +++ b/lib/internal/Magento/Framework/Setup/SetupInterface.php @@ -34,6 +34,13 @@ public function setTable($tableName, $realTableName); */ public function getTable($tableName); + /** + * Gets table placeholder by table name + * + * @param string $tableName + * @return string + */ + public function getTablePlaceholder($tableName); /** * Checks if table exists diff --git a/lib/internal/Magento/Framework/View/Asset/File.php b/lib/internal/Magento/Framework/View/Asset/File.php index 19a8026f4668d..02eb9aa728610 100644 --- a/lib/internal/Magento/Framework/View/Asset/File.php +++ b/lib/internal/Magento/Framework/View/Asset/File.php @@ -115,7 +115,7 @@ public function getPath() public function getRelativeSourceFilePath() { $path = $this->filePath; - $sourcePath = $this->source->findRelativeSourceFilePath($this); + $sourcePath = $this->source->findSource($this); if ($sourcePath) { $origExt = pathinfo($path, PATHINFO_EXTENSION); $ext = pathinfo($sourcePath, PATHINFO_EXTENSION); diff --git a/lib/internal/Magento/Framework/View/Asset/Source.php b/lib/internal/Magento/Framework/View/Asset/Source.php index d47a76a4fe58c..b6af6e7d897d3 100644 --- a/lib/internal/Magento/Framework/View/Asset/Source.php +++ b/lib/internal/Magento/Framework/View/Asset/Source.php @@ -7,6 +7,7 @@ namespace Magento\Framework\View\Asset; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\View\Asset\PreProcessor\ChainFactoryInterface; use Magento\Framework\View\Design\FileResolution\Fallback\Resolver\Simple; @@ -53,7 +54,15 @@ class Source private $chainFactory; /** + * @var ReadFactory + */ + private $readFactory; + + /** + * Constructor + * * @param \Magento\Framework\Filesystem $filesystem + * @param ReadFactory $readFactory * @param PreProcessor\Pool $preProcessorPool * @param \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile $fallback * @param \Magento\Framework\View\Design\Theme\ListInterface $themeList @@ -61,12 +70,14 @@ class Source */ public function __construct( \Magento\Framework\Filesystem $filesystem, + ReadFactory $readFactory, PreProcessor\Pool $preProcessorPool, \Magento\Framework\View\Design\FileResolution\Fallback\StaticFile $fallback, \Magento\Framework\View\Design\Theme\ListInterface $themeList, ChainFactoryInterface $chainFactory ) { $this->filesystem = $filesystem; + $this->readFactory = $readFactory; $this->rootDir = $filesystem->getDirectoryRead(DirectoryList::ROOT); $this->varDir = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); $this->preProcessorPool = $preProcessorPool; @@ -87,8 +98,8 @@ public function getFile(LocalInterface $asset) if (!$result) { return false; } - list($dirCode, $path) = $result; - return $this->filesystem->getDirectoryRead($dirCode)->getAbsolutePath($path); + list($dir, $path) = $result; + return $this->readFactory->create($dir)->getAbsolutePath($path); } /** @@ -103,15 +114,15 @@ public function getContent(LocalInterface $asset) if (!$result) { return false; } - list($dirCode, $path) = $result; - return $this->filesystem->getDirectoryRead($dirCode)->readFile($path); + list($dir, $path) = $result; + return $this->readFactory->create($dir)->readFile($path); } /** * Perform necessary preprocessing and materialization when the specified asset is requested * * Returns an array of two elements: - * - directory code where the file is supposed to be found + * - directory where the file is supposed to be found * - relative path to the file * * returns false if source file was not found @@ -122,18 +133,22 @@ public function getContent(LocalInterface $asset) private function preProcess(LocalInterface $asset) { $sourceFile = $this->findSourceFile($asset); - $path = $this->rootDir->getRelativePath($sourceFile); + $dir = $this->rootDir->getAbsolutePath(); + $path = ''; + if ($sourceFile) { + $path = basename($sourceFile); + $dir = dirname($sourceFile); + } - $chain = $this->createChain($asset, $path); + $chain = $this->createChain($asset, $dir, $path); $this->preProcessorPool->process($chain); $chain->assertValid(); - $dirCode = DirectoryList::ROOT; if ($chain->isChanged()) { - $dirCode = DirectoryList::VAR_DIR; + $dir = $this->varDir->getAbsolutePath(); $path = DirectoryList::TMP_MATERIALIZATION_DIR . '/source/' . $chain->getTargetAssetPath(); $this->varDir->writeFile($path, $chain->getContent()); } - $result = [$dirCode, $path]; + $result = [$dir, $path]; return $result; } @@ -218,6 +233,7 @@ private function findFile(LocalInterface $asset, \Magento\Framework\View\Asset\F * @param \Magento\Framework\View\Asset\LocalInterface $asset * * @return bool|string + * @deprecated If custom vendor directory is outside Magento root, then this method will return unexpected result */ public function findRelativeSourceFilePath(LocalInterface $asset) { @@ -232,13 +248,14 @@ public function findRelativeSourceFilePath(LocalInterface $asset) * Creates a chain for pre-processing * * @param LocalInterface $asset + * @param string|bool $dir * @param string|bool $path * @return PreProcessor\Chain */ - private function createChain(LocalInterface $asset, $path) + private function createChain(LocalInterface $asset, $dir, $path) { if ($path) { - $origContent = $this->rootDir->readFile($path); + $origContent = $this->readFactory->create($dir)->readFile($path); $origContentType = $this->getContentType($path); } else { $origContent = ''; @@ -250,7 +267,7 @@ private function createChain(LocalInterface $asset, $path) 'asset' => $asset, 'origContent' => $origContent, 'origContentType' => $origContentType, - 'origAssetPath' => $path + 'origAssetPath' => $dir . '/' . $path ] ); return $chain; diff --git a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Alternative.php b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Alternative.php index c8872027ad2af..ab4228a24e175 100644 --- a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Alternative.php +++ b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Alternative.php @@ -6,7 +6,7 @@ namespace Magento\Framework\View\Design\FileResolution\Fallback\Resolver; -use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\View\Design\Fallback\Rule\RuleInterface; use Magento\Framework\View\Design\FileResolution\Fallback; @@ -23,12 +23,12 @@ class Alternative extends Simple /** * Constructor * - * @param Filesystem $filesystem + * @param ReadFactory $readFactory * @param \Magento\Framework\View\Design\Fallback\RulePool $rulePool * @param array $alternativeExtensions */ public function __construct( - Filesystem $filesystem, + ReadFactory $readFactory, \Magento\Framework\View\Design\Fallback\RulePool $rulePool, array $alternativeExtensions = [] ) { @@ -41,7 +41,7 @@ public function __construct( } } $this->alternativeExtensions = $alternativeExtensions; - parent::__construct($filesystem, $rulePool); + parent::__construct($readFactory, $rulePool); } /** diff --git a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Simple.php b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Simple.php index 3524480c27099..ec7c73eaa0bce 100644 --- a/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Simple.php +++ b/lib/internal/Magento/Framework/View/Design/FileResolution/Fallback/Resolver/Simple.php @@ -6,9 +6,7 @@ namespace Magento\Framework\View\Design\FileResolution\Fallback\Resolver; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem; -use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\View\Design\Fallback\Rule\RuleInterface; use Magento\Framework\View\Design\Fallback\RulePool; use Magento\Framework\View\Design\FileResolution\Fallback; @@ -20,11 +18,11 @@ class Simple implements Fallback\ResolverInterface { /** - * Root directory + * Directory read factory * - * @var ReadInterface + * @var ReadFactory */ - protected $rootDirectory; + protected $readFactory; /** * Fallback factory @@ -36,12 +34,12 @@ class Simple implements Fallback\ResolverInterface /** * Constructor * - * @param Filesystem $filesystem + * @param ReadFactory $readFactory * @param RulePool $rulePool */ - public function __construct(Filesystem $filesystem, RulePool $rulePool) + public function __construct(ReadFactory $readFactory, RulePool $rulePool) { - $this->rootDirectory = $filesystem->getDirectoryRead(DirectoryList::ROOT); + $this->readFactory = $readFactory; $this->rulePool = $rulePool; } @@ -92,7 +90,8 @@ protected function resolveFile(RuleInterface $fallbackRule, $file, array $params { foreach ($fallbackRule->getPatternDirs($params) as $dir) { $path = "{$dir}/{$file}"; - if ($this->rootDirectory->isExist($this->rootDirectory->getRelativePath($path))) { + $dirRead = $this->readFactory->create($dir); + if ($dirRead->isExist($file)) { return $path; } } diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php index 880b6bc6e1ce7..f05a064a173b5 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Config/FileCollector/AggregatedFileCollector.php @@ -5,9 +5,8 @@ */ namespace Magento\Framework\View\Element\UiComponent\Config\FileCollector; -use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadFactory; use Magento\Framework\View\DesignInterface; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\View\File\CollectorInterface; use Magento\Framework\View\Element\UiComponent\Config\FileCollectorInterface; @@ -34,28 +33,28 @@ class AggregatedFileCollector implements FileCollectorInterface protected $design; /** - * @var Filesystem + * @var ReadFactory */ - protected $filesystem; + protected $readFactory; /** * Constructor * * @param CollectorInterface $collectorAggregated * @param DesignInterface $design - * @param Filesystem $filesystem + * @param ReadFactory $readFactory * @param string $searchPattern */ public function __construct( CollectorInterface $collectorAggregated, DesignInterface $design, - Filesystem $filesystem, + ReadFactory $readFactory, $searchPattern = null ) { $this->searchPattern = $searchPattern; $this->collectorAggregated = $collectorAggregated; $this->design = $design; - $this->filesystem = $filesystem; + $this->readFactory = $readFactory; } /** @@ -75,10 +74,12 @@ public function collectFiles($searchPattern = null) throw new \Exception('Search pattern cannot be empty.'); } $files = $this->collectorAggregated->getFiles($this->design->getDesignTheme(), $searchPattern); - $fileReader = $this->filesystem->getDirectoryRead(DirectoryList::ROOT); foreach ($files as $file) { - $filePath = $fileReader->getRelativePath($file->getFilename()); - $result[sprintf('%x', crc32($filePath))] = $fileReader->readFile($filePath); + $fullFileName = $file->getFileName(); + $fileDir = dirname($fullFileName); + $fileName = basename($fullFileName); + $dirRead = $this->readFactory->create($fileDir); + $result[$fullFileName] = $dirRead->readFile($fileName); } return $result; diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php index 427ed2bc15578..cec61e94cb8bd 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Asset/SourceTest.php @@ -69,6 +69,11 @@ class SourceTest extends \PHPUnit_Framework_TestCase */ private $chain; + /** + * @var \Magento\Framework\Filesystem\Directory\ReadFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $readFactory; + protected function setUp() { $this->preProcessorPool = $this->getMock( @@ -96,10 +101,17 @@ protected function setUp() ->with('frontend/magento_theme') ->willReturn($this->theme); + $this->readFactory = $this->getMock('Magento\Framework\Filesystem\Directory\ReadFactory', [], [], '', false); + $this->initFilesystem(); $this->object = new Source( - $this->filesystem, $this->preProcessorPool, $this->viewFileResolution, $themeList, $this->chainFactory + $this->filesystem, + $this->readFactory, + $this->preProcessorPool, + $this->viewFileResolution, + $themeList, + $this->chainFactory ); } @@ -115,18 +127,13 @@ protected function setUp() public function testGetFile($origFile, $origPath, $origContent, $isMaterialization, $isExist) { $filePath = 'some/file.ext'; + $read = $this->getMock('Magento\Framework\Filesystem\Directory\Read', [], [], '', false); + $read->expects($this->at(0))->method('readFile')->with($origPath)->willReturn($origContent); + $this->readFactory->expects($this->atLeastOnce())->method('create')->willReturn($read); $this->viewFileResolution->expects($this->once()) ->method('getFile') ->with('frontend', $this->theme, 'en_US', $filePath, 'Magento_Module') ->willReturn($origFile); - $this->rootDirRead->expects($this->once()) - ->method('getRelativePath') - ->with($origFile) - ->willReturn($origPath); - $this->rootDirRead->expects($this->once()) - ->method('readFile') - ->with($origPath) - ->willReturn($origContent); $this->preProcessorPool->expects($this->once()) ->method('process') ->with($this->chain); @@ -151,12 +158,16 @@ public function testGetFile($origFile, $origPath, $origContent, $isMaterializati ->with('view_preprocessed/source/some/file.ext', 'processed'); $this->varDir->expects($this->once()) ->method('getAbsolutePath') - ->with('view_preprocessed/source/some/file.ext')->willReturn('result'); + ->willReturn('var'); + $read->expects($this->once()) + ->method('getAbsolutePath') + ->with('view_preprocessed/source/some/file.ext') + ->willReturn('result'); } else { $this->varDir->expects($this->never())->method('writeFile'); - $this->rootDirRead->expects($this->once()) + $read->expects($this->at(1)) ->method('getAbsolutePath') - ->with('source/some/file.ext') + ->with('file.ext') ->willReturn('result'); } $this->assertSame('result', $this->object->getFile($this->getAsset())); @@ -201,10 +212,10 @@ public function chainTestCallback(Chain $chain) public function getFileDataProvider() { return [ - ['/root/some/file.ext', 'source/some/file.ext', 'processed', false, true], - ['/root/some/file.ext', 'source/some/file.ext', 'not_processed', true, false], - ['/root/some/file.ext2', 'source/some/file.ext2', 'processed', true, true], - ['/root/some/file.ext2', 'source/some/file.ext2', 'not_processed', true, false], + ['/root/some/file.ext', 'file.ext', 'processed', false, true], + ['/root/some/file.ext', 'file.ext', 'not_processed', true, false], + ['/root/some/file.ext2', 'file.ext2', 'processed', true, true], + ['/root/some/file.ext2', 'file.ext2', 'not_processed', true, false], ]; } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/AlternativeTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/AlternativeTest.php index 2dbaa2e6c6066..891c03f4f4fbd 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/AlternativeTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/AlternativeTest.php @@ -32,13 +32,9 @@ class AlternativeTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->directory = $this->getMock('\Magento\Framework\Filesystem\Directory\Read', [], [], '', false); - $this->directory->expects($this->any()) - ->method('getRelativePath') - ->will($this->returnArgument(0)); - $filesystem = $this->getMock('\Magento\Framework\Filesystem', [], [], '', false); - $filesystem->expects($this->once()) - ->method('getDirectoryRead') - ->with(DirectoryList::ROOT) + $readFactory = $this->getMock('\Magento\Framework\Filesystem\Directory\ReadFactory', [], [], '', false); + $readFactory->expects($this->any()) + ->method('create') ->will($this->returnValue($this->directory)); $this->rule = $this->getMock( '\Magento\Framework\View\Design\Fallback\Rule\RuleInterface', [], [], '', false @@ -48,7 +44,7 @@ protected function setUp() ->method('getRule') ->with('type') ->will($this->returnValue($this->rule)); - $this->object = new Alternative($filesystem, $rulePool, ['css' => ['less']]); + $this->object = new Alternative($readFactory, $rulePool, ['css' => ['less']]); } /** @@ -61,9 +57,9 @@ public function testConstructorException(array $alternativeExtensions) $this->setExpectedException('\InvalidArgumentException', "\$alternativeExtensions must be an array with format:" . " array('ext1' => array('ext1', 'ext2'), 'ext3' => array(...)]"); - $filesystem = $this->getMock('Magento\Framework\Filesystem', [], [], '', false); + $readFactory = $this->getMock('Magento\Framework\Filesystem\Directory\ReadFactory', [], [], '', false); $rulePool = $this->getMock('Magento\Framework\View\Design\Fallback\RulePool', [], [], '', false); - new Alternative($filesystem, $rulePool, $alternativeExtensions); + new Alternative($readFactory, $rulePool, $alternativeExtensions); } /** @@ -91,8 +87,8 @@ public function testResolve() ->will($this->returnValue(['some/dir'])); $fileExistsMap = [ - ['some/dir/file.css', false], - ['some/dir/file.less', true], + ['file.css', false], + ['file.less', true], ]; $this->directory->expects($this->any()) ->method('isExist') diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/SimpleTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/SimpleTest.php index 14411757fc878..6ae8f5f71f593 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/SimpleTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Design/FileResolution/Fallback/Resolver/SimpleTest.php @@ -30,13 +30,9 @@ class SimpleTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->directory = $this->getMock('\Magento\Framework\Filesystem\Directory\Read', [], [], '', false); - $this->directory->expects($this->any()) - ->method('getRelativePath') - ->will($this->returnArgument(0)); - $filesystem = $this->getMock('\Magento\Framework\Filesystem', [], [], '', false); - $filesystem->expects($this->once()) - ->method('getDirectoryRead') - ->with(DirectoryList::ROOT) + $readFactory = $this->getMock('\Magento\Framework\Filesystem\Directory\ReadFactory', [], [], '', false); + $readFactory->expects($this->any()) + ->method('create') ->will($this->returnValue($this->directory)); $this->rule = $this->getMock('\Magento\Framework\View\Design\Fallback\Rule\RuleInterface', [], [], '', false); $rulePool = $this->getMock('Magento\Framework\View\Design\Fallback\RulePool', [], [], '', false); @@ -45,7 +41,7 @@ protected function setUp() ->with('type') ->will($this->returnValue($this->rule)); - $this->object = new Simple($filesystem, $rulePool); + $this->object = new Simple($readFactory, $rulePool); } /** @@ -65,15 +61,13 @@ public function testResolve($area, $themePath, $locale, $module, array $expected $expectedParams['theme'] = $this->getMockForTheme($expectedParams['theme']); } - $this->directory->expects($this->never()) - ->method('getAbsolutePath'); $this->rule->expects($this->once()) ->method('getPatternDirs') ->with($expectedParams) ->will($this->returnValue(['/some/dir'])); $this->directory->expects($this->once()) ->method('isExist') - ->with($expectedPath) + ->with('file.ext') ->will($this->returnValue(true)); $actualPath = $this->object->resolve('type', 'file.ext', $area, $theme, $locale, $module); $this->assertSame($expectedPath, $actualPath); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Js/CookieTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Js/CookieTest.php index adecba17d959e..00d864460f810 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Js/CookieTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Js/CookieTest.php @@ -13,17 +13,17 @@ class CookieTest extends \PHPUnit_Framework_TestCase protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\View\Element\Template\Context */ protected $contextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Session\Config\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $sessionConfigMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Validator\Ip|\PHPUnit_Framework_MockObject_MockObject */ protected $ipValidatorMock; diff --git a/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php b/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php index afb277dab8062..674e8c33e5549 100644 --- a/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php +++ b/setup/src/Magento/Setup/Console/Command/I18nCollectPhrasesCommand.php @@ -35,7 +35,11 @@ protected function configure() $this->setName('i18n:collect-phrases') ->setDescription('Discovers phrases in the codebase'); $this->setDefinition([ - new InputArgument(self::INPUT_KEY_DIRECTORY, InputArgument::REQUIRED, 'Directory path to parse'), + new InputArgument( + self::INPUT_KEY_DIRECTORY, + InputArgument::OPTIONAL, + 'Directory path to parse. Not needed if --magento flag is set' + ), new InputOption( self::INPUT_KEY_OUTPUT, self::SHORTCUT_KEY_OUTPUT, @@ -46,8 +50,8 @@ protected function configure() self::INPUT_KEY_MAGENTO, self::SHORTCUT_KEY_MAGENTO, InputOption::VALUE_NONE, - 'Use the --magento parameter to specify the directory is the Magento root directory.' . - ' Omit the parameter if the directory is not the Magento root directory.' + 'Use the --magento parameter to parse the current Magento codebase.' . + ' Omit the parameter if a directory is specified.' ), ]); } @@ -57,9 +61,18 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $directory = $input->getArgument(self::INPUT_KEY_DIRECTORY); + if ($input->getOption(self::INPUT_KEY_MAGENTO)) { + $directory = BP; + if ($input->getArgument(self::INPUT_KEY_DIRECTORY)) { + throw new \InvalidArgumentException('Directory path is not needed when --magento flag is set.'); + } + } elseif (!$input->getArgument(self::INPUT_KEY_DIRECTORY)) { + throw new \InvalidArgumentException('Directory path is needed when --magento flag is not set.'); + } $generator = ServiceLocator::getDictionaryGenerator(); $generator->generate( - $input->getArgument(self::INPUT_KEY_DIRECTORY), + $directory, $input->getOption(self::INPUT_KEY_OUTPUT), $input->getOption(self::INPUT_KEY_MAGENTO) ); diff --git a/setup/src/Magento/Setup/Console/Command/I18nPackCommand.php b/setup/src/Magento/Setup/Console/Command/I18nPackCommand.php index 82ff951a89e0d..bb6ce5d2c8afe 100644 --- a/setup/src/Magento/Setup/Console/Command/I18nPackCommand.php +++ b/setup/src/Magento/Setup/Console/Command/I18nPackCommand.php @@ -21,7 +21,6 @@ class I18nPackCommand extends Command * Keys and shortcuts for input arguments and options */ const INPUT_KEY_SOURCE = 'source'; - const INPUT_KEY_PACK = 'pack'; const INPUT_KEY_LOCALE = 'locale'; const INPUT_KEY_MODE = 'mode'; const INPUT_KEY_ALLOW_DUPLICATES = 'allow-duplicates'; @@ -50,11 +49,6 @@ protected function configure() InputArgument::REQUIRED, 'Path to source dictionary file with translations' ), - new InputArgument( - self::INPUT_KEY_PACK, - InputArgument::REQUIRED, - 'Path to language package' - ), new InputArgument( self::INPUT_KEY_LOCALE, InputArgument::REQUIRED, @@ -92,7 +86,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $locale = $input->getArgument(self::INPUT_KEY_LOCALE); $generator->generate( $input->getArgument(self::INPUT_KEY_SOURCE), - $input->getArgument(self::INPUT_KEY_PACK), $locale, $input->getOption(self::INPUT_KEY_MODE), $input->getOption(self::INPUT_KEY_ALLOW_DUPLICATES) diff --git a/setup/src/Magento/Setup/Fixtures/CatalogPriceRulesFixture.php b/setup/src/Magento/Setup/Fixtures/CatalogPriceRulesFixture.php index 3fd0e674ec200..29134e5a06e1a 100644 --- a/setup/src/Magento/Setup/Fixtures/CatalogPriceRulesFixture.php +++ b/setup/src/Magento/Setup/Fixtures/CatalogPriceRulesFixture.php @@ -34,6 +34,10 @@ public function execute() $category = $this->fixtureModel->getObjectManager()->get('Magento\Catalog\Model\Category'); /** @var $model \Magento\CatalogRule\Model\Rule*/ $model = $this->fixtureModel->getObjectManager()->get('Magento\CatalogRule\Model\Rule'); + /** @var \Magento\Framework\Model\Entity\MetadataPool $metadataPool */ + $metadataPool = $this->fixtureModel->getObjectManager()->get('Magento\Framework\Model\Entity\MetadataPool'); + $metadata = $metadataPool->getMetadata('Magento\CatalogRule\Api\Data\RuleInterface'); + //Get all websites $categoriesArray = []; $websites = $storeManager->getWebsites(); @@ -57,12 +61,14 @@ public function execute() } asort($categoriesArray); $categoriesArray = array_values($categoriesArray); - $idField = $model->getIdFieldName(); + $linkField = $metadata->getLinkField(); + $idField = $metadata->getIdentifierField(); for ($i = 0; $i < $catalogPriceRulesCount; $i++) { $ruleName = sprintf('Catalog Price Rule %1$d', $i); $data = [ $idField => null, + $linkField => null, 'name' => $ruleName, 'description' => '', 'is_active' => '1', diff --git a/setup/src/Magento/Setup/Model/ConfigGenerator.php b/setup/src/Magento/Setup/Model/ConfigGenerator.php index 812cbd1830a38..a7007998445fe 100644 --- a/setup/src/Magento/Setup/Model/ConfigGenerator.php +++ b/setup/src/Magento/Setup/Model/ConfigGenerator.php @@ -235,7 +235,9 @@ public function createXFrameConfig() public function createModeConfig() { $configData = new ConfigData(ConfigFilePool::APP_ENV); - $configData->set(State::PARAM_MODE, State::MODE_DEFAULT); + if ($this->deploymentConfig->get(State::PARAM_MODE) === null) { + $configData->set(State::PARAM_MODE, State::MODE_DEFAULT); + } return $configData; } diff --git a/setup/src/Magento/Setup/Module/I18n/Context.php b/setup/src/Magento/Setup/Module/I18n/Context.php index 4944390f175c3..fac38bf96d7c1 100644 --- a/setup/src/Magento/Setup/Module/I18n/Context.php +++ b/setup/src/Magento/Setup/Module/I18n/Context.php @@ -100,15 +100,13 @@ public function buildPathToLocaleDirectoryByContext($type, $value) { switch ($type) { case self::CONTEXT_TYPE_MODULE: - $absolutePath = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $value); - $path = str_replace(BP . '/', '', $absolutePath); + $path = $this->componentRegistrar->getPath(ComponentRegistrar::MODULE, $value); break; case self::CONTEXT_TYPE_THEME: - $absolutePath = $this->componentRegistrar->getPath(ComponentRegistrar::THEME, $value); - $path = str_replace(BP . '/', '', $absolutePath); + $path = $this->componentRegistrar->getPath(ComponentRegistrar::THEME, $value); break; case self::CONTEXT_TYPE_LIB: - $path = 'lib/web'; + $path = BP . '/lib/web'; break; default: throw new \InvalidArgumentException(sprintf('Invalid context given: "%s".', $type)); diff --git a/setup/src/Magento/Setup/Module/I18n/Dictionary/Options/Resolver.php b/setup/src/Magento/Setup/Module/I18n/Dictionary/Options/Resolver.php index 07a469246bd46..967054587a9e7 100644 --- a/setup/src/Magento/Setup/Module/I18n/Dictionary/Options/Resolver.php +++ b/setup/src/Magento/Setup/Module/I18n/Dictionary/Options/Resolver.php @@ -134,9 +134,7 @@ private function getComponentDirectories($componentType) { $dirs = []; foreach ($this->componentRegistrar->getPaths($componentType) as $componentDir) { - if (strstr($componentDir, $this->directory)) { - $dirs[] = $componentDir . '/'; - } + $dirs[] = $componentDir . '/'; } return $dirs; } diff --git a/setup/src/Magento/Setup/Module/I18n/Pack/Generator.php b/setup/src/Magento/Setup/Module/I18n/Pack/Generator.php index bbc0cecfa9ce6..fcf35932c412f 100644 --- a/setup/src/Magento/Setup/Module/I18n/Pack/Generator.php +++ b/setup/src/Magento/Setup/Module/I18n/Pack/Generator.php @@ -56,7 +56,6 @@ public function __construct( * Generate language pack * * @param string $dictionaryPath - * @param string $packPath * @param string $locale * @param string $mode One of const of WriterInterface::MODE_ * @param bool $allowDuplicates @@ -65,7 +64,6 @@ public function __construct( */ public function generate( $dictionaryPath, - $packPath, $locale, $mode = WriterInterface::MODE_REPLACE, $allowDuplicates = false @@ -84,7 +82,7 @@ public function generate( ); } - $this->packWriter->write($dictionary, $packPath, $locale, $mode); + $this->packWriter->writeDictionary($dictionary, $locale, $mode); } /** diff --git a/setup/src/Magento/Setup/Module/I18n/Pack/Writer/File/AbstractFile.php b/setup/src/Magento/Setup/Module/I18n/Pack/Writer/File/AbstractFile.php index e5909fb68296f..50757c7c80159 100644 --- a/setup/src/Magento/Setup/Module/I18n/Pack/Writer/File/AbstractFile.php +++ b/setup/src/Magento/Setup/Module/I18n/Pack/Writer/File/AbstractFile.php @@ -37,13 +37,6 @@ abstract class AbstractFile implements WriterInterface */ protected $_factory; - /** - * Pack path - * - * @var string - */ - protected $_packPath; - /** * Locale * @@ -77,7 +70,14 @@ public function __construct(Context $context, Dictionary\Loader\FileInterface $d */ public function write(Dictionary $dictionary, $packPath, Locale $locale, $mode = self::MODE_REPLACE) { - $this->_packPath = rtrim($packPath, '\\/') . '/'; + $this->writeDictionary($dictionary, $locale, $mode); + } + + /** + * {@inheritdoc} + */ + public function writeDictionary(Dictionary $dictionary, Locale $locale, $mode = self::MODE_REPLACE) + { $this->_locale = $locale; $this->_mode = $mode; @@ -121,7 +121,7 @@ protected function _buildPackFilesData(Dictionary $dictionary) } catch (\InvalidArgumentException $e) { throw new \InvalidArgumentException($e->getMessage() . ' Row #' . ($key + 1) . '.'); } - $filename = $this->_packPath . $path . $this->_locale . '.' . $this->_getFileExtension(); + $filename = $path . $this->_locale . '.' . $this->_getFileExtension(); $files[$filename][$phrase->getPhrase()] = $phrase; } } diff --git a/setup/src/Magento/Setup/Module/I18n/Pack/WriterInterface.php b/setup/src/Magento/Setup/Module/I18n/Pack/WriterInterface.php index 19159add87aec..edb898ac73270 100644 --- a/setup/src/Magento/Setup/Module/I18n/Pack/WriterInterface.php +++ b/setup/src/Magento/Setup/Module/I18n/Pack/WriterInterface.php @@ -30,6 +30,18 @@ interface WriterInterface * @param \Magento\Setup\Module\I18n\Locale $locale * @param string $mode One of const of WriterInterface::MODE_ * @return void + * @deprecated Writing to a specified pack path is not supported after custom vendor directory support. + * Dictionary data will be written to current Magento codebase. */ public function write(Dictionary $dictionary, $packPath, Locale $locale, $mode); + + /** + * Write dictionary data to current Magento codebase + * + * @param \Magento\Setup\Module\I18n\Dictionary $dictionary + * @param \Magento\Setup\Module\I18n\Locale $locale + * @param string $mode One of const of WriterInterface::MODE_ + * @return void + */ + public function writeDictionary(Dictionary $dictionary, Locale $locale, $mode); } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/CatalogPriceRulesFixtureTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/CatalogPriceRulesFixtureTest.php index 2e6625e107abb..71241d9d58004 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/CatalogPriceRulesFixtureTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/CatalogPriceRulesFixtureTest.php @@ -73,20 +73,27 @@ public function testExecute() ->will($this->returnValue('category_id')); $modelMock = $this->getMock('Magento\CatalogRule\Model\Rule', [], [], '', false); - $modelMock->expects($this->once()) - ->method('getIdFieldName') + $metadataMock = $this->getMock('\Magento\Framework\Model\Entity\EntityMetadata', [], [], '', false); + $metadataPoolMock = $this->getMock('Magento\Framework\Model\Entity\MetadataPool', [], [], '', false); + $metadataMock->expects($this->once()) + ->method('getLinkField') ->will($this->returnValue('Field Id Name')); $valueMap = [ ['Magento\CatalogRule\Model\Rule', $modelMock], - ['Magento\Catalog\Model\Category', $categoryMock] + ['Magento\Catalog\Model\Category', $categoryMock], + ['Magento\Framework\Model\Entity\MetadataPool', $metadataPoolMock] ]; - + $metadataPoolMock + ->expects($this->once()) + ->method('getMetadata') + ->with('Magento\CatalogRule\Api\Data\RuleInterface') + ->willReturn($metadataMock); $objectManagerMock = $this->getMock('Magento\Framework\ObjectManager\ObjectManager', [], [], '', false); $objectManagerMock->expects($this->once()) ->method('create') ->will($this->returnValue($storeManagerMock)); - $objectManagerMock->expects($this->exactly(2)) + $objectManagerMock->expects($this->exactly(3)) ->method('get') ->will($this->returnValueMap($valueMap)); @@ -95,7 +102,7 @@ public function testExecute() ->method('getValue') ->will($this->returnValue(1)); $this->fixtureModelMock - ->expects($this->exactly(3)) + ->expects($this->exactly(4)) ->method('getObjectManager') ->will($this->returnValue($objectManagerMock)); diff --git a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php index 65df905990221..0e892b753c395 100644 --- a/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Model/ConfigGeneratorTest.php @@ -3,16 +3,16 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Setup\Test\Unit\Model; - use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\App\State; class ConfigGeneratorTest extends \PHPUnit_Framework_TestCase { /** @var \Magento\Framework\App\DeploymentConfig | \PHPUnit_Framework_MockObject_MockObject */ private $deploymentConfigMock; + /** @var \Magento\Setup\Model\ConfigGenerator | \PHPUnit_Framework_MockObject_MockObject */ private $model; @@ -58,4 +58,24 @@ public function testCreateCacheHostsConfig() $configData = $this->model->createCacheHostsConfig($data); $this->assertEquals($expectedData, $configData->getData()[ConfigOptionsListConstants::CONFIG_PATH_CACHE_HOSTS]); } + + public function testCreateModeConfig() + { + $this->deploymentConfigMock->expects($this->once()) + ->method('get') + ->with(State::PARAM_MODE) + ->willReturn(null); + $configData = $this->model->createModeConfig(); + $this->assertSame(State::MODE_DEFAULT, $configData->getData()[State::PARAM_MODE]); + } + + public function testCreateModeConfigIfAlreadySet() + { + $this->deploymentConfigMock->expects($this->once()) + ->method('get') + ->with(State::PARAM_MODE) + ->willReturn(State::MODE_PRODUCTION); + $configData = $this->model->createModeConfig(); + $this->assertSame([], $configData->getData()); + } } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php index 4972001695db1..89dfe454de3ef 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/ContextTest.php @@ -117,12 +117,12 @@ public function dataProviderPathToLocaleDirectoryByContext() { return [ [ - 'app/code/Magento/Module/i18n/', + BP . '/app/code/Magento/Module/i18n/', [Context::CONTEXT_TYPE_MODULE, 'Magento_Module'], [[ComponentRegistrar::MODULE, 'Magento_Module', BP . '/app/code/Magento/Module']] ], ['/i18n/', [Context::CONTEXT_TYPE_THEME, 'theme/test.phtml'], []], - ['lib/web/i18n/', [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], []], + [BP . '/lib/web/i18n/', [Context::CONTEXT_TYPE_LIB, 'lib/web/module/test.phtml'], []], ]; } diff --git a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php index 0866b50cf75aa..36600df2570f4 100644 --- a/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Module/I18n/Pack/GeneratorTest.php @@ -56,7 +56,6 @@ protected function setUp() public function testGenerate() { $dictionaryPath = 'dictionary_path'; - $packPath = 'pack_path'; $localeString = 'locale'; $mode = 'mode'; $allowDuplicates = true; @@ -76,10 +75,10 @@ public function testGenerate() ->with($dictionaryPath) ->will($this->returnValue($this->dictionaryMock)); $this->packWriterMock->expects($this->once()) - ->method('write') - ->with($this->dictionaryMock, $packPath, $localeMock, $mode); + ->method('writeDictionary') + ->with($this->dictionaryMock, $localeMock, $mode); - $this->_generator->generate($dictionaryPath, $packPath, $localeString, $mode, $allowDuplicates); + $this->_generator->generate($dictionaryPath, $localeString, $mode, $allowDuplicates); } /** @@ -89,7 +88,6 @@ public function testGenerate() public function testGenerateEmptyFile() { $dictionaryPath = 'dictionary_path'; - $packPath = 'pack_path'; $localeString = 'locale'; $mode = 'mode'; $allowDuplicates = true; @@ -104,7 +102,7 @@ public function testGenerateEmptyFile() ->with($dictionaryPath) ->will($this->returnValue($this->dictionaryMock)); - $this->_generator->generate($dictionaryPath, $packPath, $localeString, $mode, $allowDuplicates); + $this->_generator->generate($dictionaryPath, $localeString, $mode, $allowDuplicates); } public function testGenerateWithNotAllowedDuplicatesAndDuplicatesExist() @@ -132,6 +130,6 @@ public function testGenerateWithNotAllowedDuplicatesAndDuplicatesExist() ->method('getDuplicates') ->will($this->returnValue([[$phraseFirstMock], [$phraseSecondMock]])); - $this->_generator->generate('dictionary_path', 'pack_path', 'locale', 'mode', $allowDuplicates); + $this->_generator->generate('dictionary_path', 'locale', 'mode', $allowDuplicates); } }