From a7f87cedecb81ea4901266aadd669fd8f3bea347 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" Date: Wed, 2 Oct 2019 17:11:19 +0300 Subject: [PATCH 01/15] MC-18457: Free Shipping Minimum Order Amount Excluding/Including Tax options --- .../Model/Carrier/Freeshipping.php | 23 +- ...eeShippingDisplayWithInclTaxOptionTest.xml | 68 ++++++ .../Unit/Model/Carrier/FreeshippingTest.php | 200 ++++++++++++++++++ .../OfflineShipping/etc/adminhtml/system.xml | 4 + .../Magento/Quote/Model/Quote/Address.php | 21 +- .../Test/Mftf/Data/FreeShippingMethodData.xml | 15 ++ .../Mftf/Metadata/shipping_methods-meta.xml | 3 + .../Sales/Total/Quote/CommonTaxCollector.php | 4 + 8 files changed, 324 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml create mode 100644 app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php index 674e6b8089787..a1fca2b155f11 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php @@ -63,6 +63,24 @@ public function __construct( parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } + /** + * Check subtotal for allowed free shipping + * + * @param RateRequest $request + * + * @return bool + */ + private function isFreeShippingRequired(RateRequest $request): bool + { + $minSubtotal = $request->getPackageValueWithDiscount(); + if ($request->getBaseSubtotalWithDiscountInclTax() + && $this->getConfigFlag('tax_including')) { + $minSubtotal = $request->getBaseSubtotalWithDiscountInclTax(); + } + + return $minSubtotal >= $this->getConfigData('free_shipping_subtotal'); + } + /** * FreeShipping Rates Collector * @@ -80,10 +98,7 @@ public function collectRates(RateRequest $request) $this->_updateFreeMethodQuote($request); - if ($request->getFreeShipping() || $request->getPackageValueWithDiscount() >= $this->getConfigData( - 'free_shipping_subtotal' - ) - ) { + if ($request->getFreeShipping() || $this->isFreeShippingRequired($request)) { /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); diff --git a/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml new file mode 100644 index 0000000000000..db44a06b54a88 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml @@ -0,0 +1,68 @@ + + + + + + + + + + <description value="Free Shipping Minimum Order Amount Excluding/Including Tax options"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20613"/> + <useCaseId value="MC-18457"/> + <group value="shipping"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + <field key="price">100.00</field> + </createData> + <!-- Enable free shipping method --> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> + <createData entity="SetTaxIncluding" stepKey="setTaxIncluding"/> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="configureTaxForCA"/> + <createData entity="defaultTaxRule" stepKey="createTaxRule"/> + </before> + <after> + <!-- Disable free shipping method --> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> + <createData entity="SetTaxIncludingToDefault" stepKey="setTaxIncludingToDefault"/> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <createData entity="DefaultTaxConfig" stepKey="resetTaxConfiguration"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + </after> + <!-- Add simple product to cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.tax}}" stepKey="waitForOverviewVisible"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.estimateShippingAndTaxForm}}" stepKey="waitForEstimateShippingAndTaxForm"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.shippingMethodForm}}" stepKey="waitForShippingMethodForm"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <waitForPageLoad stepKey="waitForSelectCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <waitForPageLoad stepKey="waitForSelectRegion"/> + <see selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25" stepKey="seeTaxForCA"/> + <!-- See available Free Shipping option --> + <actionGroup ref="StorefrontAssertShippingMethodPresentInCartActionGroup" stepKey="assertShippingMethodLabel"> + <argument name="shippingMethod" value="{{freeTitleDefault.value}}"/> + </actionGroup> + <!-- Change State to New York --> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_NY.state}}" stepKey="selectAnotherState"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + <dontSee selector="{{CheckoutCartSummarySection.shippingMethodLabel}}" userInput="{{freeTitleDefault.value}}" stepKey="assertShippingMethodIsNotPresentInCart"/> + </test> +</tests> diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php new file mode 100644 index 0000000000000..7f8959e610d42 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php @@ -0,0 +1,200 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\OfflineShipping\Test\Unit\Model\Carrier; + +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\OfflineShipping\Model\Carrier\Freeshipping; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory; +use Magento\Shipping\Model\Rate\ResultFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; + +/** + * Class for test free shipping + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FreeshippingTest extends TestCase +{ + /** + * @var Freeshipping + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var ResultFactory|MockObject + */ + private $resultFactoryMock; + + /** + * @var MethodFactory|MockObject + */ + private $methodFactoryMock; + + /** + * @var ObjectManager + */ + private $helper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->methodFactoryMock = $this + ->getMockBuilder(MethodFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->helper = new ObjectManager($this); + $this->model = $this->helper->getObject( + Freeshipping::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + '_rateResultFactory' => $this->resultFactoryMock, + '_rateMethodFactory' => $this->methodFactoryMock, + ] + ); + } + + /** + * Test for collect rate free shipping with tax options + * + * @param int $subtotalInclTax + * @param int $minOrderAmount + * @param int $packageValueWithDiscount + * @param int $baseSubtotalWithDiscountInclTax + * @param InvokedCount $expectedCallAppend + * + * @return void + * @dataProvider freeShippingWithSubtotalTaxDataProvider + */ + public function testCollectRatesFreeShippingWithTaxOptions( + int $subtotalInclTax, + int $minOrderAmount, + int $packageValueWithDiscount, + int $baseSubtotalWithDiscountInclTax, + InvokedCount $expectedCallAppend + ): void { + /** @var RateRequest|MockObject $request */ + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getAllItems', + 'getPackageQty', + 'getFreeShipping', + 'getBaseSubtotalWithDiscountInclTax', + 'getPackageValueWithDiscount', + ] + ) + ->getMock(); + $item = $this->getMockBuilder(QuoteItem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock->expects($this->at(0)) + ->method('isSetFlag') + ->willReturn(true); + $this->scopeConfigMock->expects($this->at(1)) + ->method('isSetFlag') + ->with( + 'carriers/freeshipping/tax_including', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($subtotalInclTax); + $this->scopeConfigMock->expects($this->at(2)) + ->method('getValue') + ->with( + 'carriers/freeshipping/free_shipping_subtotal', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($minOrderAmount); + $method = $this->getMockBuilder(Method::class) + ->disableOriginalConstructor() + ->setMethods(['setCarrier', 'setCarrierTitle', 'setMethod', 'setMethodTitle', 'setPrice', 'setCost']) + ->getMock(); + $resultModel = $this->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->setMethods(['append']) + ->getMock(); + $this->resultFactoryMock->method('create') + ->willReturn($resultModel); + $request->method('getPackageValueWithDiscount') + ->willReturn($packageValueWithDiscount); + $request->method('getAllItems') + ->willReturn([$item]); + $request->method('getFreeShipping') + ->willReturn(false); + $request->method('getBaseSubtotalWithDiscountInclTax') + ->willReturn($baseSubtotalWithDiscountInclTax); + $this->methodFactoryMock->method('create')->willReturn($method); + + $resultModel->expects($expectedCallAppend) + ->method('append') + ->with($method); + + $this->model->collectRates($request); + } + + /** + * @return array + */ + public function freeShippingWithSubtotalTaxDataProvider(): array + { + return [ + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->once(), + + ], + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 20, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + [ + 'subtotalInclTax' => 0, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + ]; + } +} diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index 2b29d2211b9d1..cb75bddf4d7bd 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -127,6 +127,10 @@ <label>Minimum Order Amount</label> <validate>validate-number validate-zero-or-greater</validate> </field> + <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <label>Include Tax to Amount</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 3ecbc69b80785..ecc51a00e8fb5 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -471,7 +471,7 @@ protected function _isDefaultShippingNullOrSameAsBillingAddress() /** * Declare address quote model object * - * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote $quote * @return $this */ public function setQuote(\Magento\Quote\Model\Quote $quote) @@ -691,7 +691,7 @@ public function getItemQty($itemId = 0) */ public function hasItems() { - return sizeof($this->getAllItems()) > 0; + return count($this->getAllItems()) > 0; } /** @@ -1020,6 +1020,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $request->setLimitCarrier($this->getLimitCarrier()); $baseSubtotalInclTax = $this->getBaseSubtotalTotalInclTax(); $request->setBaseSubtotalInclTax($baseSubtotalInclTax); + $request->setBaseSubtotalWithDiscountInclTax($this->getBaseSubtotalWithDiscount() + $this->getBaseTaxAmount()); $result = $this->_rateCollector->create()->collectRates($request)->getResult(); @@ -1225,8 +1226,8 @@ public function setBaseShippingAmount($value, $alreadyExclTax = false) /** * Set total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setTotalAmount($code, $amount) @@ -1243,8 +1244,8 @@ public function setTotalAmount($code, $amount) /** * Set total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setBaseTotalAmount($code, $amount) @@ -1261,8 +1262,8 @@ public function setBaseTotalAmount($code, $amount) /** * Add amount total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addTotalAmount($code, $amount) @@ -1276,8 +1277,8 @@ public function addTotalAmount($code, $amount) /** * Add amount total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addBaseTotalAmount($code, $amount) diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml index d700aa622c177..3d3667e59903f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml @@ -26,6 +26,7 @@ <requiredEntity type="title">freeTitleDefault</requiredEntity> <requiredEntity type="name">freeNameDefault</requiredEntity> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> <requiredEntity type="specificerrmsg">freeSpecificerrmsgDefault</requiredEntity> <requiredEntity type="sallowspecific">freeSallowspecificDefault</requiredEntity> <requiredEntity type="specificcountry">freeSpecificcountryDefault</requiredEntity> @@ -44,6 +45,9 @@ <entity name="freeShippingSubtotalDefault" type="free_shipping_subtotal"> <data key="value" /> </entity> + <entity name="TaxIncludingDefault" type="tax_including"> + <data key="value">0</data> + </entity> <entity name="freeSpecificerrmsgDefault" type="specificerrmsg"> <data key="value">This shipping method is not available. To use this shipping method, please contact us.</data> </entity> @@ -66,6 +70,17 @@ <entity name="freeShippingSubtotal" type="free_shipping_subtotal"> <data key="value">101</data> </entity> + <!--Set Free Shipping "Include Tax to Amount" to "Yes"--> + <entity name="SetTaxIncluding" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncluding</requiredEntity> + </entity> + <entity name="TaxIncluding" type="tax_including"> + <data key="value">1</data> + </entity> + <!--Set to default Free Shipping "Include Tax to Amount"--> + <entity name="SetTaxIncludingToDefault" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> + </entity> <!--Set to default Free Shipping Subtotal--> <entity name="setFreeShippingSubtotalToDefault" type="free_shipping_method"> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml index 5781b886386f6..14c0d6d5af725 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml @@ -66,6 +66,9 @@ <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> <field key="value">string</field> </object> + <object key="tax_including" dataType="tax_including"> + <field key="value">boolean</field> + </object> <object key="specificerrmsg" dataType="specificerrmsg"> <field key="value">string</field> </object> diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 77b3cfa3a08bb..c70c715d32c1b 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -383,6 +383,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } else { @@ -394,6 +395,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } @@ -592,6 +594,7 @@ protected function processProductItems( $total->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); $total->setBaseSubtotalInclTax($baseSubtotalInclTax); $shippingAssignment->getShipping()->getAddress()->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); + $shippingAssignment->getShipping()->getAddress()->setBaseTaxAmount($baseTax); return $this; } @@ -799,6 +802,7 @@ public function convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo 'rates' => $rates, ]; if (!empty($extraInfo)) { + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $appliedTaxArray = array_merge($appliedTaxArray, $extraInfo); } From f5a7ac98dadb79e5c994fee06a5e03b8346e1671 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 3 Oct 2019 16:02:55 +0300 Subject: [PATCH 02/15] MC-5233: DateTime product attributes support --- .../Product/Attribute/Edit/Tab/Advanced.php | 50 +++++- .../Product/Attribute/Edit/Tab/Main.php | 112 +++++++++----- .../Attribute/Edit/Tab/AdvancedTest.php | 104 ++++++++++--- .../Unit/Ui/Component/ColumnFactoryTest.php | 143 ++++++++++++++++-- .../Product/Form/Modifier/EavTest.php | 100 +++++++----- .../Catalog/Ui/Component/ColumnFactory.php | 72 +++++++-- .../Catalog/Ui/Component/Listing/Columns.php | 5 +- .../Product/Form/Modifier/Eav.php | 16 ++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 17 +++ app/code/Magento/Catalog/etc/config.xml | 7 + .../catalog/product/attribute/js.phtml | 1 + .../product_attribute_add_form.xml | 26 ++++ .../Magento/Eav/Model/Entity/Attribute.php | 37 ++++- .../Entity/Attribute/Frontend/Datetime.php | 6 +- .../Attribute/Frontend/DatetimeTest.php | 62 ++++++-- .../Test/Unit/Model/Entity/AttributeTest.php | 2 + .../adminhtml/web/js/product-attributes.js | 8 + app/code/Magento/Ui/Component/Filters.php | 3 + .../Ui/Component/Filters/Type/Date.php | 47 ++++-- .../Component/Form/Element/DataType/Date.php | 24 ++- .../Unit/Component/Filters/Type/DateTest.php | 113 +++++++++----- .../Ui/Test/Unit/Component/FiltersTest.php | 47 ++++-- .../Form/Element/DataType/DateTest.php | 69 ++++++++- .../Ui/view/base/web/js/form/element/date.js | 7 + .../Ui/view/base/web/js/grid/columns/date.js | 17 ++- .../view/base/web/js/grid/filters/filters.js | 4 + .../Ui/view/base/web/js/grid/filters/range.js | 8 + 27 files changed, 872 insertions(+), 235 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php index 1b6756968662f..89239a2e3e608 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php @@ -7,16 +7,20 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; use Magento\Backend\Block\Widget\Form\Generic; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; use Magento\Eav\Helper\Data; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime; /** - * Product attribute add/edit form main tab + * Product attribute add/edit advanced form tab * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Advanced extends Generic { @@ -70,7 +74,7 @@ public function __construct( * Adding product form elements for editing attribute * * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD) */ protected function _prepareForm() @@ -139,7 +143,21 @@ protected function _prepareForm() 'label' => __('Default Value'), 'title' => __('Default Value'), 'value' => $attributeObject->getDefaultValue(), - 'date_format' => $dateFormat + 'date_format' => $dateFormat, + ] + ); + + $timeFormat = $this->_localeDate->getTimeFormat(\IntlDateFormatter::SHORT); + $fieldset->addField( + 'default_value_datetime', + 'date', + [ + 'name' => 'default_value_datetime', + 'label' => __('Default Value'), + 'title' => __('Default Value'), + 'value' => $this->getLocalizedDateDefaultValue(), + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, ] ); @@ -266,7 +284,7 @@ protected function _initFormValues() /** * Retrieve attribute object from registry * - * @return mixed + * @return Attribute */ private function getAttributeObject() { @@ -285,4 +303,28 @@ private function getPropertyLocker() } return $this->propertyLocker; } + + /** + * Get localized date default value + * + * @return string + * @throws LocalizedException + */ + private function getLocalizedDateDefaultValue(): string + { + $attributeObject = $this->getAttributeObject(); + if (empty($attributeObject->getDefaultValue()) || $attributeObject->getFrontendInput() !== 'datetime') { + return (string)$attributeObject->getDefaultValue(); + } + + try { + $localizedDate = $this->_localeDate->date($attributeObject->getDefaultValue(), null, false); + $localizedDate->setTimezone(new \DateTimeZone($this->_localeDate->getConfigTimezone())); + $localizedDate = $localizedDate->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid.')); + } + + return $localizedDate; + } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php index 85cf37a1214b5..ddc7273432cb3 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php @@ -7,60 +7,60 @@ /** * Product attribute add/edit form main tab * - * @author Magento Core Team <core@magentocommerce.com> + * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply as HelperApply; use Magento\Eav\Block\Adminhtml\Attribute\Edit\Main\AbstractMain; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\DataObject; /** + * Product attribute add/edit form main tab + * * @api - * @SuppressWarnings(PHPMD.DepthOfInheritance) * @since 100.0.2 */ class Main extends AbstractMain { /** - * Adding product form elements for editing attribute - * - * @return $this - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @inheritdoc */ protected function _prepareForm() { parent::_prepareForm(); - /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attributeObject */ - $attributeObject = $this->getAttributeObject(); - /* @var $form \Magento\Framework\Data\Form */ - $form = $this->getForm(); - /* @var $fieldset \Magento\Framework\Data\Form\Element\Fieldset */ - $fieldset = $form->getElement('base_fieldset'); - $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; - foreach ($fieldset->getElements() as $element) { - /** @var \Magento\Framework\Data\Form\AbstractForm $element */ - if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { - $fieldsToRemove[] = $element->getId(); - } - } - foreach ($fieldsToRemove as $id) { - $fieldset->removeField($id); - } + $this->removeUnusedFields(); + $this->processFrontendInputTypes(); + + $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $this->getForm()]); + + return $this; + } + /** + * @inheritdoc + */ + protected function _getAdditionalElementTypes() + { + return ['apply' => HelperApply::class]; + } + + /** + * Process frontend input types for product attributes + * + * @return void + */ + private function processFrontendInputTypes(): void + { + $form = $this->getForm(); + /** @var AbstractElement $frontendInputElm */ $frontendInputElm = $form->getElement('frontend_input'); - $additionalTypes = [ - ['value' => 'price', 'label' => __('Price')], - ['value' => 'media_image', 'label' => __('Media Image')], - ]; - $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; - if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { - $additionalTypes[] = [ - 'value' => $attributeObject->getFrontendInput(), - 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], - ]; - } + $additionalTypes = $this->getAdditionalFrontendInputTypes(); - $response = new \Magento\Framework\DataObject(); + $response = new DataObject(); $response->setTypes([]); $this->_eventManager->dispatch('adminhtml_product_attribute_types', ['response' => $response]); $_hiddenFields = []; @@ -74,19 +74,51 @@ protected function _prepareForm() $frontendInputValues = array_merge($frontendInputElm->getValues(), $additionalTypes); $frontendInputElm->setValues($frontendInputValues); + } - $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $form]); + /** + * Get additional Frontend Input Types for product attributes + * + * @return array + */ + private function getAdditionalFrontendInputTypes(): array + { + $additionalTypes = [ + ['value' => 'price', 'label' => __('Price')], + ['value' => 'media_image', 'label' => __('Media Image')], + ]; - return $this; + $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; + $attributeObject = $this->getAttributeObject(); + if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { + $additionalTypes[] = [ + 'value' => $attributeObject->getFrontendInput(), + 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], + ]; + } + + return $additionalTypes; } /** - * Retrieve additional element types for product attributes + * Remove unused form fields * - * @return array + * @return void */ - protected function _getAdditionalElementTypes() + private function removeUnusedFields(): void { - return ['apply' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply::class]; + $form = $this->getForm(); + /* @var $fieldset Fieldset */ + $fieldset = $form->getElement('base_fieldset'); + $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; + foreach ($fieldset->getElements() as $element) { + /** @var AbstractElement $element */ + if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { + $fieldsToRemove[] = $element->getId(); + } + } + foreach ($fieldsToRemove as $id) { + $fieldset->removeField($id); + } } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php index 0352bc83cafb7..4d9345d0b3f22 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php @@ -5,66 +5,87 @@ */ namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; +use Magento\Eav\Helper\Data as EavHelper; +use Magento\Eav\Model\Entity\Type as EntityType; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\Form\Element\Text; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test product attribute add/edit advanced form tab + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AdvancedTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Adminhtml\Product\Attribute\Grid + * @var Advanced */ protected $block; /** - * @var \Magento\Framework\Data\FormFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ protected $formFactory; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registry; /** - * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ protected $localeDate; /** - * @var \Magento\Config\Model\Config\Source\Yesno|\PHPUnit_Framework_MockObject_MockObject + * @var Yesno|MockObject */ protected $yesNo; /** - * @var \Magento\Eav\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var EavHelper|MockObject */ protected $eavData; /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ protected $filesystem; /** - * @var PropertyLocker|\PHPUnit_Framework_MockObject_MockObject + * @var PropertyLocker|MockObject */ protected $propertyLocker; + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->registry = $this->createMock(\Magento\Framework\Registry::class); - $this->formFactory = $this->createMock(\Magento\Framework\Data\FormFactory::class); - $this->yesNo = $this->createMock(\Magento\Config\Model\Config\Source\Yesno::class); - $this->localeDate = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); - $this->eavData = $this->createMock(\Magento\Eav\Helper\Data::class); - $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $objectManager = new ObjectManager($this); + $this->registry = $this->createMock(Registry::class); + $this->formFactory = $this->createMock(FormFactory::class); + $this->yesNo = $this->createMock(Yesno::class); + $this->localeDate = $this->createMock(TimezoneInterface::class); + $this->eavData = $this->createMock(EavHelper::class); + $this->filesystem = $this->createMock(Filesystem::class); $this->propertyLocker = $this->createMock(PropertyLocker::class); $this->block = $objectManager->getObject( - \Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced::class, + Advanced::class, [ 'registry' => $this->registry, 'formFactory' => $this->formFactory, @@ -77,17 +98,35 @@ protected function setUp() ); } + /** + * Test the block's html output + */ public function testToHtml() { - $fieldSet = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $form = $this->createMock(\Magento\Framework\Data\Form::class); + $defaultValue = 'default_value'; + $localizedDefaultValue = 'localized_default_value'; + $frontendInput = 'datetime'; + $dateFormat = 'mm/dd/yy'; + $timeFormat = 'H:i:s:'; + $timeZone = 'America/Chicago'; + + $fieldSet = $this->createMock(Fieldset::class); + $form = $this->createMock(Form::class); $attributeModel = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['getDefaultValue', 'setDisabled', 'getId', 'getEntityType', 'getIsUserDefined', 'getAttributeCode'] + Attribute::class, + [ + 'getDefaultValue', + 'setDisabled', + 'getId', + 'getEntityType', + 'getIsUserDefined', + 'getAttributeCode', + 'getFrontendInput' + ] ); - $entityType = $this->createMock(\Magento\Eav\Model\Entity\Type::class); - $formElement = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Text::class, ['setDisabled']); - $directoryReadInterface = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + $entityType = $this->createMock(EntityType::class); + $formElement = $this->createPartialMock(Text::class, ['setDisabled']); + $directoryReadInterface = $this->createMock(ReadInterface::class); $this->registry->expects($this->any())->method('registry')->with('entity_attribute') ->willReturn($attributeModel); @@ -95,13 +134,28 @@ public function testToHtml() $form->expects($this->any())->method('addFieldset')->willReturn($fieldSet); $form->expects($this->any())->method('getElement')->willReturn($formElement); $fieldSet->expects($this->any())->method('addField')->willReturnSelf(); - $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn('default_value'); + $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn($defaultValue); $attributeModel->expects($this->any())->method('setDisabled')->willReturnSelf(); $attributeModel->expects($this->any())->method('getId')->willReturn(1); $attributeModel->expects($this->any())->method('getEntityType')->willReturn($entityType); $attributeModel->expects($this->any())->method('getIsUserDefined')->willReturn(false); $attributeModel->expects($this->any())->method('getAttributeCode')->willReturn('attribute_code'); - $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn('mm/dd/yy'); + $attributeModel->expects($this->any())->method('getFrontendInput')->willReturn($frontendInput); + + $dateTimeMock = $this->createMock(\DateTime::class); + $dateTimeMock->expects($this->once())->method('setTimezone')->with(new \DateTimeZone($timeZone)); + $dateTimeMock->expects($this->once()) + ->method('format') + ->with(DateTime::DATETIME_PHP_FORMAT) + ->willReturn($localizedDefaultValue); + $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn($dateFormat); + $this->localeDate->expects($this->any())->method('getTimeFormat')->willReturn($timeFormat); + $this->localeDate->expects($this->once())->method('getConfigTimezone')->willReturn($timeZone); + $this->localeDate->expects($this->once()) + ->method('date') + ->with($defaultValue, null, false) + ->willReturn($dateTimeMock); + $entityType->expects($this->any())->method('getEntityTypeCode')->willReturn('entity_type_code'); $this->eavData->expects($this->any())->method('getFrontendClasses')->willReturn([]); $formElement->expects($this->exactly(2))->method('setDisabled')->willReturnSelf(); diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 774edcfeb6b64..f002173de7996 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -7,14 +7,16 @@ namespace Magento\Catalog\Test\Unit\Ui\Component; -use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Catalog\Ui\Component\ColumnFactory; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\ColumnInterface; use Magento\Ui\Component\Filters\FilterModifier; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * ColumnFactory test. @@ -32,25 +34,30 @@ class ColumnFactoryTest extends TestCase private $objectManager; /** - * @var ProductAttributeInterface|\PHPUnit\Framework\MockObject\MockObject + * @var Attribute|MockObject */ private $attribute; /** - * @var ContextInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ContextInterface|MockObject */ private $context; /** - * @var UiComponentFactory|\PHPUnit\Framework\MockObject\MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var ColumnInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ColumnInterface|MockObject */ private $column; + /** + * @var TimezoneInterface|MockObject + */ + private $timeZone; + /** * @inheritdoc */ @@ -58,18 +65,30 @@ protected function setUp(): void { $this->objectManager = new ObjectManager($this); - $this->attribute = $this->getMockBuilder(ProductAttributeInterface::class) - ->setMethods(['usesSource']) - ->getMockForAbstractClass(); + $this->attribute = $this->createPartialMock( + Attribute::class, + [ + 'getAttributeCode', + 'getIsFilterableInGrid', + 'getFrontendInput', + 'getDefaultFrontendLabel', + 'getIsVisibleInGrid', + ] + ); $this->context = $this->createMock(ContextInterface::class); $this->uiComponentFactory = $this->createMock(UiComponentFactory::class); $this->column = $this->getMockForAbstractClass(ColumnInterface::class); $this->uiComponentFactory->method('create') ->willReturn($this->column); + $this->timeZone = $this->createMock(TimezoneInterface::class); - $this->columnFactory = $this->objectManager->getObject(ColumnFactory::class, [ - 'componentFactory' => $this->uiComponentFactory - ]); + $this->columnFactory = $this->objectManager->getObject( + ColumnFactory::class, + [ + 'componentFactory' => $this->uiComponentFactory, + 'timezone' => $this->timeZone, + ] + ); } /** @@ -96,7 +115,6 @@ public function testCreatedObject(): void * * @param array $filterModifiers * @param null|string $filter - * * @return void * @dataProvider filterModifiersProvider */ @@ -132,7 +150,7 @@ public function testCreateWithNotFilterableInGridAttribute(array $filterModifier } /** - * Filter modifiers data provider. + * Filter modifiers data provider * * @return array */ @@ -153,4 +171,101 @@ public function filterModifiersProvider(): array ], ]; } + + /** + * Test to create date column + * + * @param string $frontendInput + * @param bool $showsTime + * @param string $expectedDateFormat + * @param string $expectedTimezone + * @dataProvider createDateColumnDataProvider + */ + public function testCreateDateColumn( + string $frontendInput, + bool $showsTime, + string $expectedDateFormat, + string $expectedTimezone + ) { + $attributeCode = 'attribute_code'; + $dateFormat = 'date_format'; + $dateTimeFormat = 'datetime_format'; + $defaultTimezone = 'default_timezone'; + $configTimezone = 'config_timezone'; + $label = 'Date label'; + + $expectedConfig = [ + 'data' => [ + 'config' => [ + 'label' => __($label), + 'dataType' => 'date', + 'add_field' => true, + 'visible' => true, + 'filter' => 'dateRange', + 'component' => 'Magento_Ui/js/grid/columns/date', + 'timeZone' => $expectedTimezone, + 'dateFormat' => $expectedDateFormat, + 'options' => [ + 'showsTime' => $showsTime + ] + ], + ], + 'context' => $this->context, + ]; + + $this->attribute->method('getAttributeCode') + ->willReturn($attributeCode); + $this->attribute->method('getDefaultFrontendLabel') + ->willReturn($label); + $this->attribute->method('getIsFilterableInGrid') + ->willReturn(true); + $this->attribute->method('getIsVisibleInGrid') + ->willReturn(true); + $this->attribute->method('getFrontendInput') + ->willReturn($frontendInput); + + $this->timeZone->method('getDateFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateFormat); + $this->timeZone->method('getDateTimeFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateTimeFormat); + $this->timeZone->method('getDefaultTimezone') + ->willReturn($defaultTimezone); + $this->timeZone->method('getConfigTimezone') + ->willReturn($configTimezone); + + $this->uiComponentFactory->expects($this->once()) + ->method('create') + ->with($attributeCode, 'column', $expectedConfig) + ->willReturn($this->column); + + $this->assertEquals( + $this->column, + $this->columnFactory->create($this->attribute, $this->context) + ); + } + + /** + * Data provider to create date column test + * + * @return array + */ + public function createDateColumnDataProvider(): array + { + return [ + [ + 'frontendInput' => 'date', + 'showsTime' => false, + 'dateFormat' => 'date_format', + 'expectedTimezone' => 'default_timezone', + ], + [ + 'frontendInput' => 'datetime', + 'showsTime' => true, + 'expectedDateFormat' => 'datetime_format', + 'expectedTimezone' => 'config_timezone', + ], + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 88075b13f1430..834cb505e0903 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -38,6 +38,7 @@ use Magento\Framework\Stdlib\ArrayManager; use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory as EavAttributeFactory; use Magento\Framework\Event\ManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; /** * Class EavTest @@ -49,142 +50,142 @@ class EavTest extends AbstractModifierTest { /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfigMock; /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + * @var EavValidationRules|MockObject */ private $eavValidationRulesMock; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $requestMock; /** - * @var GroupCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollectionFactory|MockObject */ private $groupCollectionFactoryMock; /** - * @var GroupCollection|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollection|MockObject */ private $groupCollectionMock; /** - * @var Group|\PHPUnit_Framework_MockObject_MockObject + * @var Group|MockObject */ private $groupMock; /** - * @var EavAttribute|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttribute|MockObject */ private $attributeMock; /** - * @var EntityType|\PHPUnit_Framework_MockObject_MockObject + * @var EntityType|MockObject */ private $entityTypeMock; /** - * @var AttributeCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollectionFactory|MockObject */ private $attributeCollectionFactoryMock; /** - * @var AttributeCollection|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollection|MockObject */ private $attributeCollectionMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var FormElementMapper|\PHPUnit_Framework_MockObject_MockObject + * @var FormElementMapper|MockObject */ private $formElementMapperMock; /** - * @var MetaPropertiesMapper|\PHPUnit_Framework_MockObject_MockObject + * @var MetaPropertiesMapper|MockObject */ private $metaPropertiesMapperMock; /** - * @var SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteriaBuilder|MockObject */ private $searchCriteriaBuilderMock; /** - * @var ProductAttributeGroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeGroupRepositoryInterface|MockObject */ private $attributeGroupRepositoryMock; /** - * @var SearchCriteria|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteria|MockObject */ private $searchCriteriaMock; /** - * @var SortOrderBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SortOrderBuilder|MockObject */ private $sortOrderBuilderMock; /** - * @var ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeRepositoryInterface|MockObject */ private $attributeRepositoryMock; /** - * @var AttributeGroupInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeGroupInterface|MockObject */ private $attributeGroupMock; /** - * @var SearchResultsInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SearchResultsInterface|MockObject */ private $searchResultsMock; /** - * @var Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ private $eavAttributeMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ protected $storeMock; /** - * @var Currency|\PHPUnit_Framework_MockObject_MockObject + * @var Currency|MockObject */ protected $currencyMock; /** - * @var CurrencyLocale|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencyLocale|MockObject */ protected $currencyLocaleMock; /** - * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeInterface|MockObject */ protected $productAttributeMock; /** - * @var ArrayManager|\PHPUnit_Framework_MockObject_MockObject + * @var ArrayManager|MockObject */ protected $arrayManagerMock; /** - * @var EavAttributeFactory|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttributeFactory|MockObject */ protected $eavAttributeFactoryMock; /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $eventManagerMock; @@ -457,8 +458,10 @@ public function testModifyData() * @param string|null $attrValue * @param array $expected * @param bool $locked - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta + * @param string|null $frontendInput + * @param array $expectedCustomize + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta * @dataProvider setupAttributeMetaDataProvider */ public function testSetupAttributeMetaDefaultAttribute( @@ -466,7 +469,9 @@ public function testSetupAttributeMetaDefaultAttribute( bool $productRequired, $attrValue, array $expected, - $locked = false + bool $locked = false, + string $frontendInput = null, + array $expectedCustomize = [] ) : void { $configPath = 'arguments/data/config'; $groupCode = 'product-details'; @@ -492,6 +497,7 @@ public function testSetupAttributeMetaDefaultAttribute( $this->productAttributeMock->method('getDefaultValue')->willReturn('required_value'); $this->productAttributeMock->method('getAttributeCode')->willReturn('code'); $this->productAttributeMock->method('getValue')->willReturn('value'); + $this->productAttributeMock->method('getFrontendInput')->willReturn($frontendInput); $attributeMock = $this->getMockBuilder(AttributeInterface::class) ->setMethods(['getValue']) @@ -527,14 +533,16 @@ function ($value) use ($attributeOptionsExpected) { } ) ) - ->willReturn($expected); + ->willReturn($expected + $expectedCustomize); $this->arrayManagerMock->method('get')->willReturn([]); $this->arrayManagerMock->method('exists')->willReturn(true); + $actual = $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder); + $this->assertEquals( - $expected, - $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder) + $expected + $expectedCustomize, + $actual ); } @@ -660,7 +668,29 @@ public function setupAttributeMetaDataProvider() 'globalScope' => false, 'sortOrder' => 0, ], - ] + ], + 'datetime_null_prod_not_new_and_required' => [ + 'productId' => 1, + 'productRequired' => true, + 'attrValue' => 'val', + 'expected' => [ + 'dataType' => 'datetime', + 'formElement' => 'datetime', + 'visible' => null, + 'required' => true, + 'notice' => null, + 'default' => null, + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0, + ], + 'locked' => false, + 'frontendInput' => 'datetime', + 'expectedCustomize' => ['arguments' => ['data' => ['config' => ['options' => ['showsTime' => 1]]]]], + ], ]; } } diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 9a6a22fcb0985..c538273476b1f 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -5,7 +5,14 @@ */ namespace Magento\Catalog\Ui\Component; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Filters\FilterModifier; +use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** * Column Factory @@ -16,7 +23,7 @@ class ColumnFactory { /** - * @var \Magento\Framework\View\Element\UiComponentFactory + * @var UiComponentFactory */ protected $componentFactory; @@ -40,25 +47,36 @@ class ColumnFactory 'select' => 'select', 'multiselect' => 'multiselect', 'date' => 'date', + 'datetime' => 'date', ]; /** - * @param \Magento\Framework\View\Element\UiComponentFactory $componentFactory + * @var TimezoneInterface */ - public function __construct(\Magento\Framework\View\Element\UiComponentFactory $componentFactory) - { + private $timezone; + + /** + * @param UiComponentFactory $componentFactory + * @param TimezoneInterface|null $timezone + */ + public function __construct( + UiComponentFactory $componentFactory, + TimezoneInterface $timezone = null + ) { $this->componentFactory = $componentFactory; + $this->timezone = $timezone + ?? ObjectManager::getInstance()->get(TimezoneInterface::class); } /** * Create Factory * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute - * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context + * @param ProductAttributeInterface $attribute + * @param ContextInterface $context * @param array $config * - * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface - * @throws \Magento\Framework\Exception\LocalizedException + * @return ColumnInterface + * @throws LocalizedException */ public function create($attribute, $context, array $config = []) { @@ -84,19 +102,46 @@ public function create($attribute, $context, array $config = []) $optionData['__disableTmpl'] = true; } } - + $config['component'] = $this->getJsComponent($config['dataType']); - + + if ($config['dataType'] === 'date') { + $config += $this->getDateConfig($attribute); + } + $arguments = [ 'data' => [ 'config' => $config, ], 'context' => $context, ]; - + return $this->componentFactory->create($columnName, 'column', $arguments); } + /** + * Get config for Date columns + * + * @param ProductAttributeInterface $attribute + * @return array + */ + private function getDateConfig(ProductAttributeInterface $attribute): array + { + $isDatetime = $attribute->getFrontendInput() === 'datetime'; + $dateFormat = $isDatetime + ? $this->timezone->getDateTimeFormat(\IntlDateFormatter::MEDIUM) + : $this->timezone->getDateFormat(\IntlDateFormatter::MEDIUM); + $timeZone = $isDatetime + ? $this->timezone->getConfigTimezone() + : $this->timezone->getDefaultTimezone(); + + return [ + 'timeZone' => $timeZone, + 'dateFormat' => $dateFormat, + 'options' => [ 'showsTime' => $isDatetime], + ]; + } + /** * Get Js Component * @@ -112,7 +157,7 @@ protected function getJsComponent($dataType) /** * Get Data Type * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param ProductAttributeInterface $attribute * * @return string */ @@ -129,8 +174,9 @@ protected function getDataType($attribute) */ protected function getFilterType($frontendInput) { - $filtersMap = ['date' => 'dateRange']; + $filtersMap = ['date' => 'dateRange', 'datetime' => 'dateRange']; $result = array_replace_recursive($this->dataTypeMap, $filtersMap); + return $result[$frontendInput] ?? $result['default']; } } diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php index 8ea6d8b9e5a06..d7b9bc3846f49 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Ui\Component\Listing; /** + * Column IU component + * * @api * @since 100.0.2 */ @@ -30,6 +32,7 @@ class Columns extends \Magento\Ui\Component\Listing\Columns 'boolean' => 'select', 'multiselect' => 'select', 'date' => 'dateRange', + 'datetime' => 'datetimeRange', ]; /** @@ -52,7 +55,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function prepare() { diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index 4039ff862f6fe..e41b2390930f0 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -746,6 +746,9 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // Gallery attribute is being handled by "Images And Videos" section $meta = []; break; + case 'datetime': + $meta = $this->customizeDatetimeAttribute($meta); + break; } //Checking access to design config. @@ -948,6 +951,19 @@ private function customizeWysiwyg(ProductAttributeInterface $attribute, array $m return $meta; } + /** + * Customize datetime attribute + * + * @param array $meta + * @return array + */ + private function customizeDatetimeAttribute(array $meta) + { + $meta['arguments']['data']['config']['options']['showsTime'] = 1; + + return $meta; + } + /** * Retrieve form element * diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c04cfb2dce00a..9e02d3da9f5d8 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -232,4 +232,21 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype"> + <arguments> + <argument name="optionsArray" xsi:type="array"> + <item name="450" xsi:type="array"> + <item name="value" xsi:type="string">datetime</item> + <item name="label" xsi:type="string" translate="true">Date and Time</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Ui\DataProvider\Mapper\FormElement"> + <arguments> + <argument name="mappings" xsi:type="array"> + <item name="datetime" xsi:type="string">date</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 20511f4ff2295..8506d2ae03032 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -78,5 +78,12 @@ <thumbnail_position>stretch</thumbnail_position> </watermark> </design> + <general> + <validator_data> + <input_types> + <datetime>datetime</datetime> + </input_types> + </validator_data> + </general> </default> </config> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index f020eddc35dbd..212a345f4bcbc 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -172,6 +172,7 @@ function switchDefaultValueField() break; case 'date': + case 'datetime': defaultValueDateVisibility = true; break; diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 6c5d37a92ea4a..3a6621137ed5a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -101,6 +101,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -287,6 +288,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -376,6 +378,29 @@ <dataScope>default_value_date</dataScope> </settings> </field> + <field name="default_value_datetime" component="Magento_Catalog/js/components/visible-on-option/date" sortOrder="35" formElement="date"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="valuesForOptions" xsi:type="array"> + <item name="datetime" xsi:type="string">datetime</item> + </item> + </item> + </argument> + <settings> + <dataType>text</dataType> + <label translate="true">Default Value</label> + <dataScope>default_value_datetime</dataScope> + </settings> + <formElements> + <date> + <settings> + <options> + <option name="showsTime" xsi:type="boolean">true</option> + </options> + </settings> + </date> + </formElements> + </field> <field name="is_unique" component="Magento_Catalog/js/components/visible-on-option/yesno" sortOrder="40" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -412,6 +437,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">date</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 8bd9ca2cc03c8..82eafa6174bb2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -285,13 +285,8 @@ public function beforeSave() // save default date value as timestamp if ($hasDefaultValue) { - try { - $locale = $this->_localeResolver->getLocale(); - $defaultValue = $this->_localeDate->date($defaultValue, $locale, false, false); - $this->setDefaultValue($defaultValue->format(DateTime::DATETIME_PHP_FORMAT)); - } catch (\Exception $e) { - throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); - } + $defaultValue = $this->getUtcDateDefaultValue($defaultValue); + $this->setDefaultValue($defaultValue); } } @@ -310,6 +305,29 @@ public function beforeSave() return parent::beforeSave(); } + /** + * Convert localized date default value to UTC + * + * @param string $defaultValue + * @return string + * @throws LocalizedException + */ + private function getUtcDateDefaultValue(string $defaultValue): string + { + $hasTime = $this->getFrontendInput() === 'datetime'; + try { + $defaultValue = $this->_localeDate->date($defaultValue, null, $hasTime, $hasTime); + if ($hasTime) { + $defaultValue->setTimezone(new \DateTimeZone($this->_localeDate->getDefaultTimezone())); + } + $utcValue = $defaultValue->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); + } + + return $utcValue; + } + /** * @inheritdoc * @@ -346,6 +364,7 @@ public function getBackendTypeByInput($type) break; case 'date': + case 'datetime': $field = 'datetime'; break; @@ -401,6 +420,10 @@ public function getDefaultValueByInput($type) $field = 'default_value_date'; break; + case 'datetime': + $field = 'default_value_datetime'; + break; + case 'boolean': $field = 'default_value_yesno'; break; diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php index a9cd3be246bb1..8effd73d34af2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php @@ -7,6 +7,8 @@ namespace Magento\Eav\Model\Entity\Attribute\Frontend; /** + * Entity datetime frontend attribute + * * @api * @since 100.0.2 */ @@ -42,10 +44,12 @@ public function getValue(\Magento\Framework\DataObject $object) $value = parent::getValue($object); if ($value) { + $timeType = $this->getAttribute()->getFrontendInput() === 'datetime' + ? \IntlDateFormatter::MEDIUM : \IntlDateFormatter::NONE; $data = $this->_localeDate->formatDateTime( new \DateTime($value), \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::NONE + $timeType ); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php index 66549f2e00415..c775548fc8c75 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php @@ -5,22 +5,31 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Frontend; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Attribute\Frontend\Datetime; +use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DatetimeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Entity datetime frontend attribute + */ +class DatetimeTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ private $localeDateMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var BooleanFactory|MockObject */ private $booleanFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AbstractAttribute|MockObject */ private $attributeMock; @@ -29,40 +38,63 @@ class DatetimeTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @inheritdoc + */ protected function setUp() { - $this->booleanFactoryMock = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory::class); - $this->localeDateMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + $this->booleanFactoryMock = $this->createMock(BooleanFactory::class); + $this->localeDateMock = $this->createMock(TimezoneInterface::class); $this->attributeMock = $this->createPartialMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getAttributeCode', 'getFrontendLabel', 'getData'] + AbstractAttribute::class, + ['getAttributeCode', 'getFrontendLabel', 'getFrontendInput'] ); $this->model = new Datetime($this->booleanFactoryMock, $this->localeDateMock); $this->model->setAttribute($this->attributeMock); } - public function testGetValue() + /** + * Test to retrieve attribute value + * + * @param string $frontendInput + * @param int $timeType + * @dataProvider getValueDataProvider + */ + public function testGetValue(string $frontendInput, int $timeType) { $attributeValue = '11-11-2011'; + $attributeCode = 'datetime'; $date = new \DateTime($attributeValue); - $object = new \Magento\Framework\DataObject(['datetime' => $attributeValue]); + $object = new DataObject([$attributeCode => $attributeValue]); $this->attributeMock->expects($this->any()) ->method('getAttributeCode') - ->willReturn('datetime'); + ->willReturn($attributeCode); $this->attributeMock->expects($this->any()) - ->method('getData') - ->with('frontend_input') - ->willReturn('text'); + ->method('getFrontendInput') + ->willReturn($frontendInput); $this->localeDateMock->expects($this->once()) ->method('formatDateTime') - ->with($date, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, null, null) + ->with($date, \IntlDateFormatter::MEDIUM, $timeType) ->willReturn($attributeValue); $this->assertEquals($attributeValue, $this->model->getValue($object)); } + /** + * Retrieve attribute value data provider + * + * @return array + */ + public function getValueDataProvider(): array + { + return [ + ['frontendInput' => 'date', 'timeType' => \IntlDateFormatter::NONE], + ['frontendInput' => 'datetime', 'timeType' => \IntlDateFormatter::MEDIUM], + ]; + } + /** * @param mixed $labelText * @param string $attributeCode diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 402497a1379c0..7aa5bca00f0b6 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -57,6 +57,7 @@ public static function dataGetBackendTypeByInput() ['image', 'text'], ['textarea', 'text'], ['date', 'datetime'], + ['datetime', 'datetime'], ['select', 'int'], ['boolean', 'int'], ['price', 'decimal'], @@ -91,6 +92,7 @@ public static function dataGetDefaultValueByInput() ['weight', 'default_value_text'], ['textarea', 'default_value_textarea'], ['date', 'default_value_date'], + ['datetime', 'default_value_datetime'], ['boolean', 'default_value_yesno'] ]; } diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index f795f99e8112d..9e48af20ee945 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -31,6 +31,7 @@ define([ defaultValueText: $('#default_value_text'), defaultValueTextarea: $('#default_value_textarea'), defaultValueDate: $('#default_value_date'), + defaultValueDatetime: $('#default_value_datetime'), defaultValueYesno: $('#default_value_yesno'), isGlobal: $('#is_global'), useProductImageForSwatch: $('#use_product_image_for_swatch'), @@ -178,6 +179,7 @@ define([ defaultValueTextVisibility = false, defaultValueTextareaVisibility = false, defaultValueDateVisibility = false, + defaultValueDatetimeVisibility = false, defaultValueYesnoVisibility = false, scopeVisibility = true, useProductImageForSwatch = false, @@ -203,6 +205,10 @@ define([ defaultValueDateVisibility = true; break; + case 'datetime': + defaultValueDatetimeVisibility = true; + break; + case 'boolean': defaultValueYesnoVisibility = true; break; @@ -256,6 +262,7 @@ define([ defaultValueTextVisibility = false; defaultValueTextareaVisibility = false; defaultValueDateVisibility = false; + defaultValueDatetimeVisibility = false; defaultValueYesnoVisibility = false; break; @@ -279,6 +286,7 @@ define([ this.setRowVisibility(this.defaultValueText, defaultValueTextVisibility); this.setRowVisibility(this.defaultValueTextarea, defaultValueTextareaVisibility); this.setRowVisibility(this.defaultValueDate, defaultValueDateVisibility); + this.setRowVisibility(this.defaultValueDatetime, defaultValueDatetimeVisibility); this.setRowVisibility(this.defaultValueYesno, defaultValueYesnoVisibility); this.setRowVisibility(this.isGlobal, scopeVisibility); diff --git a/app/code/Magento/Ui/Component/Filters.php b/app/code/Magento/Ui/Component/Filters.php index fe02c23af9c8a..5bf89ae7936e9 100644 --- a/app/code/Magento/Ui/Component/Filters.php +++ b/app/code/Magento/Ui/Component/Filters.php @@ -12,6 +12,8 @@ use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** + * Grid filters UI component + * * @api * @since 100.0.2 */ @@ -36,6 +38,7 @@ class Filters extends AbstractComponent implements ObserverInterface 'textRange' => 'filterRange', 'select' => 'filterSelect', 'dateRange' => 'filterDate', + 'datetimeRange' => 'filterDate', ]; /** diff --git a/app/code/Magento/Ui/Component/Filters/Type/Date.php b/app/code/Magento/Ui/Component/Filters/Type/Date.php index e854b888c45e6..28ad8568ebe31 100644 --- a/app/code/Magento/Ui/Component/Filters/Type/Date.php +++ b/app/code/Magento/Ui/Component/Filters/Type/Date.php @@ -9,6 +9,8 @@ use Magento\Ui\Component\Form\Element\DataType\Date as DataTypeDate; /** + * Date grid filter UI Component + * * @api * @since 100.0.2 */ @@ -84,30 +86,18 @@ protected function applyFilter() if (isset($value['from'])) { $this->applyFilterByType( 'gteq', - $this->wrappedComponent->convertDate( - $value['from'], - 0, - 0, - 0, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['from']) ); } if (isset($value['to'])) { $this->applyFilterByType( 'lteq', - $this->wrappedComponent->convertDate( - $value['to'], - 23, - 59, - 59, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['to'], 23, 59, 59) ); } } else { - $this->applyFilterByType('eq', $this->wrappedComponent->convertDate($value)); + $this->applyFilterByType('eq', $this->convertDatetime((string)$value)); } } } @@ -130,4 +120,31 @@ protected function applyFilterByType($type, $value) $this->getContext()->getDataProvider()->addFilter($filter); } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $value + * @param int $hour + * @param int $minute + * @param int $second + * @return \DateTime + */ + private function convertDatetime(string $value, int $hour = 0, int $minute = 0, int $second = 0): ?\DateTime + { + $value = $this->getData('config/options/showsTime') + ? $this->wrappedComponent->convertDatetime( + $value, + !$this->getData('config/skipTimeZoneConversion') + ) + : $this->wrappedComponent->convertDate( + $value, + $hour, + $minute, + $second, + !$this->getData('config/skipTimeZoneConversion') + ); + + return $value; + } } diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 31d2fe786cfd8..8ea6236e8e2e2 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -111,7 +111,7 @@ public function getComponentName() public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcTimeZone = true) { try { - $dateObj = $this->localeDate->date($date, $this->getLocale(), true); + $dateObj = $this->localeDate->date($date, $this->getLocale(), false); $dateObj->setTime($hour, $minute, $second); //convert store date to default date in UTC timezone without DST if ($setUtcTimeZone) { @@ -122,4 +122,26 @@ public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcT return null; } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $date + * @param bool $setUtcTimeZone + * @return \DateTime|null + */ + public function convertDatetime(string $date, bool $setUtcTimeZone = true): ?\DateTime + { + try { + $date = rtrim($date, 'Z'); + $dateObj = new \DateTime($date, new \DateTimeZone($this->localeDate->getConfigTimezone())); + //convert store date to default date in UTC timezone without DST + if ($setUtcTimeZone) { + $dateObj->setTimezone(new \DateTimeZone('UTC')); + } + return $dateObj; + } catch (\Exception $e) { + return null; + } + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 78456968cbef1..7038a587be0b0 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -13,6 +13,7 @@ use Magento\Ui\Component\Filters\Type\Date; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Form\Element\DataType\Date as FormDate; +use PHPUnit\Framework\MockObject\MockObject; /** * Class DateTest @@ -20,27 +21,27 @@ class DateTest extends \PHPUnit\Framework\TestCase { /** - * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ContextInterface|MockObject */ private $contextMock; /** - * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var FilterBuilder|MockObject */ private $filterBuilderMock; /** - * @var FilterModifier|\PHPUnit_Framework_MockObject_MockObject + * @var FilterModifier|MockObject */ private $filterModifierMock; /** - * @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var DataProviderInterface|MockObject */ private $dataProviderMock; @@ -89,18 +90,19 @@ public function testGetComponentName() * Run test prepare method * * @param string $name + * @param bool $showsTime * @param array $filterData * @param array|null $expectedCondition * @dataProvider getPrepareDataProvider * @return void */ - public function testPrepare($name, $filterData, $expectedCondition) + public function testPrepare(string $name, bool $showsTime, array $filterData, ?array $expectedCondition) { $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) ->disableOriginalConstructor() ->getMock(); $this->contextMock->expects(static::atLeastOnce())->method('getProcessor')->willReturn($processor); - /** @var FormDate $uiComponent */ + /** @var FormDate|MockObject $uiComponent */ $uiComponent = $this->getMockBuilder(FormDate::class) ->disableOriginalConstructor() ->getMock(); @@ -125,7 +127,7 @@ public function testPrepare($name, $filterData, $expectedCondition) ->willReturn($this->dataProviderMock); if ($expectedCondition !== null) { - $this->processFilters($name, $filterData, $expectedCondition, $uiComponent); + $this->processFilters($name, $showsTime, $filterData, $expectedCondition, $uiComponent); } $this->uiComponentFactory->expects($this->any()) @@ -139,7 +141,10 @@ public function testPrepare($name, $filterData, $expectedCondition) $this->filterBuilderMock, $this->filterModifierMock, [], - ['name' => $name] + [ + 'name' => $name, + 'config' => ['options' => ['showsTime' => $showsTime]], + ] ); $date->prepare(); } @@ -152,7 +157,7 @@ public function testPrepare($name, $filterData, $expectedCondition) * @param string $expectedDate * @param int $i * - * @return Filter|\PHPUnit_Framework_MockObject_MockObject + * @return Filter|MockObject */ private function getFilterMock($name, $expectedType, $expectedDate, &$i) { @@ -184,57 +189,87 @@ public function getPrepareDataProvider() { return [ [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => null]], - ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => null]], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], ], [ - 'test_date', - ['test_date' => ['from' => null, 'to' => '11-05-2015']], - ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => null, 'to' => '11-05-2015']], + 'expectedCondition' => ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], ], [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], - [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], + 'expectedCondition' => [ 'date_from' => '2015-05-11 00:00:00', 'type_from' => 'gteq', 'date_to' => '2015-05-11 23:59:59', 'type_to' => 'lteq' ], ], [ - 'test_date', - ['test_date' => '11-05-2015'], - ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => '11-05-2015'], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + ], + [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '', 'to' => '']], + 'expectedCondition' => null, ], [ - 'test_date', - ['test_date' => ['from' => '', 'to' => '']], - null, + 'name' => 'test_date', + 'showsTime' => true, + 'filterData' => ['test_date' => ['from' => '11-05-2015 10:20:00', 'to' => '11-05-2015 18:25:00']], + 'expectedCondition' => [ + 'date_from' => '2015-05-11 10:20:00', 'type_from' => 'gteq', + 'date_to' => '2015-05-11 18:25:00', 'type_to' => 'lteq' + ], ], ]; } /** - * @param $name - * @param $filterData - * @param $expectedCondition - * @param $uiComponent + * @param string $name + * @param bool $showsTime + * @param array $filterData + * @param array $expectedCondition + * @param MockObject $uiComponent */ - private function processFilters($name, $filterData, $expectedCondition, $uiComponent) - { + private function processFilters( + string $name, + bool $showsTime, + array $filterData, + array $expectedCondition, + FormDate $uiComponent + ) { if (is_string($filterData[$name])) { $uiComponent->expects(static::once()) - ->method('convertDate') + ->method($showsTime ? 'convertDatetime' : 'convertDate') ->with($filterData[$name]) ->willReturn(new \DateTime($filterData[$name])); } else { - $from = new \DateTime($filterData[$name]['from']); - $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); - $uiComponent->method('convertDate') - ->willReturnMap([ - [$filterData[$name]['from'], 0, 0, 0, true, $from], - [$filterData[$name]['to'], 23, 59, 59, true, $to], - ]); + if ($showsTime) { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to']); + $uiComponent->method('convertDatetime') + ->willReturnMap([ + [$filterData[$name]['from'], true, $from], + [$filterData[$name]['to'], true, $to], + ]); + } else { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); + $uiComponent->method('convertDate') + ->willReturnMap([ + [$filterData[$name]['from'], 0, 0, 0, true, $from], + [$filterData[$name]['to'], 23, 59, 59, true, $to], + ]); + } } $i = 0; diff --git a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php index 402fd30bf4d5b..19a1be69ca1d7 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php @@ -8,23 +8,27 @@ namespace Magento\Ui\Test\Unit\Component; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Magento\Ui\Component\Filters; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Filters; +use PHPUnit\Framework\MockObject\MockObject; /** * Unit tests for \Magento\Ui\Component\Filters class */ class FiltersTest extends \PHPUnit\Framework\TestCase { - /** @var Filters|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Filters|MockObject */ private $filters; - /** @var \Magento\Framework\View\Element\UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentInterface|MockObject */ private $uiComponentInterface; - /** @var \Magento\Framework\View\Element\UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentFactory|MockObject */ private $uiComponentFactory; - /** @var \Magento\Framework\View\Element\UiComponent\ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ContextInterface|MockObject */ private $context; /** @@ -33,13 +37,13 @@ class FiltersTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $this->uiComponentInterface = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) + $this->uiComponentInterface = $this->getMockBuilder(UiComponentInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->uiComponentFactory = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentFactory::class) + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + $this->context = $this->getMockBuilder(ContextInterface::class) ->disableOriginalConstructor() ->getMock(); $this->filters = $objectManager->getObject( @@ -52,7 +56,14 @@ protected function setUp() ); } - public function testUpdate() + /** + * Test to Update filter component according to $component + * + * @param string $filterType + * @param string $filterName + * @dataProvider updateDataProvider + */ + public function testUpdate(string $filterType, string $filterName) { $componentName = 'component_name'; $componentConfig = [0, 1, 2]; @@ -60,7 +71,10 @@ public function testUpdate() ->disableOriginalConstructor() ->setMethods(['getData', 'getName', 'getConfiguration']) ->getMockForAbstractClass(); - $columnInterface->expects($this->atLeastOnce())->method('getData')->with('config/filter')->willReturn('text'); + $columnInterface->expects($this->atLeastOnce()) + ->method('getData') + ->with('config/filter') + ->willReturn($filterType); $columnInterface->expects($this->atLeastOnce())->method('getName')->willReturn($componentName); $columnInterface->expects($this->once())->method('getConfiguration')->willReturn($componentConfig); $filterComponent = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) @@ -71,11 +85,22 @@ public function testUpdate() ->willReturnSelf(); $filterComponent->expects($this->once())->method('prepare')->willReturnSelf(); $this->uiComponentFactory->expects($this->once())->method('create') - ->with($componentName, 'filterInput', ['context' => $this->context]) + ->with($componentName, $filterName, ['context' => $this->context]) ->willReturn($filterComponent); $this->filters->update($columnInterface); /** Verify that filter is already set and it wouldn't be set again */ $this->filters->update($columnInterface); } + + /** + * @return array + */ + public function updateDataProvider(): array + { + return [ + ['text', 'filterInput'], + ['datetimeRange', 'filterDate'], + ]; + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php index cdb11f05daa8c..015c025e7c102 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php @@ -11,27 +11,35 @@ use Magento\Framework\View\Element\UiComponent\Context; use Magento\Framework\View\Element\UiComponent\Processor; use Magento\Ui\Component\Form\Element\DataType\Date; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DateTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Date form element + */ +class DateTest extends TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Context|MockObject */ private $contextMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var TimezoneInterface|MockObject */ private $localeDateMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var ResolverInterface|MockObject */ private $localeResolverMock; - /** @var \Magento\Ui\Component\Form\Element\DataType\Date */ + /** @var Date */ private $date; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Processor|MockObject */ private $processorMock; - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + /** @var ObjectManager */ private $objectManagerHelper; + /** + * @inheritdoc + */ public function setUp() { $this->contextMock = $this->createMock(Context::class); @@ -39,9 +47,12 @@ public function setUp() $this->localeResolverMock = $this->createMock(ResolverInterface::class); $this->objectManagerHelper = new ObjectManager($this); $this->processorMock = $this->createMock(Processor::class); - $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($this->processorMock); + $this->contextMock->method('getProcessor')->willReturn($this->processorMock); } + /** + * Test to Prepare component configuration with Time offset + */ public function testPrepareWithTimeOffset() { $this->date = new Date( @@ -72,6 +83,9 @@ public function testPrepareWithTimeOffset() $this->assertEquals($localeDateFormat, $config['options']['dateFormat']); } + /** + * Test to Prepare component configuration without Time offset + */ public function testPrepareWithoutTimeOffset() { $defaultDateFormat = 'MM/dd/y'; @@ -130,4 +144,43 @@ public function testPrepare() $this->assertEquals('America/Chicago', $configArray['storeTimeZone']); $this->assertEquals('de-DE', $configArray['options']['storeLocale']); } + + /** + * Test to Convert given date to default (UTC) timezone + * + * @param string $dateStr + * @param bool $setUtcTimeZone + * @param string $convertedDate + * @dataProvider convertDatetimeDataProvider + */ + public function testConvertDatetime(string $dateStr, bool $setUtcTimeZone, string $convertedDate) + { + $this->localeDateMock->method('getConfigTimezone') + ->willReturn('America/Los_Angeles'); + + $this->date = $this->objectManagerHelper->getObject( + Date::class, + [ + 'localeDate' => $this->localeDateMock, + ] + ); + + $this->assertEquals( + $convertedDate, + $this->date->convertDatetime($dateStr, $setUtcTimeZone)->format('Y-m-d H:i:s'), + "The date value wasn't converted" + ); + } + + /** + * @return array + */ + public function convertDatetimeDataProvider(): array + { + return [ + ['2019-09-30T12:32:00.000Z', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000Z', true, '2019-09-30 19:32:00'], + ]; + } } diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/date.js b/app/code/Magento/Ui/view/base/web/js/form/element/date.js index ac28271e90a3b..1432372dd75a9 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/date.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/date.js @@ -107,6 +107,13 @@ define([ return this._super().observe(['shiftedValue']); }, + /** + * @inheritdoc + */ + getPreview: function () { + return this.shiftedValue(); + }, + /** * Prepares and sets date/time value that will be displayed * in the input field. diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js index 3f9c5b20d6215..cc69d990372c1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js @@ -9,8 +9,10 @@ define([ 'mageUtils', 'moment', - './column' -], function (utils, moment, Column) { + './column', + 'underscore', + 'moment-timezone-with-data' +], function (utils, moment, Column, _) { 'use strict'; return Column.extend({ @@ -20,9 +22,9 @@ define([ }, /** - * Overrides base method to normalize date format. + * Overrides base method to normalize date format * - * @returns {DateColumn} Chainable. + * @returns {DateColumn} Chainable */ initConfig: function () { this._super(); @@ -38,12 +40,15 @@ define([ * @returns {String} Formatted date. */ getLabel: function (value, format) { - var date; + var date = moment.utc(this._super()); if (this.storeLocale !== undefined) { moment.locale(this.storeLocale, utils.extend({}, this.calendarConfig)); } - date = moment(this._super()); + + if (!_.isUndefined(this.timeZone)) { + date = date.tz(this.timeZone); + } date = date.isValid() && value[this.index] ? date.format(format || this.dateFormat) : diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 98c3eb1c6f882..a6fae9df50c4d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -83,6 +83,10 @@ define([ component: 'Magento_Ui/js/grid/filters/range', rangeType: 'date' }, + datetimeRange: { + component: 'Magento_Ui/js/grid/filters/range', + rangeType: 'datetime' + }, textRange: { component: 'Magento_Ui/js/grid/filters/range', rangeType: 'text' diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js index ccfba8e98b6f4..1949234c89324 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js @@ -30,6 +30,14 @@ define([ dateFormat: 'MM/dd/YYYY', shiftedValue: 'filter' }, + datetime: { + component: 'Magento_Ui/js/form/element/date', + dateFormat: 'MM/dd/YYYY', + shiftedValue: 'filter', + options: { + showsTime: true + } + }, text: { component: 'Magento_Ui/js/form/element/abstract' }, From e9c704b9277e2567278992dabd14946e65c42f42 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 4 Oct 2019 10:12:06 +0300 Subject: [PATCH 03/15] MC-5233: DateTime product attributes support --- .../Attribute/Frontend/DatetimeTest.php | 2 +- .../Unit/Component/Filters/Type/DateTest.php | 21 ++++--- .../Ui/Test/Unit/Component/FiltersTest.php | 2 +- .../Eav/Model/Entity/AttributeTest.php | 55 ++++++++++++------- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php index c775548fc8c75..163f3d208e724 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php @@ -56,7 +56,7 @@ protected function setUp() /** * Test to retrieve attribute value - * + * * @param string $frontendInput * @param int $timeType * @dataProvider getValueDataProvider diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 7038a587be0b0..31d7ca92c5985 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -239,6 +239,7 @@ public function getPrepareDataProvider() * @param array $filterData * @param array $expectedCondition * @param MockObject $uiComponent + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processFilters( string $name, @@ -257,18 +258,22 @@ private function processFilters( $from = new \DateTime($filterData[$name]['from']); $to = new \DateTime($filterData[$name]['to']); $uiComponent->method('convertDatetime') - ->willReturnMap([ - [$filterData[$name]['from'], true, $from], - [$filterData[$name]['to'], true, $to], - ]); + ->willReturnMap( + [ + [$filterData[$name]['from'], true, $from], + [$filterData[$name]['to'], true, $to], + ] + ); } else { $from = new \DateTime($filterData[$name]['from']); $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); $uiComponent->method('convertDate') - ->willReturnMap([ - [$filterData[$name]['from'], 0, 0, 0, true, $from], - [$filterData[$name]['to'], 23, 59, 59, true, $to], - ]); + ->willReturnMap( + [ + [$filterData[$name]['from'], 0, 0, 0, true, $from], + [$filterData[$name]['to'], 23, 59, 59, true, $to], + ] + ); } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php index 19a1be69ca1d7..d4cf7f1af8d62 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php @@ -58,7 +58,7 @@ protected function setUp() /** * Test to Update filter component according to $component - * + * * @param string $filterType * @param string $filterName * @dataProvider updateDataProvider diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 2750e2a768aab..5df08762e29a7 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -3,15 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Eav\Model\Entity; -use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; -class AttributeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test EAV Entity attribute model + */ +class AttributeTest extends TestCase { /** * @var Attribute @@ -19,33 +24,33 @@ class AttributeTest extends \PHPUnit\Framework\TestCase private $attribute; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ private $objectManager; /** * @var ResolverInterface */ - private $_localeResolver; + private $localeResolver; /** - * {@inheritdoc} + * @inheritdoc */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->attribute = $this->objectManager->get(Attribute::class); - $this->_localeResolver = $this->objectManager->get(ResolverInterface::class); + $this->localeResolver = $this->objectManager->get(ResolverInterface::class); } /** - * {@inheritdoc} + * @inheritdoc */ protected function tearDown() { $this->attribute = null; $this->objectManager = null; - $this->_localeResolver = null; + $this->localeResolver = null; } /** @@ -56,11 +61,17 @@ protected function tearDown() * @dataProvider beforeSaveDataProvider * @throws */ - public function testBeforeSave($defaultValue, $backendType, $locale, $expected) - { + public function testBeforeSave( + string $defaultValue, + string $backendType, + string $frontendInput, + string $locale, + string $expected + ) { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->attribute->setFrontendInput($frontendInput); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->assertEquals($expected, $this->attribute->getDefaultValue()); @@ -74,13 +85,15 @@ public function testBeforeSave($defaultValue, $backendType, $locale, $expected) public function beforeSaveDataProvider() { return [ - ['21/07/18', 'datetime', 'en_AU', '2018-07-21 00:00:00'], - ['07/21/18', 'datetime', 'en_US', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'fr_FR', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'de_DE', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'uk_UA', '2018-07-21 00:00:00'], - ['100.50', 'decimal', 'en_US', '100.50'], - ['100,50', 'decimal', 'uk_UA', '100.5'], + ['21/07/18', 'datetime', 'date', 'en_AU', '2018-07-21 00:00:00'], + ['07/21/18', 'datetime', 'date', 'en_US', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'fr_FR', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'de_DE', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'uk_UA', '2018-07-21 00:00:00'], + ['100.50', 'decimal', 'decimal', 'en_US', '100.50'], + ['100,50', 'decimal', 'decimal', 'uk_UA', '100.5'], + ['07/21/2019 2:30 PM', 'datetime', 'datetime', 'en_US', '2019-07-21 21:30:00'], + ['21.07.2019 14:30', 'datetime', 'datetime', 'uk_UA', '2019-07-21 21:30:00'], ]; } @@ -90,13 +103,13 @@ public function beforeSaveDataProvider() * @param string $locale * @param string $expected * @dataProvider beforeSaveErrorDataDataProvider - * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedException LocalizedException */ public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $expected) { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->expectExceptionMessage($expected); From b6d071f9b57a784206730e1f3612fd13ffdabf4b Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Mon, 7 Oct 2019 16:26:34 +0300 Subject: [PATCH 04/15] MC-5233: DateTime product attributes support --- .../Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php | 2 +- app/code/Magento/Catalog/Ui/Component/ColumnFactory.php | 6 +++--- .../Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php | 2 +- .../Eav/Model/Entity/Attribute/Frontend/Datetime.php | 4 ++-- .../Magento/Ui/Component/Form/Element/DataType/Date.php | 8 ++++---- app/code/Magento/Ui/view/base/web/js/grid/columns/date.js | 4 ++-- .../testsuite/Magento/Eav/Model/Entity/AttributeTest.php | 3 +-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php index ddc7273432cb3..955ea259ec9a8 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php @@ -113,7 +113,7 @@ private function removeUnusedFields(): void $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; foreach ($fieldset->getElements() as $element) { /** @var AbstractElement $element */ - if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { + if (substr($element->getId(), 0, strlen('default_value')) === 'default_value') { $fieldsToRemove[] = $element->getId(); } } diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index c538273476b1f..bbcd9bbc9f03a 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -131,14 +131,14 @@ private function getDateConfig(ProductAttributeInterface $attribute): array $dateFormat = $isDatetime ? $this->timezone->getDateTimeFormat(\IntlDateFormatter::MEDIUM) : $this->timezone->getDateFormat(\IntlDateFormatter::MEDIUM); - $timeZone = $isDatetime + $timezone = $isDatetime ? $this->timezone->getConfigTimezone() : $this->timezone->getDefaultTimezone(); return [ - 'timeZone' => $timeZone, + 'timezone' => $timezone, 'dateFormat' => $dateFormat, - 'options' => [ 'showsTime' => $isDatetime], + 'options' => ['showsTime' => $isDatetime], ]; } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index e41b2390930f0..0c76feb2c94ec 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -957,7 +957,7 @@ private function customizeWysiwyg(ProductAttributeInterface $attribute, array $m * @param array $meta * @return array */ - private function customizeDatetimeAttribute(array $meta) + private function customizeDatetimeAttribute(array $meta): array { $meta['arguments']['data']['config']['options']['showsTime'] = 1; diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php index 8effd73d34af2..ea454fc0bcc74 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php @@ -44,12 +44,12 @@ public function getValue(\Magento\Framework\DataObject $object) $value = parent::getValue($object); if ($value) { - $timeType = $this->getAttribute()->getFrontendInput() === 'datetime' + $showTime = $this->getAttribute()->getFrontendInput() === 'datetime' ? \IntlDateFormatter::MEDIUM : \IntlDateFormatter::NONE; $data = $this->_localeDate->formatDateTime( new \DateTime($value), \IntlDateFormatter::MEDIUM, - $timeType + $showTime ); } diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 8ea6236e8e2e2..6bf1eacd161cc 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -127,20 +127,20 @@ public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcT * Convert given date to default (UTC) timezone * * @param string $date - * @param bool $setUtcTimeZone + * @param bool $setUtcTimezone * @return \DateTime|null */ - public function convertDatetime(string $date, bool $setUtcTimeZone = true): ?\DateTime + public function convertDatetime(string $date, bool $setUtcTimezone = true): ?\DateTime { try { $date = rtrim($date, 'Z'); $dateObj = new \DateTime($date, new \DateTimeZone($this->localeDate->getConfigTimezone())); //convert store date to default date in UTC timezone without DST - if ($setUtcTimeZone) { + if ($setUtcTimezone) { $dateObj->setTimezone(new \DateTimeZone('UTC')); } return $dateObj; - } catch (\Exception $e) { + } catch (\Throwable $e) { return null; } } diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js index cc69d990372c1..3179529e282c6 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js @@ -46,8 +46,8 @@ define([ moment.locale(this.storeLocale, utils.extend({}, this.calendarConfig)); } - if (!_.isUndefined(this.timeZone)) { - date = date.tz(this.timeZone); + if (!_.isUndefined(this.timezone)) { + date = date.tz(this.timezone); } date = date.isValid() && value[this.index] ? diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 5df08762e29a7..5b05308c786b5 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -7,7 +7,6 @@ namespace Magento\Eav\Model\Entity; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Locale\ResolverInterface; use Magento\Framework\ObjectManagerInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -103,7 +102,7 @@ public function beforeSaveDataProvider() * @param string $locale * @param string $expected * @dataProvider beforeSaveErrorDataDataProvider - * @expectedException LocalizedException + * @expectedException \Magento\Framework\Exception\LocalizedException */ public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $expected) { From 5a014040afd71105af6ba32737ceba8436d6e640 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 8 Oct 2019 15:42:11 +0300 Subject: [PATCH 05/15] MC-5233: DateTime product attributes support --- .../AdminProductAttributeActionGroup.xml | 11 ++++ .../Test/Mftf/Data/ProductAttributeData.xml | 4 ++ .../AdminCreateProductAttributeSection.xml | 1 + ...dminCreateDatetimeProductAttributeTest.xml | 61 +++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 3e54574c553e3..4081a362153bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -370,4 +370,15 @@ <checkOption selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault(row)}}" stepKey="setAsDefault" after="fillStoreView"/> </actionGroup> + + <!-- Go to advanced attribute properties --> + <actionGroup name="AdminNavigateToProductAttributeAdvancedSection"> + <annotations> + <description>Navigate and open Advanced Attribute Properties section on product attribute page</description> + </annotations> + + <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToSection"/> + <click selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="openSection"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutSection"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 6614fa4b5dbeb..38a273fe9fb41 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -309,6 +309,10 @@ <data key="frontend_input">date</data> <data key="is_required_admin">No</data> </entity> + <entity name="datetimeProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">datetime</data> + <data key="is_required_admin">No</data> + </entity> <entity name="priceProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> <data key="frontend_input">date</data> <data key="is_required_admin">No</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 462f721913b9c..1236e07a2f278 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -93,6 +93,7 @@ <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> + <element name="DefaultValueDatetime" type="textarea" selector="#default_value_datetime"/> <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> <element name="Scope" type="select" selector="#is_global"/> <element name="UniqueValue" type="select" selector="#is_unique"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml new file mode 100644 index 0000000000000..5d1f0716f33f7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDatetimeProductAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Datetime product attributes support"/> + <title value="Datetime product attribute type is supported"/> + <description value="Admin should be able to create datetime product attribute"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-21451"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <argument name="ProductAttribute" value="datetimeProductAttribute"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!-- Set attribute properties --> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" + userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" + userInput="{{datetimeProductAttribute.frontend_input}}" stepKey="fillInputType"/> + <actionGroup ref="AdminNavigateToProductAttributeAdvancedSection" stepKey="goToAdvancedSectionNew"/> + <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" + userInput="{{datetimeProductAttribute.attribute_code}}" + stepKey="fillAttributeCode"/> + <!-- Generate and set a default value --> + <generateDate date="now" format="m/j/y g:i A" stepKey="generateDefaultValue"/> + <fillField selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" + userInput="{$generateDefaultValue}" + stepKey="fillDefaultValue"/> + <!-- Save the new product attribute --> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveAttribute"/> + <waitForPageLoad stepKey="waitForGridPageLoadAfterSaveAttribute"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" + stepKey="waitForSuccessMessage"/> + <!-- Navigate to created product attribute --> + <actionGroup ref="navigateToCreatedProductAttribute" stepKey="navigateToAttribute"> + <argument name="ProductAttribute" value="datetimeProductAttribute"/> + </actionGroup> + <!-- Check the saved date and time default value --> + <actionGroup ref="AdminNavigateToProductAttributeAdvancedSection" stepKey="goToAdvancedSection"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" stepKey="scrollToDefaultValue"/> + <seeInField userInput="{$generateDefaultValue}" + selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" + stepKey="checkDefaultValue"/> + </test> +</tests> From a9f42f9ed40aef0aaaefd5754d575fcb80f4fc7d Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 8 Oct 2019 15:43:47 +0300 Subject: [PATCH 06/15] MC-5233: DateTime product attributes support --- app/code/Magento/Ui/view/base/web/js/grid/columns/date.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js index 3179529e282c6..88959cda7499d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js @@ -40,12 +40,14 @@ define([ * @returns {String} Formatted date. */ getLabel: function (value, format) { - var date = moment.utc(this._super()); + var date; if (this.storeLocale !== undefined) { moment.locale(this.storeLocale, utils.extend({}, this.calendarConfig)); } + date = moment.utc(this._super()); + if (!_.isUndefined(this.timezone)) { date = date.tz(this.timezone); } From 1a780d2a113b0deba056fc983e2660aa4d21ff58 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Wed, 9 Oct 2019 14:42:37 +0300 Subject: [PATCH 07/15] MC-5233: DateTime product attributes support --- .../AdminProductAttributeActionGroup.xml | 19 +++++- .../Test/Mftf/Data/ProductAttributeData.xml | 2 +- .../Section/AdminProductGridFilterSection.xml | 2 + ...dminCreateDatetimeProductAttributeTest.xml | 37 ++++------- ...SimpleProductWithDatetimeAttributeTest.xml | 65 +++++++++++++++++++ 5 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 4081a362153bb..bc99e8d3f57e9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -345,6 +345,23 @@ <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{{date}}"/> </actionGroup> + <!-- Inputs datetime default value and attribute code--> + <actionGroup name="CreateProductAttributeWithDatetimeField" extends="createProductAttribute" insertAfter="checkRequired"> + <annotations> + <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Date and Time Field).</description> + </annotations> + <arguments> + <argument name="date" type="string"/> + </arguments> + + <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToAdvancedSection"/> + <click selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="openAdvancedSection"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutAdvancedSection"/> + <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" stepKey="scrollToDefaultField"/> + <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" userInput="{{date}}"/> + </actionGroup> + <!-- Creates dropdown option at row without saving--> <actionGroup name="createAttributeDropdownNthOption"> <annotations> @@ -377,7 +394,7 @@ <description>Navigate and open Advanced Attribute Properties section on product attribute page</description> </annotations> - <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToSection"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToSection"/> <click selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="openSection"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutSection"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 38a273fe9fb41..b24a765706f2d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -309,7 +309,7 @@ <data key="frontend_input">date</data> <data key="is_required_admin">No</data> </entity> - <entity name="datetimeProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <entity name="DatetimeProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> <data key="frontend_input">datetime</data> <data key="is_required_admin">No</data> </entity> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index 3b6f24c0f259d..4e86f14611c24 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -36,5 +36,7 @@ <element name="productCount" type="text" selector="#catalog_category_products-total-count"/> <element name="productPerPage" type="select" selector="#catalog_category_products_page-limit"/> <element name="storeViewDropdown" type="text" selector="//select[@name='store_id']/option[contains(.,'{{storeView}}')]" parameterized="true"/> + <element name="inputByCodeRangeFrom" type="input" selector="input.admin__control-text[name='{{code}}[from]']" parameterized="true"/> + <element name="inputByCodeRangeTo" type="input" selector="input.admin__control-text[name='{{code}}[to]']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml index 5d1f0716f33f7..d084eb962c5da 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -14,44 +14,31 @@ <description value="Admin should be able to create datetime product attribute"/> <severity value="CRITICAL"/> <testCaseId value="MC-21451"/> - <group value="Catalog"/> + <group value="catalog"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> </before> <after> <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> - <argument name="ProductAttribute" value="datetimeProductAttribute"/> + <argument name="ProductAttribute" value="DatetimeProductAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> - - <amOnPage url="{{ProductAttributePage.url}}" stepKey="navigateToNewProductAttributePage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <!-- Set attribute properties --> - <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" - userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillDefaultLabel"/> - <selectOption selector="{{AttributePropertiesSection.InputType}}" - userInput="{{datetimeProductAttribute.frontend_input}}" stepKey="fillInputType"/> - <actionGroup ref="AdminNavigateToProductAttributeAdvancedSection" stepKey="goToAdvancedSectionNew"/> - <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" - userInput="{{datetimeProductAttribute.attribute_code}}" - stepKey="fillAttributeCode"/> - <!-- Generate and set a default value --> + <!-- Generate the datetime default value --> <generateDate date="now" format="m/j/y g:i A" stepKey="generateDefaultValue"/> - <fillField selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" - userInput="{$generateDefaultValue}" - stepKey="fillDefaultValue"/> - <!-- Save the new product attribute --> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveAttribute"/> - <waitForPageLoad stepKey="waitForGridPageLoadAfterSaveAttribute"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" - stepKey="waitForSuccessMessage"/> + <!-- Create new datetime product attribute --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForPageLoadAttributes"/> + <actionGroup ref="CreateProductAttributeWithDatetimeField" stepKey="createAttribute"> + <argument name="attribute" value="DatetimeProductAttribute"/> + <argument name="date" value="{$generateDefaultValue}"/> + </actionGroup> <!-- Navigate to created product attribute --> <actionGroup ref="navigateToCreatedProductAttribute" stepKey="navigateToAttribute"> - <argument name="ProductAttribute" value="datetimeProductAttribute"/> + <argument name="ProductAttribute" value="DatetimeProductAttribute"/> </actionGroup> - <!-- Check the saved date and time default value --> + <!-- Check the saved datetime default value --> <actionGroup ref="AdminNavigateToProductAttributeAdvancedSection" stepKey="goToAdvancedSection"/> <scrollTo selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" stepKey="scrollToDefaultValue"/> <seeInField userInput="{$generateDefaultValue}" diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml new file mode 100644 index 0000000000000..a21c5a84447dc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductWithDatetimeAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Datetime product attributes support"/> + <title value="Set datetime attribute to product"/> + <description value="Admin should be able to specify datetime attribute to product and find by them in product grid"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-21461"/> + <group value="catalog"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createDatetimeAttribute" stepKey="DatetimeProductAttribute"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Generate default value --> + <generateDate date="now" format="m/j/Y g:i A" stepKey="generateDefaultValue"/> + <generateDate date="now" format="M j, Y g:i:00 A" stepKey="generateDefaultGridValue"/> + <generateDate date="+1 minute" format="m/j/Y g:i A" stepKey="generateFilterToDate"/> + <!-- Create new datetime product attribute --> + <createData entity="DatetimeProductAttribute" stepKey="createDatetimeAttribute"> + <field key="default_value">{$generateDefaultValue}</field> + </createData> + <!-- Open the new simple product page --> + <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductPage"/> + <actionGroup ref="fillMainProductForm" stepKey="fillDefaultProductFields"/> + <!-- Add datetime attribute --> + <actionGroup ref="addProductAttributeInProductModal" stepKey="addDatetimeAttribute"> + <argument name="attributeCode" value="$createDatetimeAttribute.attribute_code$"/> + </actionGroup> + <!-- Check default value --> + <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="goToAttributesSection"/> + <click selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="openAttributesSection"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" stepKey="waitForSlideOutAttributes"/> + <seeInField selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" userInput="$generateDefaultValue" stepKey="checkDefaultValue"/> + <!-- Save the product --> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <!-- Check datetime grid filter --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeFrom($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateDefaultValue}" stepKey="fillProductDatetimeFromFilter"/> + <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeTo($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateFilterToDate}" stepKey="fillProductDatetimeToFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{_defaultProduct.name}}" stepKey="checkAppliedDatetimeFilter"/> + <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="{$generateDefaultGridValue}" stepKey="checkDefaultValueInGrid"/> + </test> +</tests> From 7379bc9a62df7af85003a26bf94f72bee16989cc Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 10 Oct 2019 11:16:11 +0300 Subject: [PATCH 08/15] MC-5233: DateTime product attributes support --- .../AdminProductAttributeActionGroup.xml | 10 +++++----- .../Section/AdminCreateProductAttributeSection.xml | 2 +- .../AdminCreateDatetimeProductAttributeTest.xml | 5 +++-- ...reateSimpleProductWithDatetimeAttributeTest.xml | 14 +++++++------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index bc99e8d3f57e9..6deec50a831b4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -355,11 +355,11 @@ </arguments> <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToAdvancedSection"/> - <click selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="openAdvancedSection"/> + <conditionalClick selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" dependentSelector="{{AdvancedAttributePropertiesSection.AttributeCode}}" visible="false" stepKey="openAdvancedSection"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutAdvancedSection"/> - <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> - <scrollTo selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" stepKey="scrollToDefaultField"/> - <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" userInput="{{date}}"/> + <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}" stepKey="fillCode"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" stepKey="scrollToDefaultField"/> + <fillField selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" userInput="{{date}}" stepKey="fillDefaultValue"/> </actionGroup> <!-- Creates dropdown option at row without saving--> @@ -395,7 +395,7 @@ </annotations> <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToSection"/> - <click selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="openSection"/> + <conditionalClick selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" dependentSelector="{{AdvancedAttributePropertiesSection.AttributeCode}}" visible="false" stepKey="openSection"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutSection"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 1236e07a2f278..e6b86c7a9172b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -93,7 +93,7 @@ <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> - <element name="DefaultValueDatetime" type="textarea" selector="#default_value_datetime"/> + <element name="defaultValueDatetime" type="textarea" selector="#default_value_datetime"/> <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> <element name="Scope" type="select" selector="#is_global"/> <element name="UniqueValue" type="select" selector="#is_unique"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml index d084eb962c5da..e46114ff752f6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -23,6 +23,7 @@ <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="DatetimeProductAttribute"/> </actionGroup> + <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Generate the datetime default value --> @@ -40,9 +41,9 @@ </actionGroup> <!-- Check the saved datetime default value --> <actionGroup ref="AdminNavigateToProductAttributeAdvancedSection" stepKey="goToAdvancedSection"/> - <scrollTo selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" stepKey="scrollToDefaultValue"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" stepKey="scrollToDefaultValue"/> <seeInField userInput="{$generateDefaultValue}" - selector="{{AdvancedAttributePropertiesSection.DefaultValueDatetime}}" + selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" stepKey="checkDefaultValue"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml index a21c5a84447dc..019baa4ac153d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -22,10 +22,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <deleteData createDataKey="createDatetimeAttribute" stepKey="DatetimeProductAttribute"/> + <deleteData createDataKey="createDatetimeAttribute" stepKey="deleteDatetimeAttribute"/> <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersOnProductIndexPage"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -53,13 +54,12 @@ <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Check datetime grid filter --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openProductFilters"/> <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeFrom($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateDefaultValue}" stepKey="fillProductDatetimeFromFilter"/> <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeTo($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateFilterToDate}" stepKey="fillProductDatetimeToFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{_defaultProduct.name}}" stepKey="checkAppliedDatetimeFilter"/> - <see selector="{{AdminProductGridSection.productGridContentsOnRow('1')}}" userInput="{$generateDefaultGridValue}" stepKey="checkDefaultValueInGrid"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminDataGridTableSection.gridCell('1', 'Name')}}" userInput="{{_defaultProduct.name}}" stepKey="checkAppliedDatetimeFilter"/> + <see selector="{{AdminDataGridTableSection.rowTemplateStrict(_defaultProduct.name)}}" userInput="{$generateDefaultGridValue}" stepKey="checkDefaultValueInGrid"/> </test> </tests> From 9110f821c258e91431596d835c0b811083d327f3 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 10 Oct 2019 12:44:11 +0300 Subject: [PATCH 09/15] MC-5233: DateTime product attributes support --- .../Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 1 + .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 6deec50a831b4..389330fd7deb5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -17,6 +17,7 @@ </arguments> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.attribute_code}}" stepKey="setAttributeCode"/> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index e6b86c7a9172b..31c4f5198a8b0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -93,7 +93,7 @@ <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> - <element name="defaultValueDatetime" type="textarea" selector="#default_value_datetime"/> + <element name="defaultValueDatetime" type="text" selector="#default_value_datetime"/> <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> <element name="Scope" type="select" selector="#is_global"/> <element name="UniqueValue" type="select" selector="#is_unique"/> From c5a1666a93e10806f1861a7df958a6f4de5c4a99 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 10 Oct 2019 12:49:48 +0300 Subject: [PATCH 10/15] MC-5233: DateTime product attributes support --- .../Test/Unit/Ui/Component/ColumnFactoryTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index f002173de7996..4e6730b553307 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -56,7 +56,7 @@ class ColumnFactoryTest extends TestCase /** * @var TimezoneInterface|MockObject */ - private $timeZone; + private $timezone; /** * @inheritdoc @@ -80,13 +80,13 @@ protected function setUp(): void $this->column = $this->getMockForAbstractClass(ColumnInterface::class); $this->uiComponentFactory->method('create') ->willReturn($this->column); - $this->timeZone = $this->createMock(TimezoneInterface::class); + $this->timezone = $this->createMock(TimezoneInterface::class); $this->columnFactory = $this->objectManager->getObject( ColumnFactory::class, [ 'componentFactory' => $this->uiComponentFactory, - 'timezone' => $this->timeZone, + 'timezone' => $this->timezone, ] ); } @@ -203,7 +203,7 @@ public function testCreateDateColumn( 'visible' => true, 'filter' => 'dateRange', 'component' => 'Magento_Ui/js/grid/columns/date', - 'timeZone' => $expectedTimezone, + 'timezone' => $expectedTimezone, 'dateFormat' => $expectedDateFormat, 'options' => [ 'showsTime' => $showsTime @@ -224,15 +224,15 @@ public function testCreateDateColumn( $this->attribute->method('getFrontendInput') ->willReturn($frontendInput); - $this->timeZone->method('getDateFormat') + $this->timezone->method('getDateFormat') ->with(\IntlDateFormatter::MEDIUM) ->willReturn($dateFormat); - $this->timeZone->method('getDateTimeFormat') + $this->timezone->method('getDateTimeFormat') ->with(\IntlDateFormatter::MEDIUM) ->willReturn($dateTimeFormat); - $this->timeZone->method('getDefaultTimezone') + $this->timezone->method('getDefaultTimezone') ->willReturn($defaultTimezone); - $this->timeZone->method('getConfigTimezone') + $this->timezone->method('getConfigTimezone') ->willReturn($configTimezone); $this->uiComponentFactory->expects($this->once()) From e9f0b8a117f09134ed15652e551266a237de3c6e Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 10 Oct 2019 14:12:39 +0300 Subject: [PATCH 11/15] MC-5233: DateTime product attributes support --- .../Test/Mftf/Section/AdminCreateProductAttributeSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 31c4f5198a8b0..0934e39dcb062 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -93,7 +93,7 @@ <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> - <element name="defaultValueDatetime" type="text" selector="#default_value_datetime"/> + <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> <element name="Scope" type="select" selector="#is_global"/> <element name="UniqueValue" type="select" selector="#is_unique"/> From 27e2049537f34d8ce0748e2f47afc9f95ffed1b1 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 10 Oct 2019 17:09:16 +0300 Subject: [PATCH 12/15] MC-5233: DateTime product attributes support --- app/code/Magento/Catalog/Ui/Component/Listing/Columns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php index d7b9bc3846f49..1e056d9f8d517 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php @@ -14,7 +14,7 @@ class Columns extends \Magento\Ui\Component\Listing\Columns { /** - * Default columns max order + * Default columns max order value */ const DEFAULT_COLUMNS_MAX_ORDER = 100; From ee1807bbcd027f37f0d3460710c713f9793b5395 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Fri, 11 Oct 2019 13:34:14 +0300 Subject: [PATCH 13/15] MC-5233: DateTime product attributes support --- .../AdminCreateSimpleProductWithDatetimeAttributeTest.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml index 019baa4ac153d..6772e95b6ec27 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -45,13 +45,15 @@ <actionGroup ref="addProductAttributeInProductModal" stepKey="addDatetimeAttribute"> <argument name="attributeCode" value="$createDatetimeAttribute.attribute_code$"/> </actionGroup> + <!-- Flush config cache to reset product attributes in attribute set --> + <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> + <!-- Save the product --> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Check default value --> <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="goToAttributesSection"/> <click selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="openAttributesSection"/> <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" stepKey="waitForSlideOutAttributes"/> <seeInField selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" userInput="$generateDefaultValue" stepKey="checkDefaultValue"/> - <!-- Save the product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!-- Check datetime grid filter --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> From 393ef91131c4649784a887abe42cb242921d94e8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Tue, 10 Dec 2019 10:11:19 +0200 Subject: [PATCH 14/15] MC-5233: DateTime product attributes support --- .../Unit/Ui/Component/ColumnFactoryTest.php | 3 ++- .../Product/Form/Modifier/EavTest.php | 3 ++- .../Catalog/Ui/Component/ColumnFactory.php | 2 +- .../Product/Form/Modifier/Eav.php | 2 +- .../catalog/product/attribute/js.phtml | 24 ++++++++++++------- .../Test/Unit/Model/Entity/AttributeTest.php | 23 +++++++++++------- .../Component/Form/Element/DataType/Date.php | 2 +- .../Unit/Component/Filters/Type/DateTest.php | 2 +- 8 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 78e241100dc3b..b3acaa4b8bbed 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -19,7 +19,7 @@ use PHPUnit\Framework\TestCase; /** - * ColumnFactory test. + * Test to Create columns factory on product grid page */ class ColumnFactoryTest extends TestCase { @@ -206,6 +206,7 @@ public function testCreateDateColumn( 'component' => 'Magento_Ui/js/grid/columns/date', 'timezone' => $expectedTimezone, 'dateFormat' => $expectedDateFormat, + '__disableTmpl' => ['label' => true], 'options' => [ 'showsTime' => $showsTime ] diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 6221704d51112..91e22407acc43 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -41,7 +41,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class EavTest + * Class to test Data provider for eav attributes on product page * * @method Eav getModel * @SuppressWarnings(PHPMD.TooManyFields) @@ -692,6 +692,7 @@ public function setupAttributeMetaDataProvider() 'scopeLabel' => '', 'globalScope' => false, 'sortOrder' => 0, + '__disableTmpl' => ['label' => true, 'code' => true] ], 'locked' => false, 'frontendInput' => 'datetime', diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 61bc518756ba6..b902e741c006c 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -15,7 +15,7 @@ use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** - * Column Factory + * Create columns factory on product grid page * * @api * @since 100.0.2 diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index bc5bee64912c7..25e816f79639a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -40,7 +40,7 @@ use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory as AttributeCollectionFactory; /** - * Class Eav + * Data provider for eav attributes on product page * * @api * diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index 212a345f4bcbc..64384ac391a8d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Catalog\Helper\Data; // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> @@ -63,7 +64,12 @@ function bindAttributeInputType() { checkOptionsPanelVisibility(); switchDefaultValueField(); - if($('frontend_input') && ($('frontend_input').value=='boolean' || $('frontend_input').value=='select' || $('frontend_input').value=='multiselect' || $('frontend_input').value=='price')){ + if ($('frontend_input') + && ($('frontend_input').value=='boolean' + || $('frontend_input').value=='select' + || $('frontend_input').value=='multiselect' + || $('frontend_input').value=='price') + ){ if($('is_filterable') && !$('is_filterable').getAttribute('readonly')){ $('is_filterable').disabled = false; } @@ -75,8 +81,7 @@ function bindAttributeInputType() if($('backend_type').options[i].value=='int') $('backend_type').selectedIndex = i; } } - } - else { + } else { if($('is_filterable')){ $('is_filterable').selectedIndex=0; $('is_filterable').disabled = true; @@ -203,21 +208,22 @@ function switchDefaultValueField() setRowVisibility('frontend_class', false); break; - <?php foreach ($this->helper(Magento\Catalog\Helper\Data::class)->getAttributeHiddenFields() as $type => $fields) :?> + <?php // phpcs:ignore Magento2.Templates.ThisInTemplate ?> + <?php foreach ($this->helper(Data::class)->getAttributeHiddenFields() as $type => $fields): ?> case '<?= $block->escapeJs($type) ?>': var isFrontTabHidden = false; - <?php foreach ($fields as $one) :?> - <?php if ($one == '_front_fieldset') :?> + <?php foreach ($fields as $one): ?> + <?php if ($one == '_front_fieldset'): ?> getFrontTab().hide(); isFrontTabHidden = true; - <?php elseif ($one == '_default_value') :?> + <?php elseif ($one == '_default_value'): ?> defaultValueTextVisibility = defaultValueTextareaVisibility = defaultValueDateVisibility = defaultValueYesnoVisibility = false; - <?php elseif ($one == '_scope') :?> + <?php elseif ($one == '_scope'): ?> scopeVisibility = false; - <?php else :?> + <?php else: ?> setRowVisibility('<?= $block->escapeJs($one) ?>', false); <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 7aa5bca00f0b6..ae4ae7ee707e3 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -5,14 +5,21 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\Entity\Attribute\FrontendLabel; +use Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class AttributeTest. + * Test for EAV Entity attribute model */ -class AttributeTest extends \PHPUnit\Framework\TestCase +class AttributeTest extends TestCase { /** * Attribute model to be tested - * @var \Magento\Eav\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ protected $_model; @@ -21,7 +28,7 @@ class AttributeTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->_model = $this->createPartialMock(\Magento\Eav\Model\Entity\Attribute::class, ['__wakeup']); + $this->_model = $this->createPartialMock(Attribute::class, ['__wakeup']); } /** @@ -132,7 +139,7 @@ public function testGetFrontendLabels() { $attributeId = 1; $storeLabels = ['test_attribute_store1']; - $frontendLabelFactory = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory::class) + $frontendLabelFactory = $this->getMockBuilder(FrontendLabelFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -144,15 +151,15 @@ public function testGetFrontendLabels() '_resource' => $resource, 'frontendLabelFactory' => $frontendLabelFactory, ]; - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $objectManager->getObject(\Magento\Eav\Model\Entity\Attribute::class, $arguments); + $objectManager = new ObjectManager($this); + $this->_model = $objectManager->getObject(Attribute::class, $arguments); $this->_model->setAttributeId($attributeId); $resource->expects($this->once()) ->method('getStoreLabelsByAttributeId') ->with($attributeId) ->willReturn($storeLabels); - $frontendLabel = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabel::class) + $frontendLabel = $this->getMockBuilder(FrontendLabel::class) ->setMethods(['setStoreId', 'setLabel']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 6bf1eacd161cc..ef2df77e7daff 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -11,7 +11,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** - * Class Date + * UI component date type */ class Date extends AbstractDataType { diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 31d7ca92c5985..20af2627fbb04 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\MockObject\MockObject; /** - * Class DateTest + * Test for Date grid filter functionality */ class DateTest extends \PHPUnit\Framework\TestCase { From 32fb4d3b02ebc1529b29a2158e7ccf59cd4384f8 Mon Sep 17 00:00:00 2001 From: Serhii Balko <serhii.balko@transoftgroup.com> Date: Thu, 12 Dec 2019 09:38:43 +0200 Subject: [PATCH 15/15] MC-5233: DateTime product attributes support --- .../CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 3b60e4b09de28..1cec03a4c765e 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -308,6 +308,10 @@ <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/>