diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000000000..10589b97ea9b3
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,68 @@
+# Configuration for probot-stale - https://github.com/probot/stale
+
+# Number of days of inactivity before an Issue or Pull Request becomes stale
+ daysUntilStale: 76
+
+# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
+# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
+daysUntilClose: 14
+
+# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
+onlyLabels: []
+
+# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
+exemptLabels:
+ - "Priority: P0"
+ - "Priority: P1"
+ - "Priority: P2"
+ - "Progress: dev in progress"
+ - "Progress: PR in progress"
+ - "Progress: done"
+ - "B2B: GraphQL"
+ - "Progress: PR Created"
+ - "PAP"
+ - "Project: Login as Customer"
+ - "Project: GraphQL"
+
+# Set to true to ignore issues in a project (defaults to false)
+exemptProjects: false
+
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones: false
+
+# Set to true to ignore issues with an assignee (defaults to false)
+exemptAssignees: false
+
+# Label to use when marking as stale
+staleLabel: "stale issue"
+
+# Comment to post when marking as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed after 14 days if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when removing the stale label.
+# unmarkComment: >
+# Your comment here.
+
+# Comment to post when closing a stale Issue or Pull Request.
+# closeComment: >
+# Your comment here.
+
+# Limit the number of actions per hour, from 1-30. Default is 30
+limitPerRun: 30
+
+# Limit to only `issues` or `pulls`
+only: issues
+
+# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
+# pulls:
+# daysUntilStale: 30
+# markComment: >
+# This pull request has been automatically marked as stale because it has not had
+# recent activity. It will be closed if no further activity occurs. Thank you
+# for your contributions.
+
+# issues:
+# exemptLabels:
+# - confirmed
diff --git a/app/code/Magento/AsynchronousOperations/etc/db_schema.xml b/app/code/Magento/AsynchronousOperations/etc/db_schema.xml
index 5d49d71ee46b0..ab482d2e2c761 100644
--- a/app/code/Magento/AsynchronousOperations/etc/db_schema.xml
+++ b/app/code/Magento/AsynchronousOperations/etc/db_schema.xml
@@ -34,7 +34,7 @@
-
mergeDataProviderPrototype;
$categoryId = $category->getId();
foreach ($category->getStoreIds() as $storeId) {
- $category->setStoreId($storeId);
if (!$this->isGlobalScope($storeId)
&& $this->isOverrideUrlsForStore($storeId, $categoryId, $overrideStoreUrls)
) {
+ $category = clone $category; // prevent undesired side effects on original object
+ $category->setStoreId($storeId);
$this->updateCategoryUrlForStore($storeId, $category);
$mergeDataProvider->merge($this->generateForSpecificStoreView($storeId, $category, $rootCategoryId));
}
diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryUrlRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryUrlRewriteGeneratorTest.php
index 62a9699b3988d..1626cca6b7ae7 100644
--- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryUrlRewriteGeneratorTest.php
+++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/CategoryUrlRewriteGeneratorTest.php
@@ -158,6 +158,7 @@ public function testGenerationForGlobalScope()
],
$this->categoryUrlRewriteGenerator->generate($this->category, false, $categoryId)
);
+ $this->assertEquals(0, $this->category->getStoreId(), 'Store ID should not have been modified');
}
/**
diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_sidebar_item_renderers.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_sidebar_item_renderers.xml
index 1b9bad3d81c65..1cf965e83718a 100644
--- a/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_sidebar_item_renderers.xml
+++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_cart_sidebar_item_renderers.xml
@@ -21,7 +21,7 @@
-
-
-
- uiComponent
+ - Magento_Checkout/js/view/cart-item-renderer
-
- defaultRenderer
- Magento_Checkout/minicart/item/default
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/cart-item-renderer.js b/app/code/Magento/Checkout/view/frontend/web/js/view/cart-item-renderer.js
new file mode 100644
index 0000000000000..e1568efc3d6c8
--- /dev/null
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/cart-item-renderer.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'uiComponent'
+], function (Component) {
+ 'use strict';
+
+ return Component.extend({
+ /**
+ * Prepare the product name value to be rendered as HTML
+ *
+ * @param {String} productName
+ * @return {String}
+ */
+ getProductNameUnsanitizedHtml: function (productName) {
+ // product name has already escaped on backend
+ return productName;
+ },
+
+ /**
+ * Prepare the given option value to be rendered as HTML
+ *
+ * @param {String} optionValue
+ * @return {String}
+ */
+ getOptionValueUnsanitizedHtml: function (optionValue) {
+ // option value has already escaped on backend
+ return optionValue;
+ }
+ });
+});
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
index 053b15b4ad343..b15b7952d2cbd 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html
@@ -24,10 +24,10 @@
-
+
-
+
@@ -42,10 +42,10 @@
-
+
-
+
diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
index 5321dfecba182..d50541504662d 100644
--- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
+++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js
@@ -78,7 +78,7 @@ define([
var parameters;
sectionNames = sectionConfig.filterClientSideSections(sectionNames);
- parameters = _.isArray(sectionNames) ? {
+ parameters = _.isArray(sectionNames) && sectionNames.indexOf('*') < 0 ? {
sections: sectionNames.join(',')
} : [];
parameters['force_new_section_timestamp'] = forceNewSectionTimestamp;
diff --git a/app/code/Magento/Directory/etc/zip_codes.xml b/app/code/Magento/Directory/etc/zip_codes.xml
index 3c540f7ce0ffd..14d250656d28c 100644
--- a/app/code/Magento/Directory/etc/zip_codes.xml
+++ b/app/code/Magento/Directory/etc/zip_codes.xml
@@ -64,7 +64,7 @@
- ^[0-9]{5}$
+ ^[0-9]{8}$
^[0-9]{5}\-[0-9]{3}$
diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInCategoryFilterTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInCategoryFilterTest.xml
index a495e2ff07e6a..df3ac35c0bfcd 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInCategoryFilterTest.xml
+++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInCategoryFilterTest.xml
@@ -27,7 +27,7 @@
-
+
@@ -37,6 +37,8 @@
+
+
@@ -50,6 +52,8 @@
+
+
diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkProductGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkProductGridTest.xml
index db7942d4c53bf..d2e8574ef7033 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkProductGridTest.xml
+++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkProductGridTest.xml
@@ -29,6 +29,8 @@
+
+
@@ -42,6 +44,10 @@
+
+
+
+
@@ -51,6 +57,8 @@
+
+
@@ -64,15 +72,14 @@
-
+
-
+
-
diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Config.php b/app/code/Magento/MediaGalleryRenditions/Model/Config.php
index d1a48904d1f13..6622ef36dffd7 100644
--- a/app/code/Magento/MediaGalleryRenditions/Model/Config.php
+++ b/app/code/Magento/MediaGalleryRenditions/Model/Config.php
@@ -18,7 +18,8 @@
class Config
{
private const TABLE_CORE_CONFIG_DATA = 'core_config_data';
- private const XML_PATH_ENABLED = 'system/media_gallery/enabled';
+ private const XML_PATH_MEDIA_GALLERY_ENABLED = 'system/media_gallery/enabled';
+ private const XML_PATH_ENABLED = 'system/media_gallery_renditions/enabled';
private const XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH = 'system/media_gallery_renditions/width';
private const XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH = 'system/media_gallery_renditions/height';
@@ -49,6 +50,16 @@ public function __construct(
*
* @return bool
*/
+ public function isMediaGalleryEnabled(): bool
+ {
+ return $this->scopeConfig->isSetFlag(self::XML_PATH_MEDIA_GALLERY_ENABLED);
+ }
+
+ /**
+ * Should the renditions be inserted in the content instead of original image
+ *
+ * @return bool
+ */
public function isEnabled(): bool
{
return $this->scopeConfig->isSetFlag(self::XML_PATH_ENABLED);
diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php b/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php
index ec2012c528ef1..2fc49950463ea 100644
--- a/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php
+++ b/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php
@@ -91,7 +91,7 @@ public function beforeExecute(
$storeId
];
- if (!$this->config->isEnabled()) {
+ if (!$this->config->isEnabled() || !$this->config->isMediaGalleryEnabled()) {
return $arguments;
}
diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php
index 9cf969c16782f..6fcb37398f3ad 100644
--- a/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php
+++ b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php
@@ -8,6 +8,7 @@
namespace Magento\MediaGalleryRenditions\Plugin;
use Magento\Framework\App\Config\Value;
+use Magento\MediaGalleryRenditions\Model\Config;
use Magento\MediaGalleryRenditions\Model\Queue\ScheduleRenditionsUpdate;
/**
@@ -15,6 +16,7 @@
*/
class UpdateRenditionsOnConfigChange
{
+ private const XML_PATH_MEDIA_GALLERY_RENDITIONS_ENABLE_PATH = 'system/media_gallery_renditions/enabled';
private const XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH = 'system/media_gallery_renditions/width';
private const XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH = 'system/media_gallery_renditions/height';
@@ -24,10 +26,17 @@ class UpdateRenditionsOnConfigChange
private $scheduleRenditionsUpdate;
/**
+ * @var Config
+ */
+ private $config;
+
+ /**
+ * @param Config $config
* @param ScheduleRenditionsUpdate $scheduleRenditionsUpdate
*/
- public function __construct(ScheduleRenditionsUpdate $scheduleRenditionsUpdate)
+ public function __construct(Config $config, ScheduleRenditionsUpdate $scheduleRenditionsUpdate)
{
+ $this->config = $config;
$this->scheduleRenditionsUpdate = $scheduleRenditionsUpdate;
}
@@ -41,7 +50,13 @@ public function __construct(ScheduleRenditionsUpdate $scheduleRenditionsUpdate)
*/
public function afterSave(Value $config, Value $result): Value
{
- if ($this->isRenditionsValue($result) && $result->isValueChanged()) {
+ if ($this->isRenditionsEnabled($result)) {
+ $this->scheduleRenditionsUpdate->execute();
+
+ return $result;
+ }
+
+ if ($this->config->isEnabled() && $this->isRenditionsValue($result) && $result->isValueChanged()) {
$this->scheduleRenditionsUpdate->execute();
}
@@ -59,4 +74,17 @@ private function isRenditionsValue(Value $value): bool
return $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH
|| $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH;
}
+
+ /**
+ * Determine if media gallery renditions is enabled based on configuration value
+ *
+ * @param Value $value
+ * @return bool
+ */
+ private function isRenditionsEnabled(Value $value): bool
+ {
+ return $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_ENABLE_PATH
+ && $value->isValueChanged()
+ && $value->getValue();
+ }
}
diff --git a/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml b/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml
index b60a858da5f26..f36f628cb122f 100644
--- a/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml
+++ b/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml
@@ -10,14 +10,18 @@
Media Gallery Image Optimization
+
+ Enable Image Optimization
+ Magento\Config\Model\Config\Source\Yesno
+
Resize images to improve performance and decrease the file size. When you use an image from Media Gallery on the storefront, the smaller image is generated and placed instead of the original.
Changing these settings will update all generated images.
-
+
Maximum Width
validate-zero-or-greater validate-digits
Enter the maximum width of an image in pixels.
-
+
Maximum Height
validate-zero-or-greater validate-digits
Enter the maximum height of an image in pixels.
diff --git a/app/code/Magento/MediaGalleryRenditions/etc/config.xml b/app/code/Magento/MediaGalleryRenditions/etc/config.xml
index 58c5aa1f11fd2..6b4f2351b8b10 100644
--- a/app/code/Magento/MediaGalleryRenditions/etc/config.xml
+++ b/app/code/Magento/MediaGalleryRenditions/etc/config.xml
@@ -9,6 +9,7 @@
+ 1
1000
1000
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryRenditionsEnableActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryRenditionsEnableActionGroup.xml
new file mode 100644
index 0000000000000..b64247a708242
--- /dev/null
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryRenditionsEnableActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdobeStockConfigData.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdobeStockConfigData.xml
index e8f394a006104..9ac743f2f9983 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdobeStockConfigData.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdobeStockConfigData.xml
@@ -15,4 +15,12 @@
system/media_gallery/enabled
0
+
+ system/media_gallery_renditions/enabled
+ 1
+
+
+ system/media_gallery_renditions/enabled
+ 0
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminConfigSystemSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminConfigSystemSection.xml
index b7900f6664c62..f3ab74470e4e4 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminConfigSystemSection.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminConfigSystemSection.xml
@@ -11,6 +11,8 @@
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiDisabledSuite.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiDisabledSuite.xml
index 727fbde3f17b6..d5f2abe965575 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiDisabledSuite.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiDisabledSuite.xml
@@ -9,6 +9,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiSuite.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiSuite.xml
index 4749fc4a885b0..e81dc807d0f48 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiSuite.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Suite/MediaGalleryUiSuite.xml
@@ -15,6 +15,7 @@
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteImageWithWarningPopupTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteImageWithWarningPopupTest.xml
index 6ae8ed7047434..84dd84a22d3c3 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteImageWithWarningPopupTest.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteImageWithWarningPopupTest.xml
@@ -33,6 +33,8 @@
+
+
@@ -43,11 +45,13 @@
+
+
-
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryStoreViewCategoryFilterTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryStoreViewCategoryFilterTest.xml
index eceda879e5597..9c72ee4ac38fe 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryStoreViewCategoryFilterTest.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryStoreViewCategoryFilterTest.xml
@@ -33,6 +33,8 @@
+
+
@@ -56,7 +58,7 @@
-
+
diff --git a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php
index 13b8aa23073ce..e528f9e88d8a4 100644
--- a/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php
+++ b/app/code/Magento/Theme/Model/PageLayout/Config/Builder.php
@@ -5,25 +5,38 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Theme\Model\PageLayout\Config;
+use Magento\Framework\App\Cache\Type\Layout;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\View\Model\PageLayout\Config\BuilderInterface;
+use Magento\Framework\View\PageLayout\ConfigFactory;
+use Magento\Framework\View\PageLayout\File\Collector\Aggregated;
+use Magento\Theme\Model\ResourceModel\Theme\Collection;
+use Magento\Theme\Model\Theme\Data;
+use Magento\Framework\Serialize\SerializerInterface;
+
/**
* Page layout config builder
*/
-class Builder implements \Magento\Framework\View\Model\PageLayout\Config\BuilderInterface
+class Builder implements BuilderInterface
{
+ const CACHE_KEY_LAYOUTS = 'THEME_LAYOUTS_FILES_MERGED';
+
/**
- * @var \Magento\Framework\View\PageLayout\ConfigFactory
+ * @var ConfigFactory
*/
protected $configFactory;
/**
- * @var \Magento\Framework\View\PageLayout\File\Collector\Aggregated
+ * @var Aggregated
*/
protected $fileCollector;
/**
- * @var \Magento\Theme\Model\ResourceModel\Theme\Collection
+ * @var Collection
*/
protected $themeCollection;
@@ -33,19 +46,36 @@ class Builder implements \Magento\Framework\View\Model\PageLayout\Config\Builder
private $configFiles = [];
/**
- * @param \Magento\Framework\View\PageLayout\ConfigFactory $configFactory
- * @param \Magento\Framework\View\PageLayout\File\Collector\Aggregated $fileCollector
- * @param \Magento\Theme\Model\ResourceModel\Theme\Collection $themeCollection
+ * @var Layout|null
+ */
+ private $cacheModel;
+ /**
+ * @var SerializerInterface|null
+ */
+ private $serializer;
+
+ /**
+ * @param ConfigFactory $configFactory
+ * @param Aggregated $fileCollector
+ * @param Collection $themeCollection
+ * @param Layout|null $cacheModel
+ * @param SerializerInterface|null $serializer
*/
public function __construct(
- \Magento\Framework\View\PageLayout\ConfigFactory $configFactory,
- \Magento\Framework\View\PageLayout\File\Collector\Aggregated $fileCollector,
- \Magento\Theme\Model\ResourceModel\Theme\Collection $themeCollection
+ ConfigFactory $configFactory,
+ Aggregated $fileCollector,
+ Collection $themeCollection,
+ ?Layout $cacheModel = null,
+ ?SerializerInterface $serializer = null
) {
$this->configFactory = $configFactory;
$this->fileCollector = $fileCollector;
$this->themeCollection = $themeCollection;
- $this->themeCollection->setItemObjectClass(\Magento\Theme\Model\Theme\Data::class);
+ $this->themeCollection->setItemObjectClass(Data::class);
+ $this->cacheModel = $cacheModel
+ ?? ObjectManager::getInstance()->get(Layout::class);
+ $this->serializer = $serializer
+ ?? ObjectManager::getInstance()->get(SerializerInterface::class);
}
/**
@@ -57,7 +87,7 @@ public function getPageLayoutsConfig()
}
/**
- * Retrieve configuration files.
+ * Retrieve configuration files. Caches merged layouts.xml XML files.
*
* @return array
*/
@@ -65,10 +95,18 @@ protected function getConfigFiles()
{
if (!$this->configFiles) {
$configFiles = [];
- foreach ($this->themeCollection->loadRegisteredThemes() as $theme) {
- $configFiles[] = $this->fileCollector->getFilesContent($theme, 'layouts.xml');
+ $this->configFiles = $this->cacheModel->load(self::CACHE_KEY_LAYOUTS);
+ if (!empty($this->configFiles)) {
+ //if value in cache is corrupted.
+ $this->configFiles = $this->serializer->unserialize($this->configFiles);
+ }
+ if (empty($this->configFiles)) {
+ foreach ($this->themeCollection->loadRegisteredThemes() as $theme) {
+ $configFiles[] = $this->fileCollector->getFilesContent($theme, 'layouts.xml');
+ }
+ $this->configFiles = array_merge(...$configFiles);
+ $this->cacheModel->save($this->serializer->serialize($this->configFiles), self::CACHE_KEY_LAYOUTS);
}
- $this->configFiles = array_merge(...$configFiles);
}
return $this->configFiles;
diff --git a/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php b/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php
index d9eccdb871222..2e2117b79e5ab 100644
--- a/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Model/PageLayout/Config/BuilderTest.php
@@ -10,8 +10,11 @@
*/
namespace Magento\Theme\Test\Unit\Model\PageLayout\Config;
+use Magento\Framework\App\Cache\Type\Layout;
+use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Framework\View\PageLayout\Config;
+use Magento\Framework\View\PageLayout\ConfigFactory;
use Magento\Framework\View\PageLayout\File\Collector\Aggregated;
use Magento\Theme\Model\PageLayout\Config\Builder;
use Magento\Theme\Model\ResourceModel\Theme\Collection;
@@ -27,7 +30,7 @@ class BuilderTest extends TestCase
protected $builder;
/**
- * @var \Magento\Framework\View\PageLayout\ConfigFactory|MockObject
+ * @var ConfigFactory|MockObject
*/
protected $configFactory;
@@ -41,6 +44,15 @@ class BuilderTest extends TestCase
*/
protected $themeCollection;
+ /**
+ * @var Layout|MockObject
+ */
+ protected $cacheModel;
+ /**
+ * @var SerializerInterface|MockObject
+ */
+ protected $serializer;
+
/**
* SetUp method
*
@@ -48,19 +60,24 @@ class BuilderTest extends TestCase
*/
protected function setUp(): void
{
- $this->configFactory = $this->getMockBuilder(\Magento\Framework\View\PageLayout\ConfigFactory::class)
+ $this->configFactory = $this->getMockBuilder(ConfigFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->fileCollector = $this->getMockBuilder(
- Aggregated::class
- )->disableOriginalConstructor()
+ $this->fileCollector = $this->getMockBuilder(Aggregated::class)
+ ->disableOriginalConstructor()
->getMock();
$this->themeCollection = $this->getMockBuilder(Collection::class)
->disableOriginalConstructor()
->getMock();
+ $this->cacheModel = $this->getMockBuilder(Layout::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->serializer = $this->getMockForAbstractClass(SerializerInterface::class);
+
$this->themeCollection->expects($this->once())
->method('setItemObjectClass')
->with(Data::class)
@@ -72,7 +89,9 @@ protected function setUp(): void
[
'configFactory' => $this->configFactory,
'fileCollector' => $this->fileCollector,
- 'themeCollection' => $this->themeCollection
+ 'themeCollection' => $this->themeCollection,
+ 'cacheModel' => $this->cacheModel,
+ 'serializer' => $this->serializer,
]
);
}
@@ -84,8 +103,10 @@ protected function setUp(): void
*/
public function testGetPageLayoutsConfig()
{
+ $this->cacheModel->clean();
$files1 = ['content layouts_1.xml', 'content layouts_2.xml'];
$files2 = ['content layouts_3.xml', 'content layouts_4.xml'];
+ $configFiles = array_merge($files1, $files2);
$theme1 = $this->getMockBuilder(Data::class)
->disableOriginalConstructor()
@@ -113,9 +134,17 @@ public function testGetPageLayoutsConfig()
$this->configFactory->expects($this->once())
->method('create')
- ->with(['configFiles' => array_merge($files1, $files2)])
+ ->with(['configFiles' => $configFiles])
->willReturn($config);
+ $this->serializer->expects($this->once())
+ ->method('serialize')
+ ->with($configFiles);
+
+ $this->cacheModel->expects($this->once())
+ ->method('save')
+ ->willReturnSelf();
+
$this->assertSame($config, $this->builder->getPageLayoutsConfig());
}
}
diff --git a/app/code/Magento/Ups/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Ups/Plugin/Block/DataProviders/Tracking/ChangeTitle.php
new file mode 100644
index 0000000000000..973b199217271
--- /dev/null
+++ b/app/code/Magento/Ups/Plugin/Block/DataProviders/Tracking/ChangeTitle.php
@@ -0,0 +1,34 @@
+getCarrier() === Carrier::CODE) {
+ $result = __('Status Updated On:');
+ }
+ return $result;
+ }
+}
diff --git a/app/code/Magento/Ups/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php b/app/code/Magento/Ups/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php
new file mode 100644
index 0000000000000..fa608584be964
--- /dev/null
+++ b/app/code/Magento/Ups/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php
@@ -0,0 +1,81 @@
+plugin = $objectManagerHelper->getObject(ChangeTitle::class);
+ }
+
+ /**
+ * Check if DeliveryDateTitle was changed if the carrier is UPS
+ *
+ * @param string $carrierCode
+ * @param string $originalResult
+ * @param Phrase|string $finalResult
+ * @dataProvider testAfterGetTitleDataProvider
+ */
+ public function testAfterGetTitle(string $carrierCode, string $originalResult, $finalResult)
+ {
+ /** @var DeliveryDateTitle|MockObject $subjectMock */
+ $subjectMock = $this->getMockBuilder(DeliveryDateTitle::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ /** @var Status|MockObject $trackingStatusMock */
+ $trackingStatusMock = $this->getMockBuilder(Status::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getCarrier'])
+ ->getMock();
+ $trackingStatusMock->expects($this::once())
+ ->method('getCarrier')
+ ->willReturn($carrierCode);
+
+ $actual = $this->plugin->afterGetTitle($subjectMock, $originalResult, $trackingStatusMock);
+
+ $this->assertEquals($finalResult, $actual);
+ }
+
+ /**
+ * Data provider
+ *
+ * @return array
+ */
+ public function testAfterGetTitleDataProvider(): array
+ {
+ return [
+ [Carrier::CODE, 'Original Title', __('Status Updated On:')],
+ ['not-fedex', 'Original Title', 'Original Title'],
+ ];
+ }
+}
diff --git a/app/code/Magento/Ups/etc/di.xml b/app/code/Magento/Ups/etc/di.xml
index a04a5eb48bdab..08d751fc3e2c8 100644
--- a/app/code/Magento/Ups/etc/di.xml
+++ b/app/code/Magento/Ups/etc/di.xml
@@ -28,4 +28,7 @@
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/ConsumerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/ConsumerTest.php
new file mode 100644
index 0000000000000..8dffcdbdd4582
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/ConsumerTest.php
@@ -0,0 +1,163 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->publisherMock = $this->getMockForAbstractClass(BulkPublisherInterface::class);
+
+ $this->bulkManagement = $this->objectManager->create(
+ BulkManagement::class,
+ [
+ 'publisher' => $this->publisherMock
+ ]
+ );
+ $this->bulkStatus = $this->objectManager->get(BulkStatus::class);
+ $catalogProductMock = $this->createMock(Product::class);
+ $productFlatIndexerProcessorMock = $this->createMock(
+ FlatProcessor::class
+ );
+ $productPriceIndexerProcessorMock = $this->createMock(
+ PriceProcessor::class
+ );
+ $operationManagementMock = $this->createMock(
+ OperationManagementInterface::class
+ );
+ $actionMock = $this->createMock(Action::class);
+ $loggerMock = $this->createMock(LoggerInterface::class);
+ $this->serializer = $this->objectManager->get(SerializerInterface::class);
+ $entityManager = $this->objectManager->get(EntityManager::class);
+ $this->model = $this->objectManager->create(
+ Consumer::class,
+ [
+ 'catalogProduct' => $catalogProductMock,
+ 'productFlatIndexerProcessor' => $productFlatIndexerProcessorMock,
+ 'productPriceIndexerProcessor' => $productPriceIndexerProcessorMock,
+ 'operationManagement' => $operationManagementMock,
+ 'action' => $actionMock,
+ 'logger' => $loggerMock,
+ 'serializer' => $this->serializer,
+ 'entityManager' => $entityManager
+ ]
+ );
+
+ parent::setUp();
+ }
+
+ /**
+ * Testing saving bulk operation during processing operation by attribute backend consumer
+ */
+ public function testSaveOperationDuringProcess()
+ {
+ $operation = $this->prepareUpdateAttributesBulkAndOperation();
+ try {
+ $this->model->process($operation);
+ } catch (\Exception $e) {
+ $this->fail(sprintf('Operation save process failed.: %s', $e->getMessage()));
+ }
+ $operationStatus = $operation->getStatus();
+ $this->assertEquals(
+ 1,
+ $this->bulkStatus->getOperationsCountByBulkIdAndStatus(self::BULK_UUID, $operationStatus)
+ );
+ }
+
+ /**
+ * Schedules test bulk and returns operation
+ * @return OperationInterface
+ */
+ private function prepareUpdateAttributesBulkAndOperation(): OperationInterface
+ {
+ // general bulk information
+ $bulkUuid = self::BULK_UUID;
+ $bulkDescription = 'Update attributes for 2 selected products';
+ $topicName = 'product_action_attribute.update';
+ $userId = 1;
+ /** @var OperationInterfaceFactory $operationFactory */
+ $operationFactory = $this->objectManager->get(OperationInterfaceFactory::class);
+ $operation = $operationFactory->create();
+ $operation->setBulkUuid($bulkUuid)
+ ->setTopicName($topicName)
+ ->setSerializedData($this->serializer->serialize(
+ ['product_ids' => [1,3], 'attributes' => [], 'store_id' => '0']
+ ));
+ $this->bulkManagement->scheduleBulk($bulkUuid, [$operation], $bulkDescription, $userId);
+ return $operation;
+ }
+
+ /**
+ * Clear created bulk and operation
+ */
+ protected function tearDown(): void
+ {
+ $this->bulkManagement->deleteBulk(self::BULK_UUID);
+ parent::tearDown();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php
index 74db33398f721..2a4efd766b7c6 100644
--- a/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php
+++ b/dev/tests/integration/testsuite/Magento/Directory/Model/Country/Postcode/ValidatorTest.php
@@ -104,7 +104,7 @@ public function getPostcodesDataProvider()
['countryId' => 'BY', 'postcode' => '123456'],
['countryId' => 'BE', 'postcode' => '1234'],
['countryId' => 'BA', 'postcode' => '12345'],
- ['countryId' => 'BR', 'postcode' => '12345'],
+ ['countryId' => 'BR', 'postcode' => '12345678'],
['countryId' => 'BR', 'postcode' => '12345-678'],
['countryId' => 'BN', 'postcode' => 'PS1234'],
['countryId' => 'BG', 'postcode' => '1234'],
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js
index 7063b846ed166..ae7c03dfd7792 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js
@@ -3,30 +3,35 @@
* See COPYING.txt for license details.
*/
-/*eslint-disable max-nested-callbacks*/
-/*jscs:disable jsDoc*/
+/* global _ */
+/* eslint max-nested-callbacks: 0 */
+/* jscs:disable jsDoc*/
define([
+ 'squire',
'jquery',
- 'underscore',
'Magento_Customer/js/section-config',
- 'Magento_Customer/js/customer-data'
-], function (
- $,
- _,
- sectionConfig,
- customerData
-) {
+ 'Magento_Customer/js/customer-data',
+ 'jquery/jquery-storageapi'
+], function (Squire, $, sectionConfig, customerData) {
'use strict';
- var sectionConfigSettings = {
+ var injector = new Squire(),
+ obj,
+ originaljQuery,
+ originalGetJSON,
+ originalReload,
+ originalIsEmpty,
+ originalEach,
+ cookieLifeTime = 3600,
+ sectionConfigSettings = {
baseUrls: [
'http://localhost/'
],
sections: {
'customer/account/loginpost': ['*'],
'checkout/cart/add': ['cart'],
- 'rest/*/v1/guest-carts/*/selected-payment-method': ['cart','checkout-data'],
+ 'rest/*/v1/guest-carts/*/selected-payment-method': ['cart', 'checkout-data'],
'*': ['messages']
},
clientSideSections: [
@@ -39,9 +44,7 @@ define([
'cart',
'messages'
]
- },
- cookieLifeTime = 3600,
- jQueryGetJSON;
+ };
function init(config) {
var defaultConfig = {
@@ -95,75 +98,102 @@ define([
}
describe('Magento_Customer/js/customer-data', function () {
+
+ var _;
+
beforeAll(function () {
clearLocalStorage();
});
- beforeEach(function () {
- jQueryGetJSON = $.getJSON;
+ beforeEach(function (done) {
+ originalGetJSON = $.getJSON;
sectionConfig['Magento_Customer/js/section-config'](sectionConfigSettings);
+
+ injector.require([
+ 'underscore',
+ 'Magento_Customer/js/customer-data'
+ ], function (underscore, Constr) {
+ _ = underscore;
+ obj = Constr;
+ done();
+ });
});
afterEach(function () {
- $.getJSON = jQueryGetJSON;
- clearLocalStorage();
+ try {
+ $.getJSON = originalGetJSON;
+ clearLocalStorage();
+ injector.clean();
+ injector.remove();
+ } catch (e) {
+ }
});
- describe('getExpiredSectionNames()', function () {
- it('check that result contains expired section names', function () {
- setupLocalStorage({
- 'cart': {
- 'data_id': Math.floor(Date.now() / 1000) - 61 * 60, // 61 minutes ago
- 'content': {}
- }
- });
- init();
- expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ describe('"init" method', function () {
+ var storageInvalidation = {
+ keys: function () {
+ return ['section'];
+ }
+ };
+
+ beforeEach(function () {
+ originalReload = obj.reload;
+ originalIsEmpty = _.isEmpty;
+
+ $.initNamespaceStorage('mage-cache-storage').localStorage;
+ $.initNamespaceStorage('mage-cache-storage-section-invalidation').localStorage;
+
+ spyOn(storageInvalidation, 'keys').and.returnValue(['section']);
});
- it('check that result doest not contain unexpired section names', function () {
- setupLocalStorage({
- 'cart': {
- 'data_id': Math.floor(Date.now() / 1000) + 60, // in 1 minute
- 'content': {}
- }
- });
- init();
- expect(customerData.getExpiredSectionNames()).toEqual([]);
+ afterEach(function () {
+ obj.reload = originalReload;
+ _.isEmpty = originalIsEmpty;
+ $.namespaceStorages = {};
});
- it('check that result contains invalidated section names', function () {
- setupLocalStorage({
- 'cart': { // without storage content
- 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
- }
- });
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('init')).toBeDefined();
+ });
- init();
- expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ it('Does not throw before component is initialized', function () {
+ obj.initStorage();
+
+ expect(function () {
+ obj.init();
+ }).not.toThrow();
});
- it('check that result does not contain unsupported section names', function () {
- setupLocalStorage({
- 'catalog': { // without storage content
- 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
- }
- });
+ it('Calls "getExpiredSectionNames" method', function () {
+ spyOn(obj, 'getExpiredSectionNames').and.returnValue([]);
+ obj.init();
+ expect(obj.getExpiredSectionNames).toHaveBeenCalled();
+ });
- init();
- expect(customerData.getExpiredSectionNames()).toEqual([]);
+ it('Calls "reload" method when expired sections exist', function () {
+ spyOn(obj, 'getExpiredSectionNames').and.returnValue(['section']);
+ spyOn(obj, 'reload').and.returnValue(true);
+ obj.init();
+ expect(obj.reload).toHaveBeenCalled();
});
- });
- describe('init()', function () {
- it('check that sections are not requested from server, if there are no expired sections', function () {
+ it('Calls "reload" method when expired sections do not exist', function () {
+ spyOn(obj, 'getExpiredSectionNames').and.returnValue([]);
+ spyOn(obj, 'reload').and.returnValue(true);
+ spyOn(_, 'isEmpty').and.returnValue(false);
+
+ obj.init();
+ expect(obj.reload).toHaveBeenCalled();
+ });
+
+ it('Check it does not request sections from the server if there are no expired sections', function () {
setupLocalStorage({
'catalog': { // without storage content
'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
}
});
- $.getJSON = jasmine.createSpy('$.getJSON').and.callFake(function () {
+ $.getJSON = jasmine.createSpy().and.callFake(function () {
var deferred = $.Deferred();
return deferred.promise();
@@ -172,7 +202,8 @@ define([
init();
expect($.getJSON).not.toHaveBeenCalled();
});
- it('check that sections are requested from server, if there are expired sections', function () {
+
+ it('Check it requests sections from the server if there are expired sections', function () {
setupLocalStorage({
'customer': {
'data_id': Math.floor(Date.now() / 1000) + 60 // invalidated,
@@ -209,5 +240,280 @@ define([
);
});
});
+
+ describe('"getExpiredSectionNames method', function () {
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('getExpiredSectionNames')).toBeDefined();
+ });
+
+ it('Does not throw before component is initialized', function () {
+ expect(function () {
+ obj.getExpiredSectionNames();
+ }).not.toThrow();
+ });
+
+ it('Check that result contains expired section names', function () {
+ setupLocalStorage({
+ 'cart': {
+ 'data_id': Math.floor(Date.now() / 1000) - 61 * 60, // 61 minutes ago
+ 'content': {}
+ }
+ });
+
+ $.getJSON = jasmine.createSpy('$.getJSON').and.callFake(function () {
+ var deferred = $.Deferred();
+
+ return deferred.promise();
+ });
+
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ });
+
+ it('Check that result does not contain unexpired section names', function () {
+ setupLocalStorage({
+ 'cart': {
+ 'data_id': Math.floor(Date.now() / 1000) + 60, // in 1 minute
+ 'content': {}
+ }
+ });
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual([]);
+ });
+
+ it('Check that result contains invalidated section names', function () {
+ setupLocalStorage({
+ 'cart': { // without storage content
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
+ }
+ });
+
+ $.getJSON = jasmine.createSpy('$.getJSON').and.callFake(function () {
+ var deferred = $.Deferred();
+
+ return deferred.promise();
+ });
+
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ });
+
+ it('Check that result does not contain unsupported section names', function () {
+ setupLocalStorage({
+ 'catalog': { // without storage content
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
+ }
+ });
+
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual([]);
+ });
+ });
+
+ describe('"get" method', function () {
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('get')).toBeDefined();
+ });
+
+ it('Does not throw before component is initialized', function () {
+ expect(function () {
+ obj.get();
+ }).not.toThrow();
+ });
+ });
+
+ describe('"set" method', function () {
+ beforeEach(function () {
+ originalEach = _.each;
+ });
+
+ afterEach(function () {
+ _.each = originalEach;
+ });
+
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('set')).toBeDefined();
+ });
+
+ it('Does not throw before component is initialized', function () {
+ _.each = jasmine.createSpy().and.returnValue(true);
+
+ expect(function () {
+ obj.set();
+ }).not.toThrow();
+ });
+ });
+
+ describe('"reload" method', function () {
+ beforeEach(function () {
+ originaljQuery = $;
+ $ = jQuery;
+
+ $.getJSON = jasmine.createSpy().and.callFake(function () {
+ var deferred = $.Deferred();
+
+ /**
+ * Mock Done Method for getJSON
+ * @returns object
+ */
+ deferred.promise().done = function () {
+ return {
+ responseJSON: {
+ section: {}
+ }
+ };
+ };
+
+ return deferred.promise();
+ });
+ });
+
+ afterEach(function () {
+ $ = originaljQuery;
+ });
+
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('reload')).toBeDefined();
+ });
+
+ it('Does not throw before component is initialized', function () {
+ expect(function () {
+ obj.reload();
+ }).not.toThrow();
+ });
+
+ it('Check it returns proper sections object when passed array with a single section name', function () {
+ var result;
+
+ spyOn(sectionConfig, 'filterClientSideSections').and.returnValue(['section']);
+
+ $.getJSON = jasmine.createSpy().and.callFake(function (url, parameters) {
+ var deferred = $.Deferred();
+
+ /**
+ * Mock Done Method for getJSON
+ * @returns object
+ */
+ deferred.promise().done = function () {
+ return {
+ responseJSON: {
+ section: {}
+ }
+ };
+ };
+
+ expect(parameters).toEqual(jasmine.objectContaining({
+ sections: 'section'
+ }));
+
+ return deferred.promise();
+ });
+
+ result = obj.reload(['section'], true);
+
+ expect(result).toEqual(jasmine.objectContaining({
+ responseJSON: {
+ section: {}
+ }
+ }));
+ });
+
+ it('Check it returns proper sections object when passed array with multiple section names', function () {
+ var result;
+
+ spyOn(sectionConfig, 'filterClientSideSections').and.returnValue(['cart,customer,messages']);
+
+ $.getJSON = jasmine.createSpy().and.callFake(function (url, parameters) {
+ var deferred = $.Deferred();
+
+ expect(parameters).toEqual(jasmine.objectContaining({
+ sections: 'cart,customer,messages'
+ }));
+
+ /**
+ * Mock Done Method for getJSON
+ * @returns object
+ */
+ deferred.promise().done = function () {
+ return {
+ responseJSON: {
+ cart: {},
+ customer: {},
+ messages: {}
+ }
+ };
+ };
+
+ return deferred.promise();
+ });
+
+ result = obj.reload(['cart', 'customer', 'messages'], true);
+
+ expect(result).toEqual(jasmine.objectContaining({
+ responseJSON: {
+ cart: {},
+ customer: {},
+ messages: {}
+ }
+ }));
+ });
+ //
+ it('Check it returns all sections when passed wildcard string', function () {
+ var result;
+
+ $.getJSON = jasmine.createSpy().and.callFake(function (url, parameters) {
+ var deferred = $.Deferred();
+
+ expect(parameters).toEqual(jasmine.objectContaining({
+ 'force_new_section_timestamp': true
+ }));
+
+ /**
+ * Mock Done Method for getJSON
+ * @returns object
+ */
+ deferred.promise().done = function () {
+ return {
+ responseJSON: {
+ cart: {},
+ customer: {},
+ messages: {}
+ }
+ };
+ };
+
+ return deferred.promise();
+ });
+
+ result = obj.reload('*', true);
+
+ expect($.getJSON).toHaveBeenCalled();
+ expect(result).toEqual(jasmine.objectContaining({
+ responseJSON: {
+ cart: {},
+ customer: {},
+ messages: {}
+ }
+ }));
+ });
+ });
+
+ describe('"invalidated" method', function () {
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('invalidate')).toBeDefined();
+ });
+
+ it('Does not throw before component is initialized', function () {
+ expect(function () {
+ obj.invalidate();
+ }).not.toThrow();
+ });
+ });
+
+ describe('"Magento_Customer/js/customer-data" method', function () {
+ it('Should be defined', function () {
+ expect(obj.hasOwnProperty('Magento_Customer/js/customer-data')).toBeDefined();
+ });
+ });
});
});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Multishipping/frontend/js/multi-shipping.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Multishipping/frontend/js/multi-shipping.test.js
index 65ee180476f3a..a8ae8ab65e378 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Multishipping/frontend/js/multi-shipping.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Multishipping/frontend/js/multi-shipping.test.js
@@ -68,9 +68,11 @@ define([
var addNewAddressBtn,
addressflag,
canContinueBtn,
- canContinueFlag;
+ canContinueFlag,
+ originalGetJSON;
beforeEach(function () {
+ originalGetJSON = $.getJSON;
addNewAddressBtn = $(' ');
addressflag = $(' ');
canContinueBtn = $(' ');
@@ -79,6 +81,12 @@ define([
.append(addressflag)
.append(canContinueBtn)
.append(canContinueFlag);
+
+ $.getJSON = jasmine.createSpy().and.callFake(function () {
+ var deferred = $.Deferred();
+
+ return deferred.promise();
+ });
});
afterEach(function () {
@@ -86,6 +94,7 @@ define([
addressflag.remove();
canContinueBtn.remove();
canContinueFlag.remove();
+ $.getJSON = originalGetJSON;
});
it('Check add new address event', function () {
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index 00c216d1f2100..3d0521fa5ac61 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -21,6 +21,7 @@
use Magento\Framework\DB\Query\Generator as QueryGenerator;
use Magento\Framework\DB\Select;
use Magento\Framework\DB\SelectFactory;
+use Magento\Framework\DB\Sql\Expression;
use Magento\Framework\DB\Statement\Parameter;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Phrase;
@@ -1511,10 +1512,10 @@ public function select()
* Method revrited for handle empty arrays in value param
*
* @param string $text The text with a placeholder.
- * @param mixed $value The value to quote.
- * @param string $type OPTIONAL SQL datatype
+ * @param array|null|int|string|float|Expression|Select|\DateTimeInterface $value The value to quote.
+ * @param int|string|null $type OPTIONAL SQL datatype of the given value e.g. Zend_Db::FLOAT_TYPE or "INT"
* @param integer $count OPTIONAL count of placeholders to replace
- * @return string An SQL-safe quoted value placed into the orignal text.
+ * @return string An SQL-safe quoted value placed into the original text.
*/
public function quoteInto($text, $value, $type = null, $count = null)
{
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index 0aaf29aeb332e..8869c874cd5cb 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -7,6 +7,7 @@
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Sql\Expression;
/**
* Class for SQL SELECT generation and results.
@@ -108,8 +109,8 @@ public function __construct(
*
*
* @param string $cond The WHERE condition.
- * @param string|array|null $value OPTIONAL An optional single or array value to quote into the condition.
- * @param string|int|null $type OPTIONAL The type of the given value
+ * @param array|null|int|string|float|Expression|Select|\DateTimeInterface $value The value to quote.
+ * @param int|string|null $type OPTIONAL SQL datatype of the given value e.g. Zend_Db::FLOAT_TYPE or "INT"
* @return \Magento\Framework\DB\Select
*/
public function where($cond, $value = null, $type = null)
diff --git a/lib/web/jquery/jquery.storageapi.min.js b/lib/web/jquery/jquery.storageapi.min.js
index 886c3d847ed3b..fcf296a384bce 100644
--- a/lib/web/jquery/jquery.storageapi.min.js
+++ b/lib/web/jquery/jquery.storageapi.min.js
@@ -1,2 +1,2 @@
/* jQuery Storage API Plugin 1.7.3 https://github.com/julien-maurel/jQuery-Storage-API */
-!function(e){"function"==typeof define&&define.amd?define(["jquery", "jquery/jquery.cookie"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function t(t){var r,i,n,o=arguments.length,s=window[t],a=arguments,u=a[1];if(2>o)throw Error("Minimum 2 arguments must be given");if(e.isArray(u)){i={};for(var f in u){r=u[f];try{i[r]=JSON.parse(s.getItem(r))}catch(c){i[r]=s.getItem(r)}}return i}if(2!=o){try{i=JSON.parse(s.getItem(u))}catch(c){throw new ReferenceError(u+" is not defined in this storage")}for(var f=2;o-1>f;f++)if(i=i[a[f]],void 0===i)throw new ReferenceError([].slice.call(a,1,f+1).join(".")+" is not defined in this storage");if(e.isArray(a[f])){n=i,i={};for(var m in a[f])i[a[f][m]]=n[a[f][m]];return i}return i[a[f]]}try{return JSON.parse(s.getItem(u))}catch(c){return s.getItem(u)}}function r(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1],u=s[2],f={};if(2>n||!e.isPlainObject(a)&&3>n)throw Error("Minimum 3 arguments must be given or second parameter must be an object");if(e.isPlainObject(a)){for(var c in a)r=a[c],e.isPlainObject(r)?o.setItem(c,JSON.stringify(r)):o.setItem(c,r);return a}if(3==n)return"object"==typeof u?o.setItem(a,JSON.stringify(u)):o.setItem(a,u),u;try{i=o.getItem(a),null!=i&&(f=JSON.parse(i))}catch(m){}i=f;for(var c=2;n-2>c;c++)r=s[c],i[r]&&e.isPlainObject(i[r])||(i[r]={}),i=i[r];return i[s[c]]=s[c+1],o.setItem(a,JSON.stringify(f)),f}function i(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1];if(2>n)throw Error("Minimum 2 arguments must be given");if(e.isArray(a)){for(var u in a)o.removeItem(a[u]);return!0}if(2==n)return o.removeItem(a),!0;try{r=i=JSON.parse(o.getItem(a))}catch(f){throw new ReferenceError(a+" is not defined in this storage")}for(var u=2;n-1>u;u++)if(i=i[s[u]],void 0===i)throw new ReferenceError([].slice.call(s,1,u).join(".")+" is not defined in this storage");if(e.isArray(s[u]))for(var c in s[u])delete i[s[u][c]];else delete i[s[u]];return o.setItem(a,JSON.stringify(r)),!0}function n(t,r){var n=a(t);for(var o in n)i(t,n[o]);if(r)for(var o in e.namespaceStorages)u(o)}function o(r){var i=arguments.length,n=arguments,s=(window[r],n[1]);if(1==i)return 0==a(r).length;if(e.isArray(s)){for(var u=0;ui)throw Error("Minimum 2 arguments must be given");if(e.isArray(o)){for(var a=0;a1?t.apply(this,o):n,a._cookie)for(var u in e.cookie())""!=u&&s.push(u.replace(a._prefix,""));else for(var f in a)s.push(f);return s}function u(t){if(!t||"string"!=typeof t)throw Error("First parameter must be a string");g?(window.localStorage.getItem(t)||window.localStorage.setItem(t,"{}"),window.sessionStorage.getItem(t)||window.sessionStorage.setItem(t,"{}")):(window.localCookieStorage.getItem(t)||window.localCookieStorage.setItem(t,"{}"),window.sessionCookieStorage.getItem(t)||window.sessionCookieStorage.setItem(t,"{}"));var r={localStorage:e.extend({},e.localStorage,{_ns:t}),sessionStorage:e.extend({},e.sessionStorage,{_ns:t})};return e.cookie&&(window.cookieStorage.getItem(t)||window.cookieStorage.setItem(t,"{}"),r.cookieStorage=e.extend({},e.cookieStorage,{_ns:t})),e.namespaceStorages[t]=r,r}function f(e){if(!window[e])return!1;var t="jsapi";try{return window[e].setItem(t,t),window[e].removeItem(t),!0}catch(r){return!1}}var c="ls_",m="ss_",g=f("localStorage"),h={_type:"",_ns:"",_callMethod:function(e,t){var r=[this._type],t=Array.prototype.slice.call(t),i=t[0];return this._ns&&r.push(this._ns),"string"==typeof i&&-1!==i.indexOf(".")&&(t.shift(),[].unshift.apply(t,i.split("."))),[].push.apply(r,t),e.apply(this,r)},get:function(){return this._callMethod(t,arguments)},set:function(){var t=arguments.length,i=arguments,n=i[0];if(1>t||!e.isPlainObject(n)&&2>t)throw Error("Minimum 2 arguments must be given or first parameter must be an object");if(e.isPlainObject(n)&&this._ns){for(var o in n)r(this._type,this._ns,o,n[o]);return n}var s=this._callMethod(r,i);return this._ns?s[n.split(".")[0]]:s},remove:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(i,arguments)},removeAll:function(e){return this._ns?(r(this._type,this._ns,{}),!0):n(this._type,e)},isEmpty:function(){return this._callMethod(o,arguments)},isSet:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(s,arguments)},keys:function(){return this._callMethod(a,arguments)}};if(e.cookie){window.name||(window.name=Math.floor(1e8*Math.random()));var l={_cookie:!0,_prefix:"",_expires:null,_path:null,_domain:null,setItem:function(t,r){e.cookie(this._prefix+t,r,{expires:this._expires,path:this._path,domain:this._domain})},getItem:function(t){return e.cookie(this._prefix+t)},removeItem:function(t){return e.removeCookie(this._prefix+t)},clear:function(){for(var t in e.cookie())""!=t&&(!this._prefix&&-1===t.indexOf(c)&&-1===t.indexOf(m)||this._prefix&&0===t.indexOf(this._prefix))&&e.removeCookie(t)},setExpires:function(e){return this._expires=e,this},setPath:function(e){return this._path=e,this},setDomain:function(e){return this._domain=e,this},setConf:function(e){return e.path&&(this._path=e.path),e.domain&&(this._domain=e.domain),e.expires&&(this._expires=e.expires),this},setDefaultConf:function(){this._path=this._domain=this._expires=null}};g||(window.localCookieStorage=e.extend({},l,{_prefix:c,_expires:3650}),window.sessionCookieStorage=e.extend({},l,{_prefix:m+window.name+"_"})),window.cookieStorage=e.extend({},l),e.cookieStorage=e.extend({},h,{_type:"cookieStorage",setExpires:function(e){return window.cookieStorage.setExpires(e),this},setPath:function(e){return window.cookieStorage.setPath(e),this},setDomain:function(e){return window.cookieStorage.setDomain(e),this},setConf:function(e){return window.cookieStorage.setConf(e),this},setDefaultConf:function(){return window.cookieStorage.setDefaultConf(),this}})}e.initNamespaceStorage=function(e){return u(e)},g?(e.localStorage=e.extend({},h,{_type:"localStorage"}),e.sessionStorage=e.extend({},h,{_type:"sessionStorage"})):(e.localStorage=e.extend({},h,{_type:"localCookieStorage"}),e.sessionStorage=e.extend({},h,{_type:"sessionCookieStorage"})),e.namespaceStorages={},e.removeAllStorages=function(t){e.localStorage.removeAll(t),e.sessionStorage.removeAll(t),e.cookieStorage&&e.cookieStorage.removeAll(t),t||(e.namespaceStorages={})}});
\ No newline at end of file
+!function(e){"function"==typeof define&&define.amd?define(["jquery", "jquery/jquery.cookie"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function t(t){var r,i,n,o=arguments.length,s=window[t],a=arguments,u=a[1];if(2>o)throw Error("Minimum 2 arguments must be given");if(e.isArray(u)){i={};for(var f in u){r=u[f];try{i[r]=JSON.parse(s.getItem(r))}catch(c){i[r]=s.getItem(r)}}return i}if(2!=o){try{i=JSON.parse(s.getItem(u))}catch(c){throw new ReferenceError(u+" is not defined in this storage")}for(var f=2;o-1>f;f++)if(i=i[a[f]],void 0===i)throw new ReferenceError([].slice.call(a,1,f+1).join(".")+" is not defined in this storage");if(e.isArray(a[f])){n=i,i={};for(var m in a[f])i[a[f][m]]=n[a[f][m]];return i}return i[a[f]]}try{return JSON.parse(s.getItem(u))}catch(c){return s.getItem(u)}}function r(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1],u=s[2],f={};if(2>n||!e.isPlainObject(a)&&3>n)throw Error("Minimum 3 arguments must be given or second parameter must be an object");if(e.isPlainObject(a)){for(var c in a)r=a[c],e.isPlainObject(r)?o.setItem(c,JSON.stringify(r)):o.setItem(c,r);return a}if(3==n)return"object"==typeof u?o.setItem(a,JSON.stringify(u)):o.setItem(a,u),u;try{i=o.getItem(a),null!=i&&(f=JSON.parse(i))}catch(m){}i=f;for(var c=2;n-2>c;c++)r=s[c],i[r]&&e.isPlainObject(i[r])||(i[r]={}),i=i[r];return i[s[c]]=s[c+1],o.setItem(a,JSON.stringify(f)),f}function i(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1];if(2>n)throw Error("Minimum 2 arguments must be given");if(e.isArray(a)){for(var u in a)o.removeItem(a[u]);return!0}if(2==n)return o.removeItem(a),!0;try{r=i=JSON.parse(o.getItem(a))}catch(f){throw new ReferenceError(a+" is not defined in this storage")}for(var u=2;n-1>u;u++)if(i=i[s[u]],void 0===i)throw new ReferenceError([].slice.call(s,1,u).join(".")+" is not defined in this storage");if(e.isArray(s[u]))for(var c in s[u])delete i[s[u][c]];else delete i[s[u]];return o.setItem(a,JSON.stringify(r)),!0}function n(t,r){var n=a(t);for(var o in n)i(t,n[o]);if(r)for(var o in e.namespaceStorages)u(o)}function o(r){var i=arguments.length,n=arguments,s=(window[r],n[1]);if(1==i)return 0==a(r).length;if(e.isArray(s)){for(var u=0;ui)throw Error("Minimum 2 arguments must be given");if(e.isArray(o)){for(var a=0;a1?t.apply(this,o):n,a._cookie)for(var u in e.cookie())""!=u&&s.push(u.replace(a._prefix,""));else for(var f in a)s.push(f);return s}function u(t){if(!t||"string"!=typeof t)throw Error("First parameter must be a string");g?(window.localStorage.getItem(t)||window.localStorage.setItem(t,"{}"),window.sessionStorage.getItem(t)||window.sessionStorage.setItem(t,"{}")):(window.localCookieStorage.getItem(t)||window.localCookieStorage.setItem(t,"{}"),window.sessionCookieStorage.getItem(t)||window.sessionCookieStorage.setItem(t,"{}"));var r={localStorage:e.extend({},e.localStorage,{_ns:t}),sessionStorage:e.extend({},e.sessionStorage,{_ns:t})};return e.cookie&&(window.cookieStorage.getItem(t)||window.cookieStorage.setItem(t,"{}"),r.cookieStorage=e.extend({},e.cookieStorage,{_ns:t})),e.namespaceStorages[t]=r,r}function f(e){if(!window[e])return!1;var t="jsapi";try{return window[e].setItem(t,t),window[e].removeItem(t),!0}catch(r){return!1}}var c="ls_",m="ss_",g=f("localStorage"),h={_type:"",_ns:"",_callMethod:function(e,t){var r=[this._type],t=Array.prototype.slice.call(t),i=t[0];return this._ns&&r.push(this._ns),"string"==typeof i&&-1!==i.indexOf(".")&&(t.shift(),[].unshift.apply(t,i.split("."))),[].push.apply(r,t),e.apply(this,r)},get:function(){return this._callMethod(t,arguments)},set:function(){var t=arguments.length,i=arguments,n=i[0];if(1>t||!e.isPlainObject(n)&&2>t)throw Error("Minimum 2 arguments must be given or first parameter must be an object");if(e.isPlainObject(n)&&this._ns){for(var o in n)r(this._type,this._ns,o,n[o]);return n}var s=this._callMethod(r,i);return this._ns?s[n.split(".")[0]]:s},remove:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(i,arguments)},removeAll:function(e){return this._ns?(r(this._type,this._ns,{}),!0):n(this._type,e)},isEmpty:function(){return this._callMethod(o,arguments)},isSet:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(s,arguments)},keys:function(){return this._callMethod(a,arguments)}};if(e.cookie){window.name||(window.name=Math.floor(1e8*Math.random()));var l={_cookie:!0,_prefix:"",_expires:null,_path:null,_domain:null,setItem:function(t,r){e.cookie(this._prefix+t,r,{expires:this._expires,path:this._path,domain:this._domain})},getItem:function(t){return e.cookie(this._prefix+t)},removeItem:function(t){return e.removeCookie(this._prefix+t)},clear:function(){for(var t in e.cookie())""!=t&&(!this._prefix&&-1===t.indexOf(c)&&-1===t.indexOf(m)||this._prefix&&0===t.indexOf(this._prefix))&&e.removeCookie(t)},setExpires:function(e){return this._expires=e,this},setPath:function(e){return this._path=e,this},setDomain:function(e){return this._domain=e,this},setConf:function(e){return e.path&&(this._path=e.path),e.domain&&(this._domain=e.domain),e.expires&&(this._expires=e.expires),this},setDefaultConf:function(){this._path=this._domain=this._expires=null}};g||(window.localCookieStorage=e.extend({},l,{_prefix:c,_expires:3650}),window.sessionCookieStorage=e.extend({},l,{_prefix:m+window.name+"_"})),window.cookieStorage=e.extend({},l),e.cookieStorage=e.extend({},h,{_type:"cookieStorage",setExpires:function(e){return window.cookieStorage.setExpires(e),this},setPath:function(e){return window.cookieStorage.setPath(e),this},setDomain:function(e){return window.cookieStorage.setDomain(e),this},setConf:function(e){return window.cookieStorage.setConf(e),this},setDefaultConf:function(){return window.cookieStorage.setDefaultConf(),this}})}e.initNamespaceStorage=function(e){return u(e)},g?(e.localStorage=e.extend({},h,{_type:"localStorage"}),e.sessionStorage=e.extend({},h,{_type:"sessionStorage"})):(e.localStorage=e.extend({},h,{_type:"localCookieStorage"}),e.sessionStorage=e.extend({},h,{_type:"sessionCookieStorage"})),e.namespaceStorages={},e.removeAllStorages=function(t){e.localStorage.removeAll(t),e.sessionStorage.removeAll(t),e.cookieStorage&&e.cookieStorage.removeAll(t),t||(e.namespaceStorages={})}});